#!/bin/bash
# This file is part of acme-setup (code.ivysaur.me)
#
# Need to add the following to *all* server blocks in nginx:
#  location /.well-known/acme-challenge/ {
#      alias /var/www/acme-challenges/;
#      try_files $uri =404;
#  }

set -eu

# Check dependencies

if [[ ! -f /usr/bin/sudo ]] ; then
	echo "FATAL: Missing sudo binary" >&2
	exit 1
fi

# Create surrogate user

if [[ ! -d /home/acme ]] ; then
	adduser --system --home /home/acme --disabled-password --disabled-login --shell /bin/bash acme
fi

(
	# Retrieve acme-tiny.py

	cd /home/acme/
	curl 'https://raw.githubusercontent.com/diafygi/acme-tiny/b37284d218eed51b862a42a3f792162fd01f4e15/acme_tiny.py' > ./acme-tiny.py

	echo '8adf21b8947adfe2fbead3fa8f22d9cd acme-tiny.py' > ./acme-tiny.sfv
	
	if ! md5sum -c ./acme-tiny.sfv ; then
		echo "FATAL: acme-tiny hash mismatch" >&2
		exit 1
	fi
	
	chmod 700 ./acme-tiny*
	chown acme:nogroup ./acme-tiny*
	
	# Retrieve intermediate cert
	
	curl https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > ./intermediate.crt
	
	# Generate LE account key
	
	su acme -c 'openssl genrsa 4096 > account.key'
	
	# Make CSR directory
	
	mkdir csr
	chmod 700 ./csr
	chown acme:nogroup ./csr
	
)

# Write locations for acme user, readable by www-data user

mkdir /var/www/acme-{challenges,certs}/
chown acme:www-data /var/www/acme-{challenges,certs}/
chmod 750 /var/www/acme-{challenges,certs}/

# Allow acme user SUDO 'systemctl nginx reload'

echo 'acme ALL=(root) NOPASSWD: /bin/systemctl reload nginx' >> /etc/sudoers

# Generation/Signing scripts

touch /home/acme/csr{gen,sign}.sh
chown acme:nogroup /home/acme/csr{gen,sign}.sh
chmod 700 /home/acme/csr{gen,sign}.sh

# Write csrgen.sh

cat > /home/acme/csrgen.sh <<'EOD'
#!/bin/bash
# This file is part of acme-setup (code.ivysaur.me)

set -eu

main() {
	
	if [[ $# -lt 1 ]] ; then
		echo "FATAL: Expected at least one argument" >&2
		exit 1
	fi
	
	if [[ ! -f /etc/ssl/openssl.cnf ]] ; then
		echo "FATAL: Platform is missing expected file /etc/ssl/openssl.cnf" >&2
		exit 2
	fi
	
	if [[ -f /var/www/acme-certs/$1.key ]] ; then
		echo "FATAL: The file $1.key already exists" >&2
		exit 3
	fi
	
	# join arguments
	local SANText=''
	for arg in "$@" ; do
		SANText="${SANText}DNS:${arg},"
	done

	# strip trailing comma
	SANText="${SANText:0:$(( ${#SANText} - 1 ))}"
	
	# generate
	touch "/var/www/acme-certs/$1.key"
	chmod 644 "/var/www/acme-certs/$1.key"
	
	openssl genrsa 4096 > "/var/www/acme-certs/$1.key"
	
	openssl req \
		-new \
		-sha256 \
		-key "/var/www/acme-certs/$1.key" \
		-subj "/" \
		-reqexts SAN \
		-config <(
			cat /etc/ssl/openssl.cnf
			printf "[SAN]\nsubjectAltName=${SANText}"
		) \
	> "/home/acme/csr/$1.csr"

}

main "$@"

EOD

# Write csrsign.sh

cat > /home/acme/csrsign.sh <<'EOD'
#!/bin/bash
# This file is part of acme-setup (code.ivysaur.me)

set -eu

main() {

	date

	cd csr

	ls | grep '.csr$' | while read -r csrfile ; do

		local base=$(basename -s.csr "$csrfile")
		
		# Pre-setup permissions.
		touch "/var/www/acme-certs/${base}.crt"
		chmod 644 "/var/www/acme-certs/${base}.crt"
		
		# Sign the certificate.
		if python /home/acme/acme-tiny.py \
			--account-key ../account.key \
			--csr "$csrfile" \
			--acme-dir /var/www/acme-challenges/ \
			> "/var/www/acme-certs/${base}.crt"
		then
			touch "/var/www/acme-certs/${base}.ca-chain"
			chmod 644 "/var/www/acme-certs/${base}.ca-chain"
		
			# Append intermediate certificate for nginx
			cat \
				"/var/www/acme-certs/${base}.crt" \
				/home/acme/intermediate.crt \
				> "/var/www/acme-certs/${base}.ca-chain"
		fi
		
	done

}

main "$@" >>/home/acme/csrsign.log 2>&1

sudo /bin/systemctl reload nginx

EOD

# Add cronjob for acme user, to re-sign certificates every month

echo '0 0 1 * * /home/acme/csrsign.sh' | crontab -u acme -

# Done

echo "INFO: acme-setup.sh script completed."
