#!/bin/bash
#
# .---.                  .              .
# |                      |              |
# |--- .--. .-.  .-.  .-.|  .-. .--.--. |.-.  .-. .--.  .-.
# |    |   (.-' (.-' (   | (   )|  |  | |   )(   )|  | (.-'
# '    '     --'  --'  -' -  -' '  '   -' -'   -' '   -  --'
#
#                    Freedom in the Cloud
#
# Create self-signed or Let's Encrypt certificates on Debian

# License
# =======
#
# Copyright (C) 2015-2016 Bob Mottram <bob@robotics.uk.to>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

PROJECT_NAME='freedombone'

export TEXTDOMAIN=${PROJECT_NAME}-addcert
export TEXTDOMAINDIR="/usr/share/locale"

CONFIGURATION_FILE=$HOME/${PROJECT_NAME}.cfg
COMPLETION_FILE=$HOME/${PROJECT_NAME}-completed.txt

source /usr/local/bin/${PROJECT_NAME}-utils-git
if [ -f /usr/bin/${PROJECT_NAME}-utils-git ]; then
	source /usr/bin/${PROJECT_NAME}-utils-git
fi

HOSTNAME=
LETSENCRYPT_HOSTNAME=
COUNTRY_CODE="US"
AREA="Free Speech Zone"
LOCATION="Freedomville"
ORGANISATION="Freedombone"
UNIT="Freedombone Unit"
EXTENSIONS=""
NODH=
DH_KEYLENGTH=2048
INSTALL_DIR=/root/build
LETSENCRYPT_SERVER='https://acme-v01.api.letsencrypt.org/directory'
LETSENCRYPT_REPO="https://github.com/letsencrypt/letsencrypt"
MY_EMAIL_ADDRESS=
FRIENDS_MIRRORS_SERVER=
FRIENDS_MIRRORS_PASSWORD=
FRIENDS_MIRRORS_SSH_PORT=
MY_MIRRORS_PASSWORD=

function read_repo_servers {
	if [ -f $CONFIGURATION_FILE ]; then
		if grep -q "FRIENDS_MIRRORS_SERVER" $CONFIGURATION_FILE; then
			FRIENDS_MIRRORS_SERVER=$(grep "FRIENDS_MIRRORS_SERVER" $CONFIGURATION_FILE | awk -F '=' '{print $2}')
		fi
		if grep -q "FRIENDS_MIRRORS_SSH_PORT" $CONFIGURATION_FILE; then
			FRIENDS_MIRRORS_SSH_PORT=$(grep "FRIENDS_MIRRORS_SSH_PORT" $CONFIGURATION_FILE | awk -F '=' '{print $2}')
		fi
		if grep -q "MY_MIRRORS_PASSWORD" $CONFIGURATION_FILE; then
			MY_MIRRORS_PASSWORD=$(grep "MY_MIRRORS_PASSWORD" $CONFIGURATION_FILE | awk -F '=' '{print $2}')
		fi
		if grep -q "FRIENDS_MIRRORS_PASSWORD" $CONFIGURATION_FILE; then
			FRIENDS_MIRRORS_PASSWORD=$(grep "FRIENDS_MIRRORS_PASSWORD" $CONFIGURATION_FILE | awk -F '=' '{print $2}')
		fi
	fi

	if [ ! $FRIENDS_MIRRORS_SERVER ]; then
		return
	fi
	if [ ${#FRIENDS_MIRRORS_SERVER} -lt 2 ]; then
		return
	fi

	MAIN_COMMAND=/usr/local/bin/${PROJECT_NAME}
	if [ ! -f $MAIN_COMMAND ]; then
		MAIN_COMMAND=/usr/bin/${PROJECT_NAME}
	fi

	REPOS=($(cat ${MAIN_COMMAND} | grep "_REPO=\"" | uniq -u | sed 's|${PROJECT_NAME}|'"${PROJECT_NAME}"'|g'))

	for line in "${REPOS[@]}"
	do
		repo_name=$(echo "$line" | awk -F '=' '{print $1}')
		mirrors_name=$(echo "$repo_name" | sed "s|_REPO||g" | awk '{print tolower($0)}')
		friends_repo_url="ssh://mirrors@${FRIENDS_MIRRORS_SERVER}:${FRIENDS_MIRRORS_SSH_PORT}/home/mirrors/${mirrors_name}"
		${repo_name}="${friends_repo_url}"
	done
}

function show_help {
	echo ''
	echo $"${PROJECT_NAME}-addcert -h [hostname] -c [country code] -a [area] -l [location]"
	echo $'                    -o [organisation] -u [unit] --ca "" --nodh ""'
	echo ''
	echo $'Creates a self-signed certificate for the given hostname'
	echo ''
	echo $'     --help                   Show help'
	echo $'  -h --hostname [name]        Hostname'
	echo $'  -e --letsencrypt [hostname] Hostname to use with Lets Encrypt'
	echo $'  -s --server [url]           Lets Encrypt server URL'
	echo $'  -c --country [code]         Optional country code (eg. US, GB, etc)'
	echo $'  -a --area [description]     Optional area description'
	echo $'  -l --location [locn]        Optional location name'
	echo $'  -o --organisation [name]    Optional organisation name'
	echo $'  -u --unit [name]            Optional unit name'
	echo $'     --email [address]        Email address for letsencrypt'
	echo $'     --dhkey [bits]           DH key length in bits'
	echo $'     --nodh ""                Do not calculate DH params'
	echo $'     --ca ""                  Certificate authority cert'
	echo ''
	exit 0
}

while [[ $# > 1 ]]
do
key="$1"

case $key in
	--help)
	show_help
	;;
	-h|--hostname)
	shift
	HOSTNAME="$1"
	;;
	-e|--letsencrypt)
	shift
	LETSENCRYPT_HOSTNAME="$1"
	;;
	--email)
	shift
	MY_EMAIL_ADDRESS="$1"
	;;
	-s|--server)
	shift
	LETSENCRYPT_SERVER="$1"
	;;
	-c|--country)
	shift
	COUNTRY_CODE="$1"
	;;
	-a|--area)
	shift
	AREA="$1"
	;;
	-l|--location)
	shift
	LOCATION="$1"
	;;
	-o|--organisation)
	shift
	ORGANISATION="$1"
	;;
	-u|--unit)
	shift
	UNIT="$1"
	;;
	--ca)
	shift
	EXTENSIONS="-extensions v3_ca"
	ORGANISATION="Freedombone-CA"
	;;
	--nodh)
	shift
	NODH="true"
	;;
	--dhkey)
	shift
	DH_KEYLENGTH=${1}
	;;
	*)
	# unknown option
	;;
