#!/bin/sh # configuration web_root_path="/service/html" cert_root_path="/service/ssl" temp_path="/service/temp" cert_bot_ssl="/service/letsencrypt/ssl" cert_bot_work="/service/letsencrypt/work" cert_bot_log="/service/letsencrypt/log" server_retries=10 log_file="" # leave empty for stdout # variables local_webserver="http://proxy:8080" domain_file="$temp_path/domains.txt" certs_updated="$cert_root_path/.updated" #functions function log() { local time=$(date +%F_%T) local prefix="LOG" case $1 in ERR | ERROR) prefix="ERR" shift ;; WAR | WARNING) prefix="WAR" shift ;; esac local func=$1; shift # remove any \n #local text=$(echo "$@"| tr '\n' '#' | sed 's|#|\\n |g') case "$log_file" in "") echo "[$time]:[$prefix]:[$0][$func]: $@" 2>&1 ;; *) echo "[$time]:[$prefix]:[$0][$func]: $@" >> log_path ;; esac } function env_domain_file() { log env_domain_file $(echo "ENV DOMAINS: $DOMAINS" | tr '\n' '#' | sed 's|#|\\n |g') echo -e "$DOMAINS" > $1 } function verify_domain() { # TODO: create and verify token in web_root_path via curl if nslookup $1 > /dev/null 2>&1; then # domain exists return 0 else # false return 1 fi } function read_file() { local func=$1 local file=$2 local result=0 while read -r line || [[ -n "$line" ]]; do # skip empty lines if READ_EMPTY_LINES is not set [ -z ${READ_EMPTY_LINES+x} ] && [ "${#line}" -lt 1 ] && continue log read_file "LINE: $line" log read_file "FUNC START [$func]" if $func $line; then log read_file "FUNC END [$func]" else result=1 log ERROR read_file "FUNC END [$func]" fi done < "$file" return $result } function init_cert() { local domain=$1 local cert_path="$cert_root_path/$domain" if [[ ! -f "$cert_path/fullchain.pem" ]]; then log init_cert "Creating temporary cert for [$domain]" mkdir -p "$cert_path" openssl genrsa -out "$cert_path/privkey.pem" 4096 openssl req -new -key "$cert_path/privkey.pem" -out "$temp_path/$domain.csr" -nodes -subj \ "/C=PT/ST=World/L=World/O=${domain}/OU=temp lda/CN=${domain}/emailAddress=contact@${domain}" openssl x509 -req -days 365 -in "$temp_path/$domain.csr" -signkey "$cert_path/privkey.pem" -out "$cert_path/fullchain.pem" fi } function copy_cert() { local domain=$1 local cert_path="$cert_root_path/$domain" if [[ -f "$cert_bot_ssl/live/$domain/privkey.pem" ]]; then cp "$cert_bot_ssl/live/$domain/privkey.pem" "$cert_path/privkey.pem" cp "$cert_bot_ssl/live/$domain/fullchain.pem" "$cert_path/fullchain.pem" return 0 else log ERROR copy_cert "No key exists at "$cert_bot_ssl/live/$domain/privkey.pem" for [$domain]" return 1 fi } function verify_webserver() { wget -q -O /dev/null "$1/check/" > /dev/null 2>&1 } function verify_local_webserver() { local domain=$1 # todo check if configured for domain wget -q -O /dev/null "$local_webserver/check/" > /dev/null 2>&1 } function get_cert() { local root_domain=$1 local domains=$2 # comma seperated list, first is file path local retries=0 local webserver_exists=0 verify_local_webserver $root_domain && webserver_exists=1 while [ ! $webserver_exists -eq 1 ] && [ "$retries" -lt "$server_retries" ]; do retries=$(( retries+1 )) log get_cert WARNING "Webserver for [$root_domain] not reachable, trying again in 1s" sleep 1 verify_local_webserver $root_domain && webserver_exists=1 done if [ $webserver_exists -eq 1 ] ; then log get_cert "Getting cert for [$root_domain] with [$domains]" # PRODUCTION needs to be set in env if [ -z ${PRODUCTION+x} ]; then log WARNING get_cert "PRODUCTION not set, exiting" return 0 fi log get_cert "Running certbot" certbot certonly \ --config-dir "$cert_bot_ssl" \ --work-dir "$cert_bot_work" \ --logs-dir "$cert_bot_log" \ --agree-tos \ --domains "$domains" \ --email "contact@${root_domain}" \ --no-eff-email \ --expand \ --noninteractive \ --webroot \ --webroot-path "$web_root_path" # $OPTIONS || true else log get_cert ERROR "Webserver for [$root_domain] not reachable after $server_retries retries" return 1 fi } function domain_init() { if [ "$1" == "" ]; then log domain_init "Skipping empty input" return 0 fi local domain=$1; # don't really care at this point if the domain exists # if ! verify_domain $domain; then # log ERROR domain_init "Root domain [$domain] does not exist" # return 1 # fi init_cert $domain } function domain_handler() { if [ "$1" == "" ]; then log domain_handler "Skipping empty input" return 0 fi local domain=$1; shift local subs=$@ local domain_string="$domain" log domain_handler "Checking root domain [$domain]" if ! verify_domain $domain; then log ERROR domain_handler "Root domain [$domain] does not exist" return 1 fi for sub in $subs ; do local subdomain="$sub.$domain" log domain_handler "Checking sub domain [$subdomain]" if ! verify_domain $subdomain; then log WARNING domain_handler "Sub domain [$subdomain] does not exist" else domain_string="$domain_string,$subdomain" fi done init_cert $domain if ! get_cert $domain $domain_string; then log ERROR domain_handler "No cert generated by certbot for [$domain]" return 1 fi copy_cert $domain } function main() { intervall="${1-12h}" while :; do log main "Starting intervall" # load env variable with domains env_domain_file "$domain_file" # create temp certs for domains if none exists # nginx need certs to start read_file domain_init "$domain_file" # get or renew domain certs # requires nginx to be started # timout is set via $server_retries (default 10s) read_file domain_handler "$domain_file" # indicated that certs have been updated to nginx touch "$certs_updated" log main "Ending intervall" log main "Next interval in $intervall" sleep "$intervall" done } main $@