supports dns alias

This commit is contained in:
Martin Berghaus
2025-10-25 10:12:46 +02:00
parent 2b3da35e19
commit d676bd33e2
3 changed files with 98 additions and 28 deletions

View File

@@ -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'

View File

@@ -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}" \

View File

@@ -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