esac
shift
done

if [ ! $HOSTNAME ]; then
	if [ ! $LETSENCRYPT_HOSTNAME ]; then
		echo $'No hostname specified'
		exit 5748
	fi
fi

if ! which openssl > /dev/null ;then
	echo $"$0: openssl is not installed, exiting" 1>&2
	exit 5689
fi

if [ ! -d /etc/ssl/mycerts ]; then
	mkdir /etc/ssl/mycerts
fi

CERTFILE=$HOSTNAME

function add_cert_letsencrypt {
	CERTFILE=$LETSENCRYPT_HOSTNAME

	# obtain the email address for the admin user
	if [ ! $MY_EMAIL_ADDRESS ]; then
		if [ -f $CONFIGURATION_FILE ]; then
			if grep -q "MY_EMAIL_ADDRESS=" $CONFIGURATION_FILE; then
				MY_EMAIL_ADDRESS=$(cat $CONFIGURATION_FILE | grep "MY_EMAIL_ADDRESS=" | awk -F '=' '{print $2}')
			fi
		fi
	fi
	if [ ! $MY_EMAIL_ADDRESS ]; then
		if [ -f $COMPLETION_FILE ]; then
			if grep -q "Admin user:" $COMPLETION_FILE; then
				ADMIN_USER=$(cat $COMPLETION_FILE | grep "Admin user" | awk -F ':' '{print $2}')
				MY_EMAIL_ADDRESS=$ADMIN_USER@$HOSTNAME
			fi
		fi
	fi

	if [ ! -d $INSTALL_DIR ]; then
		mkdir -p $INSTALL_DIR
	fi
	cd $INSTALL_DIR

	# obtain the repo
	if [ ! -d ${INSTALL_DIR}/letsencrypt ]; then
		git_clone $LETSENCRYPT_REPO ${INSTALL_DIR}/letsencrypt
		if [ ! -d ${INSTALL_DIR}/letsencrypt ]; then
			exit 76283
		fi
	else
		cd ${INSTALL_DIR}/letsencrypt
		git_pull $LETSENCRYPT_REPO
	fi

	# stop the web server
	systemctl stop nginx

	cd ${INSTALL_DIR}/letsencrypt
	./letsencrypt-auto certonly --server $LETSENCRYPT_SERVER --standalone -d $LETSENCRYPT_HOSTNAME --renew-by-default --agree-tos --email $MY_EMAIL_ADDRESS
	if [ ! "$?" = "0" ]; then
		echo $"Failed to install letsencrypt for domain $LETSENCRYPT_HOSTNAME"
		systemctl start nginx
		exit 63216
	fi

	# replace some legacy filenames
	if [ -f /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.bundle.crt ]; then
		mv /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.bundle.crt /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem
	fi
	if [ -f /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.crt ]; then
		mv /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.crt /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem
	fi
	sed -i "s|ssl_certificate /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.bundle.crt|ssl_certificate /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem|g" /etc/nginx/sites-available/$LETSENCRYPT_HOSTNAME
	sed -i "s|ssl_certificate /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.crt|ssl_certificate /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem|g" /etc/nginx/sites-available/$LETSENCRYPT_HOSTNAME

	# link the private key
	if [ -f /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key ]; then
		if [ ! -f /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key.old ]; then
			mv /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key.old
		else
			rm -f /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key
		fi
	fi
	ln -s /etc/letsencrypt/live/${LETSENCRYPT_HOSTNAME}/privkey.pem /etc/ssl/private/${LETSENCRYPT_HOSTNAME}.key

	# link the public key
	if [ -f /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem ]; then
		if [ ! -f /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem.old ]; then
			mv /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem.old
		else
			rm -f /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem
		fi
	fi
	ln -s /etc/letsencrypt/live/${LETSENCRYPT_HOSTNAME}/fullchain.pem /etc/ssl/certs/${LETSENCRYPT_HOSTNAME}.pem

	cp /etc/letsencrypt/live/${LETSENCRYPT_HOSTNAME}/fullchain.pem /etc/ssl/mycerts/${LETSENCRYPT_HOSTNAME}.pem

	systemctl start nginx

	${PROJECT_NAME}-pin-cert $LETSENCRYPT_HOSTNAME
	if [ ! "$?" = "0" ]; then
		echo $"Certificate for $LETSENCRYPT_HOSTNAME could not be pinned"
		exit 62878
	fi
}

