diff --git a/script/ssl/docker-compose.yml.template b/script/ssl/docker-compose.yml.template index 8eb7c64..191b29d 100644 --- a/script/ssl/docker-compose.yml.template +++ b/script/ssl/docker-compose.yml.template @@ -10,8 +10,11 @@ services: environment: AUTOACME_CONTAINER_HOSTNAME: ${HOSTNAME:?"HINT - You may run 'export HOSTNAME' first."} AUTOACME_GIT_REPOSITORY_VIA_SSH: 'ssh://git@git.your-domain.net/your-repo.git' - # Optionally you can set a path inside the git repository + # Optionally you can set a path inside the git repository, requires a repository. AUTOACME_PATH_IN_GIT_REPOSITORY: 'hosts/all/etc/ssl/domains/' + # Optionally you can define a alias domain, see: https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode + AUTOACME_CHALLENGE_ALIAS: 'alias-domain.net' # See: https://github.com/acmesh-official/acme.sh/wiki/dnsapi and search your provider like 'hetzner' e.g. AUTOACME_DNS_PROVIDER: 'dns_hetzner' HETZNER_Token: 'your-token' + diff --git a/script/ssl/renewCerts.sh b/script/ssl/renewCerts.sh index de12af9..47d3916 100644 --- a/script/ssl/renewCerts.sh +++ b/script/ssl/renewCerts.sh @@ -171,26 +171,57 @@ function isExpiringSoon(){ return 1 } -function single(){ - local _ACME_FILE _DOMAIN _DOMAIN_FOLDER _MODE _OPTIONS _RESULT_CERTS +function printChallengeAliasOption() { + local _CHALLENGE_ALIAS_FILE _DOMAIN _MODE _RESULT_CERTS + _RESULT_CERTS="${RESULT_CERTS:?"printChallengeAliasOption(): Missing global parameter RESULT_CERTS"}" + _MODE="${1:?"printChallengeAliasOption(): Missing first parameter MODE"}" + _DOMAIN="${2:?"printChallengeAliasOption: Missing second parameter DOMAIN"}" + _CHALLENGE_ALIAS_FILE="${_RESULT_CERTS}_.${_DOMAIN}/challenge-alias" + readonly _CHALLENGE_ALIAS_FILE _DOMAIN _MODE _RESULT_CERTS + + # store given challenge-alias and print CHALLENGE_ALIAS_OPTION + [ "${_MODE}" = "dnsWithAlias" ] \ + && echo "${ACME_CHALLENGE_ALIAS:?"printChallengeAliasOption(): Missing global parameter ACME_CHALLENGE_ALIAS"}" > "${_CHALLENGE_ALIAS_FILE}" \ + && echo " --challenge-alias ${ACME_CHALLENGE_ALIAS}" \ + && return 0 + + # read stored challenge-alias and print CHALLENGE_ALIAS_OPTION + [ "${_MODE}" = "dns" ] \ + && [ -f "${_CHALLENGE_ALIAS_FILE}" ] \ + && echo " --challenge-alias $(cat "${_CHALLENGE_ALIAS_FILE}")" \ + && return 0 + + return 0 +} + +function printDomainFolder() { + local _DOMAIN _DOMAIN_FOLDER _MODE _RESULT_CERTS _RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}" - _ACME_FILE="${ACME_FILE:?"single(): Missing global parameter ACME_FILE"}" _MODE="${1:?"single(): Missing first parameter MODE"}" _DOMAIN="${2:?"single(): Missing second parameter DOMAIN"}" [ "${_MODE}" = "dns" ] \ && _DOMAIN_FOLDER="${_RESULT_CERTS}_.${_DOMAIN}" + [ "${_MODE}" = "dnsWithAlias" ] \ + && _DOMAIN_FOLDER="${_RESULT_CERTS}_.${_DOMAIN}" [ "${_MODE}" = "http" ] \ && _DOMAIN_FOLDER="${_RESULT_CERTS}${_DOMAIN}" + readonly _DOMAIN _DOMAIN_FOLDER _MODE _RESULT_CERTS - # always --force because we check expiring on ourself - # _OPTIONS="--issue --force --test" - _OPTIONS="--issue --force" - [ "${_MODE}" = "dns" ] \ - && _OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"} --domain *.${_DOMAIN}" - [ "${_MODE}" = "http" ] \ - && _OPTIONS="${_OPTIONS} --webroot /var/www/letsencrypt" - readonly _ACME_FILE _DOMAIN _DOMAIN_FOLDER _MODE _OPTIONS _RESULT_CERTS + echo "${_DOMAIN_FOLDER}" \ + && return 0 + + return 1 +} + +function single(){ + local _ACME_FILE _DOMAIN _MODE _RESULT_CERTS + _RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}" + _ACME_FILE="${ACME_FILE:?"single(): Missing global parameter ACME_FILE"}" + + _MODE="${1:?"single(): Missing first parameter MODE"}" + _DOMAIN="${2:?"single(): Missing second parameter DOMAIN"}" + readonly _ACME_FILE _DOMAIN _MODE _RESULT_CERTS ! [ -f "${_ACME_FILE}" ] \ && echo "Program 'acme.sh' seams not to be installed. Try run 'renewCerts.sh --setup'." \ @@ -200,6 +231,10 @@ function single(){ ! checkConfigViaHttp "${_MODE}" "${_DOMAIN}" \ && return 1 + local _DOMAIN_FOLDER + _DOMAIN_FOLDER="$(printDomainFolder "${_MODE}" "${_DOMAIN}")" + readonly _DOMAIN_FOLDER + # create folder for results ! [ -d "${_DOMAIN_FOLDER}" ] \ && echo -n "Creating folder '${_DOMAIN_FOLDER}'... " \ @@ -216,12 +251,27 @@ function single(){ [ -f "${_DOMAIN_FOLDER}/private.key" ] \ && cp --preserve=mode,ownership "${_DOMAIN_FOLDER}/private.key" "${_DOMAIN_FOLDER}/private.key.bak" + local _OPTIONS + # always --force because we check expiring on ourself + # _OPTIONS="--issue --force --test" + _OPTIONS="--issue --force" + [ "${_MODE}" = "dns" ] \ + && _OPTIONS="${_OPTIONS}$(printChallengeAliasOption "${_MODE}" "${_DOMAIN}")" \ + && _OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"} --domain *.${_DOMAIN}" + [ "${_MODE}" = "dnsWithAlias" ] \ + && _OPTIONS="${_OPTIONS}$(printChallengeAliasOption "${_MODE}" "${_DOMAIN}")" \ + && _OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"} --domain *.${_DOMAIN}" + [ "${_MODE}" = "http" ] \ + && _OPTIONS="${_OPTIONS} --webroot /var/www/letsencrypt" + readonly _OPTIONS + ${_ACME_FILE} ${_OPTIONS} \ --domain "${_DOMAIN}" \ --server "letsencrypt" \ --keylength "ec-384" \ --fullchain-file "${_DOMAIN_FOLDER}/fullchain.crt" \ --key-file "${_DOMAIN_FOLDER}/private.key" \ + && openssl pkcs12 -export -in "${_DOMAIN_FOLDER}/fullchain.crt" -inkey "${_DOMAIN_FOLDER}/private.key" -out "${_DOMAIN_FOLDER}/bundle.pkx" -passout pass: \ && echo "Certificate of domain '${_DOMAIN}' was updated." \ && tryGitPush "${_DOMAIN}" \ && return 0 @@ -303,8 +353,11 @@ function setup(){ function usage(){ echo echo 'Commands:' - echo ' (--dns|--http) --own [--force] : Iterates all domains found in RESULT_CERTS.' - echo ' (--dns|--http) --single DOMAIN [--force] : Issues a certificate for the given domain.' + echo ' (--dns|--http) --own [--force] : Iterates all domains found in RESULT_CERTS.' + echo + echo ' --dns --single DOMAIN [--force] : Issues a certificate for the given domain using DNS mode.' + echo ' --dns-withAlias --single DOMAIN [--force] : Issues a certificate for the given domain using DNS mode with challange alias.' + echo ' --http --single DOMAIN [--force] : Issues a certificate for the given domain using HTTP mode.' echo echo 'Current environment:' echo " Full name of this script: OWN_FULLNAME='${OWN_FULLNAME}'" @@ -313,6 +366,7 @@ function usage(){ echo " Tar file containing the setup of 'acme.sh': ACME_TAR_FILE='${ACME_TAR_FILE}'" echo " Setup file of 'acme.sh' after extraction: ACME_SETUP_FILE='${ACME_SETUP_FILE}'" echo " Full name of the installed script 'acme.sh': ACME_FILE='${ACME_FILE}'" + echo " 'acme.sh' will this alias domain in dns-mode: ACME_CHALLENGE_ALIAS='${ACME_CHALLENGE_ALIAS}'" echo " Output:" echo " Path were the issued certificate are saved: RESULT_CERTS='${RESULT_CERTS}'" @@ -327,15 +381,16 @@ function main(){ && source "/autoACME.env" \ && echo "Environment '/autoACME.env' loaded." - local ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS + local ACME_CHALLENGE_ALIAS ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS OWN_FULLNAME="$(readlink -e ${0})" + ACME_CHALLENGE_ALIAS="${AUTOACME_CHALLENGE_ALIAS:-""}" ACME_FILE="/root/.acme.sh/acme.sh" ACME_VERSION="acme.sh-3.1.1" ACME_SETUP_FILE="/tmp/acme.sh-setup/${ACME_VERSION}/acme.sh" ACME_TAR_FILE="${OWN_FULLNAME%/*}/${ACME_VERSION}.tar.gz" RESULT_CERTS="${AUTOACME_RESULT_CERTS%/}" #Removes shortest matching pattern '/' from the end RESULT_CERTS="${RESULT_CERTS:-"/etc/nginx/ssl"}/" - readonly ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS + readonly ACME_CHALLENGE_ALIAS ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS local REPOSITORY_URL isGitRepository \ @@ -363,6 +418,11 @@ function main(){ single "dns" "${3}" "${4}" \ && return 0 ;; + --dns-withAlias--single) + echo "Issue single certificate '${3}' at $(date +%F_%T) via DNS using a challange alias:" + single "dnsWithAlias" "${3}" "${4}" \ + && return 0 + ;; --http--single) echo "Issue single certificate '${3}' at $(date +%F_%T) via HTTP:" single "http" "${3}" "${4}" \ diff --git a/script/ssl/start.sh b/script/ssl/start.sh index d63e2bd..48f2f32 100644 --- a/script/ssl/start.sh +++ b/script/ssl/start.sh @@ -7,22 +7,28 @@ function createEnvironmentFile() { readonly _ENVIRONMENT_FILE _REPOSITORY_FOLDER # Save environment for cronjob - export -p | grep -v -E "(HOME|OLDPWD|PWD|SHLVL)" > "${_ENVIRONMENT_FILE}" + export -p | grep -v -E "(HOME|OLDPWD|PWD|SHLVL)" > "${_ENVIRONMENT_FILE}" \ + && echo "SUCCESS: there values were exported into file: '${_ENVIRONMENT_FILE}'" \ + && echo " - AUTOACME_CONTAINER_HOSTNAME: ${AUTOACME_CONTAINER_HOSTNAME}" \ + && echo " - AUTOACME_DNS_PROVIDER: ${AUTOACME_DNS_PROVIDER}" \ + && echo " - AUTOACME_CHALLENGE_ALIAS: ${AUTOACME_CHALLENGE_ALIAS}" \ + && echo " (additional the DNS provider specific values were added)" \ + && echo " - AUTOACME_GIT_REPOSITORY_VIA_SSH: ${AUTOACME_GIT_REPOSITORY_VIA_SSH}" \ + && echo " - AUTOACME_PATH_IN_GIT_REPOSITORY: ${AUTOACME_PATH_IN_GIT_REPOSITORY}" [ "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" == "" ] \ && echo "declare -x AUTOACME_RESULT_CERTS=\"${AUTOACME_REPOSITORY_FOLDER#/}\"" >> "${_ENVIRONMENT_FILE}" \ - && echo "SUCCESS: saved environment (without git) into file '${_ENVIRONMENT_FILE}'." \ - && return 0 + && echo "SUCCESS: added AUTOACME_RESULT_CERTS (without git) into file '${_ENVIRONMENT_FILE}'." \ + && echo " - AUTOACME_RESULT_CERTS: ${AUTOACME_REPOSITORY_FOLDER#/}" \ + && echo " (depends on if there is a git repo and the path for the certs in it)" - echo "declare -x AUTOACME_RESULT_CERTS=\"${AUTOACME_REPOSITORY_FOLDER}${AUTOACME_PATH_IN_GIT_REPOSITORY#/}\"" >> "${_ENVIRONMENT_FILE}" \ - && echo "SUCCESS: saved environment (with git) into file '${_ENVIRONMENT_FILE}'." \ - && return 0 + ! [ "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" == "" ] \ + && echo "declare -x AUTOACME_RESULT_CERTS=\"${AUTOACME_REPOSITORY_FOLDER}${AUTOACME_PATH_IN_GIT_REPOSITORY#/}\"" >> "${_ENVIRONMENT_FILE}" \ + && echo "SUCCESS: added AUTOACME_RESULT_CERTS (with git) into file '${_ENVIRONMENT_FILE}'." \ + && echo " - AUTOACME_RESULT_CERTS: ${AUTOACME_REPOSITORY_FOLDER}${AUTOACME_PATH_IN_GIT_REPOSITORY#/}" \ + && echo " (depends on if there is a git repo and the path for the certs in it)" - echo - echo "FAILED: something went wrong during the creation of the environment file: '${_ENVIRONMENT_FILE}'..." - echo " This file is mandantory to use 'renewCerts.sh' with cron." - echo - return 1 + return 0 } function ensureThereAreSSHKeys() { @@ -118,7 +124,8 @@ function prepareThisRuntimeForUsingGitOrIgnore() { && echo \ && return 0 - ensureThereAreSSHKeys \ + echo \ + && ensureThereAreSSHKeys \ && ensureGitIsInstalled \ && ensureRepositoryIsAvailableAndWritable \ && return 0