function add_cert_selfsigned {
	if [[ $ORGANISATION == "Freedombone-CA" ]]; then
		CERTFILE="ca-$HOSTNAME"
	fi

	openssl req -x509 ${EXTENSIONS} -nodes -days 3650 -sha256 \
		-subj "/O=$ORGANISATION/OU=$UNIT/C=$COUNTRY_CODE/ST=$AREA/L=$LOCATION/CN=$HOSTNAME" \
		-newkey rsa:4096 -keyout /etc/ssl/private/${CERTFILE}.key \
		-out /etc/ssl/certs/${CERTFILE}.crt
	chmod 400 /etc/ssl/private/${CERTFILE}.key
	chmod 640 /etc/ssl/certs/${CERTFILE}.crt
	cp /etc/ssl/certs/${CERTFILE}.crt /etc/ssl/mycerts

	${PROJECT_NAME}-pin-cert $CERTFILE
	if [ ! "$?" = "0" ]; then
		echo $"Certificate for $CERTFILE could not be pinned"
		exit 62879
	fi
}

function generate_dh_params {
	if [ ! $NODH ]; then
		if [ ! -f /etc/ssl/certs/${CERTFILE}.dhparam ]; then
			${PROJECT_NAME}-dhparam -h ${CERTFILE} --fast yes
		fi
	fi
}

function restart_web_server {
	if [ -f /etc/init.d/nginx ]; then
		/etc/init.d/nginx reload
	fi
}

function make_cert_bundle {
	# Create a bundle of your certificates
	cat /etc/ssl/mycerts/*.crt /etc/ssl/mycerts/*.pem > /etc/ssl/${PROJECT_NAME}-bundle.crt
	tar -czvf /etc/ssl/${PROJECT_NAME}-certs.tar.gz /etc/ssl/mycerts/*.crt /etc/ssl/mycerts/*.pem
}

function create_cert {
	if [ $LETSENCRYPT_HOSTNAME ]; then
		add_cert_letsencrypt
	else
		add_cert_selfsigned
	fi
}

read_repo_servers
create_cert
generate_dh_params
restart_web_server
make_cert_bundle

exit 0
