mirror of
https://github.com/m8tin/cis.git
synced 2025-12-06 07:48:26 +01:00
full support of challenge-alias-domain and dns issues all certs
This commit is contained in:
@@ -6,11 +6,11 @@
|
|||||||
FROM ubuntu:latest
|
FROM ubuntu:latest
|
||||||
|
|
||||||
# Update repositories
|
# Update repositories
|
||||||
RUN echo Version 20251005v5
|
RUN echo Version 20251030v1
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y apt-utils
|
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y apt-utils
|
||||||
|
|
||||||
#### BEGIN INSTALLATION ###################################################
|
#### BEGIN INSTALLATION ###################################################
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-client curl cron
|
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y cron curl dnsutils openssh-client
|
||||||
|
|
||||||
ADD acme.sh-3.1.1.tar.gz /tmp/acme.sh-setup/
|
ADD acme.sh-3.1.1.tar.gz /tmp/acme.sh-setup/
|
||||||
COPY renewCerts.sh /renewCerts.sh
|
COPY renewCerts.sh /renewCerts.sh
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ services:
|
|||||||
AUTOACME_GIT_REPOSITORY_VIA_SSH: 'ssh://git@git.your-domain.net/your-repo.git'
|
AUTOACME_GIT_REPOSITORY_VIA_SSH: 'ssh://git@git.your-domain.net/your-repo.git'
|
||||||
# Optionally you can set a path inside the git repository, requires a repository.
|
# Optionally you can set a path inside the git repository, requires a repository.
|
||||||
AUTOACME_PATH_IN_GIT_REPOSITORY: 'hosts/all/etc/ssl/domains/'
|
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.
|
# See: https://github.com/acmesh-official/acme.sh/wiki/dnsapi and search your provider like 'hetzner' e.g.
|
||||||
AUTOACME_DNS_PROVIDER: 'dns_hetzner'
|
AUTOACME_DNS_PROVIDER: 'dns_hetzner'
|
||||||
HETZNER_Token: 'your-token'
|
HETZNER_Token: 'your-token'
|
||||||
|
|||||||
@@ -5,22 +5,30 @@
|
|||||||
# /var/www/letsencrypt/.well-known/acme-challenge
|
# /var/www/letsencrypt/.well-known/acme-challenge
|
||||||
|
|
||||||
function checkConfigViaHttp() {
|
function checkConfigViaHttp() {
|
||||||
local _DOMAIN _MODE _LOCAL_FILE _LOCAL_URL _PUBLIC_URL
|
local _DOMAIN _MODE _LOCAL_FILE _LOCAL_FOLDER _LOCAL_URL _PUBLIC_URL
|
||||||
_MODE="${1:?"checkConfigViaHttp(): Missing first parameter MODE"}"
|
_MODE="${1:?"checkConfigViaHttp(): Missing first parameter MODE"}"
|
||||||
_DOMAIN="${2:?"checkConfigViaHttp(): Missing second parameter DOMAIN"}"
|
_DOMAIN="${2:?"checkConfigViaHttp(): Missing second parameter DOMAIN"}"
|
||||||
_LOCAL_FILE="/var/www/letsencrypt/.well-known/acme-challenge/${_DOMAIN}"
|
_LOCAL_FOLDER="/var/www/letsencrypt/.well-known/acme-challenge/"
|
||||||
|
_LOCAL_FILE="${_LOCAL_FOLDER}${_DOMAIN}"
|
||||||
_LOCAL_URL="http://localhost/.well-known/acme-challenge/${_DOMAIN}"
|
_LOCAL_URL="http://localhost/.well-known/acme-challenge/${_DOMAIN}"
|
||||||
_PUBLIC_URL="http://${_DOMAIN}/.well-known/acme-challenge/${_DOMAIN}"
|
_PUBLIC_URL="http://${_DOMAIN}/.well-known/acme-challenge/${_DOMAIN}"
|
||||||
readonly _DOMAIN _MODE _LOCAL_FILE _LOCAL_URL _PUBLIC_URL
|
readonly _DOMAIN _MODE _LOCAL_FILE _LOCAL_FOLDER _LOCAL_URL _PUBLIC_URL
|
||||||
|
|
||||||
# Skip check if mode is not http
|
# Skip check if mode is not http
|
||||||
[ "${_MODE}" != "http" ] \
|
[ "${_MODE}" != "http" ] \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
|
# Fail because wildcard certificate
|
||||||
|
[ "${_MODE}" == "http" ] \
|
||||||
|
&& isWildcardCertificate "${_DOMAIN}" \
|
||||||
|
&& echo "Wildcard certificates are not supported via HTTP." \
|
||||||
|
&& return 1
|
||||||
|
|
||||||
_CHECK="Available on $(hostname)@$(date)"
|
_CHECK="Available on $(hostname)@$(date)"
|
||||||
|
|
||||||
echo -n "Check domain '${_DOMAIN}'..." \
|
echo -n "Check domain '${_DOMAIN}'..." \
|
||||||
&& echo "${_CHECK}" > "/var/www/letsencrypt/.well-known/acme-challenge/${_DOMAIN}" \
|
&& [ -d "${_LOCAL_FOLDER}" ] \
|
||||||
|
&& echo "${_CHECK}" > "${_LOCAL_FILE}" \
|
||||||
&& curl -4s "${_PUBLIC_URL}" | grep -q "${_CHECK}" \
|
&& curl -4s "${_PUBLIC_URL}" | grep -q "${_CHECK}" \
|
||||||
&& echo " Fertig" \
|
&& echo " Fertig" \
|
||||||
&& return 0
|
&& return 0
|
||||||
@@ -30,7 +38,7 @@ function checkConfigViaHttp(){
|
|||||||
|
|
||||||
curl -4s "${_LOCAL_URL}" | grep -q "${_CHECK}" \
|
curl -4s "${_LOCAL_URL}" | grep -q "${_CHECK}" \
|
||||||
&& echo " (check DNS first)" \
|
&& echo " (check DNS first)" \
|
||||||
|| echo " (check Webserver first)" \
|
|| echo " (check Webserver first)"
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -44,7 +52,7 @@ function isActive(){
|
|||||||
readonly _DOMAIN _MODE _RESULT_CERTS
|
readonly _DOMAIN _MODE _RESULT_CERTS
|
||||||
|
|
||||||
# If mode is dns the domain is active always
|
# If mode is dns the domain is active always
|
||||||
[ "${_MODE}" = "dns" ] \
|
[ "${_MODE}" == "dns" ] \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
nginx -T 2> /dev/null | grep -q "${_RESULT_CERTS}${_DOMAIN}/fullchain.crt" \
|
nginx -T 2> /dev/null | grep -q "${_RESULT_CERTS}${_DOMAIN}/fullchain.crt" \
|
||||||
@@ -61,6 +69,20 @@ function isGitRepository(){
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWildcardCertificate() {
|
||||||
|
local _DOMAIN
|
||||||
|
_DOMAIN="${1:?"isWildcardCertificate(): Missing first parameter DOMAIN"}"
|
||||||
|
readonly _DOMAIN
|
||||||
|
|
||||||
|
echo "${_DOMAIN}" | grep -q -F "_." \
|
||||||
|
&& return 0
|
||||||
|
|
||||||
|
echo "${_DOMAIN}" | grep -q -F "*." \
|
||||||
|
&& return 0
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
function tryGitPush() {
|
function tryGitPush() {
|
||||||
local _DOMAIN _NOW _RESULT_CERTS
|
local _DOMAIN _NOW _RESULT_CERTS
|
||||||
_RESULT_CERTS="${RESULT_CERTS:?"tryGitPush(): Missing global parameter RESULT_CERTS"}"
|
_RESULT_CERTS="${RESULT_CERTS:?"tryGitPush(): Missing global parameter RESULT_CERTS"}"
|
||||||
@@ -89,16 +111,15 @@ function tryGitPush(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function own() {
|
function own() {
|
||||||
local _DOMAINS _MODE
|
! [ -d "${RESULT_CERTS:?"own(): Missing global parameter RESULT_CERTS"}" ] \
|
||||||
_DOMAINS=("${RESULT_CERTS:?"own(): Missing global parameter RESULT_CERTS"}"/*)
|
|
||||||
|
|
||||||
_MODE="${1:?"own(): Missing first parameter MODE"}"
|
|
||||||
readonly _DOMAINS _MODE
|
|
||||||
|
|
||||||
! [ -d "${RESULT_CERTS}" ] \
|
|
||||||
&& echo "Trying to derive domain names from subfolders of '${RESULT_CERTS}', but it is not a folder!" \
|
&& echo "Trying to derive domain names from subfolders of '${RESULT_CERTS}', but it is not a folder!" \
|
||||||
&& return 1
|
&& return 1
|
||||||
|
|
||||||
|
local _DOMAINS _MODE
|
||||||
|
_DOMAINS=("${RESULT_CERTS}"*)
|
||||||
|
_MODE="${1:?"own(): Missing first parameter MODE"}"
|
||||||
|
readonly _DOMAINS _MODE
|
||||||
|
|
||||||
local _domain
|
local _domain
|
||||||
for _domain in "${_DOMAINS[@]}"; do
|
for _domain in "${_DOMAINS[@]}"; do
|
||||||
# just take names of folders
|
# just take names of folders
|
||||||
@@ -111,16 +132,13 @@ function own(){
|
|||||||
|
|
||||||
case "${_MODE}" in
|
case "${_MODE}" in
|
||||||
dns)
|
dns)
|
||||||
# dns and wildcard certifikate => take just them
|
# dns supports all options
|
||||||
echo "${_domain}" | grep -q -F "_." || continue
|
|
||||||
# cut front '_.'
|
|
||||||
_domain="${_domain#_.}"
|
|
||||||
;;
|
;;
|
||||||
http)
|
http)
|
||||||
# http and wildcard certifikate => skip
|
# http and wildcard certifikate => skip
|
||||||
echo "${_domain}" | grep -q -F "_." && continue
|
isWildcardCertificate "${_domain}" && continue
|
||||||
# ssl on domain inaktiv => skip
|
# ssl on domain inaktiv => skip
|
||||||
isActive "{_MODE}" "${_domain}" || continue
|
! isActive "{_MODE}" "${_domain}" && continue
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown mode: ${_MODE}"
|
echo "Unknown mode: ${_MODE}"
|
||||||
@@ -134,79 +152,57 @@ function own(){
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function isExpiringSoon(){
|
function continueIssuingCertificate() {
|
||||||
local _DOMAIN _DOMAIN_CERT _ENDDATE _MODE _NOW _REMAINING_DAYS _RESULT_CERTS
|
local _CERT_FILE_FULLCHAIN _PRETTY_DOMAIN
|
||||||
_RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}"
|
_CERT_FILE_FULLCHAIN="${1:?"continueIssuingCertificate(): Missing first parameter CERT_FILE_FULLCHAIN"}"
|
||||||
|
_PRETTY_DOMAIN="${2:?"continueIssuingCertificate(): Missing second parameter DOMAIN"}"
|
||||||
|
|
||||||
_MODE="${1:?"isExpiringSoon(): Missing first parameter MODE"}"
|
# cut front '_.' or '*.' and add '*.' again
|
||||||
_DOMAIN="${2:?"isExpiringSoon(): Missing second parameter DOMAIN"}"
|
isWildcardCertificate "${_PRETTY_DOMAIN}" \
|
||||||
|
&& _PRETTY_DOMAIN="${_PRETTY_DOMAIN#_.}" \
|
||||||
|
&& _PRETTY_DOMAIN="*.${_PRETTY_DOMAIN#\*.}"
|
||||||
|
|
||||||
[ "${_MODE}" = "dns" ] \
|
readonly _CERT_FILE_FULLCHAIN _PRETTY_DOMAIN
|
||||||
&& _DOMAIN_CERT="${_RESULT_CERTS}_.${_DOMAIN}/fullchain.crt"
|
|
||||||
[ "${_MODE}" = "http" ] \
|
|
||||||
&& _DOMAIN_CERT="${_RESULT_CERTS}${_DOMAIN}/fullchain.crt"
|
|
||||||
readonly _DOMAIN _DOMAIN_CERT _MODE _RESULT_CERTS
|
|
||||||
|
|
||||||
# forced => should be issued
|
# forced => should be issued
|
||||||
[ "${3:-""}" = "--force" ] \
|
[ "${3:-""}" == "--force" ] \
|
||||||
|
&& echo "Certificate for domain '${_PRETTY_DOMAIN}' is forced to be issued." \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
# no cert => should be issued
|
# no cert => should be issued
|
||||||
! [ -f "${_DOMAIN_CERT}" ] \
|
! [ -f "${_CERT_FILE_FULLCHAIN}" ] \
|
||||||
|
&& echo "No certificate for domain '${_PRETTY_DOMAIN}', so it will be issued." \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
_ENDDATE="$(openssl x509 -enddate -noout -in ${_DOMAIN_CERT} | cut -d= -f2)"
|
local _ENDDATE _NOW _REMAINING_DAYS
|
||||||
|
_ENDDATE="$(openssl x509 -enddate -noout -in ${_CERT_FILE_FULLCHAIN} | cut -d= -f2)"
|
||||||
_ENDDATE="$(date --date="${_ENDDATE}" --utc +%s)"
|
_ENDDATE="$(date --date="${_ENDDATE}" --utc +%s)"
|
||||||
|
|
||||||
_NOW="$(date --date now +%s)"
|
_NOW="$(date --date now +%s)"
|
||||||
_REMAINING_DAYS="$(( (_ENDDATE - _NOW) / 86400 ))"
|
_REMAINING_DAYS="$(( (_ENDDATE - _NOW) / 86400 ))"
|
||||||
readonly _ENDDATE _NOW _REMAINING_DAYS
|
readonly _ENDDATE _NOW _REMAINING_DAYS
|
||||||
|
|
||||||
echo "Certificate for domain '${_DOMAIN}' will expire in ${_REMAINING_DAYS} days."
|
|
||||||
|
|
||||||
# less than 30 days remaining => should be issued
|
# less than 30 days remaining => should be issued
|
||||||
[ "${_REMAINING_DAYS}" -le "30" ] \
|
[ "${_REMAINING_DAYS}" -le "30" ] \
|
||||||
|
&& echo "Certificate for domain '${_PRETTY_DOMAIN}' (${_REMAINING_DAYS} days remaining) will be issued." \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
|
echo "Certificate for domain '${_PRETTY_DOMAIN}' (${_REMAINING_DAYS} days remaining) will be skipped."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function printChallengeAliasOption() {
|
function printFullDomainFolder() {
|
||||||
local _CHALLENGE_ALIAS_FILE _DOMAIN _MODE _RESULT_CERTS
|
local _DOMAIN _DOMAIN_FOLDER _RESULT_CERTS
|
||||||
_RESULT_CERTS="${RESULT_CERTS:?"printChallengeAliasOption(): Missing global parameter RESULT_CERTS"}"
|
_RESULT_CERTS="${RESULT_CERTS:?"printFullDomainFolder(): Missing global parameter RESULT_CERTS"}"
|
||||||
_MODE="${1:?"printChallengeAliasOption(): Missing first parameter MODE"}"
|
_DOMAIN="${1:?"printFullDomainFolder(): Missing first parameter DOMAIN"}"
|
||||||
_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
|
# cut front '*.' or '_.' and add '_.' again
|
||||||
[ "${_MODE}" = "dnsWithAlias" ] \
|
isWildcardCertificate "${_DOMAIN}" \
|
||||||
&& echo "${ACME_CHALLENGE_ALIAS:?"printChallengeAliasOption(): Missing global parameter ACME_CHALLENGE_ALIAS"}" > "${_CHALLENGE_ALIAS_FILE}" \
|
&& _DOMAIN="${_DOMAIN#\*.}" \
|
||||||
&& echo " --challenge-alias ${ACME_CHALLENGE_ALIAS}" \
|
&& _DOMAIN="_.${_DOMAIN#_.}"
|
||||||
&& return 0
|
|
||||||
|
|
||||||
# read stored challenge-alias and print CHALLENGE_ALIAS_OPTION
|
_DOMAIN_FOLDER="${_RESULT_CERTS}${_DOMAIN}/"
|
||||||
[ "${_MODE}" = "dns" ] \
|
readonly _DOMAIN _DOMAIN_FOLDER _RESULT_CERTS
|
||||||
&& [ -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"}"
|
|
||||||
|
|
||||||
_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
|
|
||||||
|
|
||||||
echo "${_DOMAIN_FOLDER}" \
|
echo "${_DOMAIN_FOLDER}" \
|
||||||
&& return 0
|
&& return 0
|
||||||
@@ -214,14 +210,71 @@ function printDomainFolder() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareFullDomainFolder() {
|
||||||
|
local _DOMAIN _DOMAIN_FOLDER
|
||||||
|
_DOMAIN="${1:?"prepareFullDomainFolder(): Missing first parameter DOMAIN"}"
|
||||||
|
_DOMAIN_FOLDER="$(printFullDomainFolder "${_DOMAIN}")"
|
||||||
|
readonly _DOMAIN _DOMAIN_FOLDER
|
||||||
|
|
||||||
|
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||||
|
&& return 0
|
||||||
|
|
||||||
|
# create folder for results
|
||||||
|
echo -n "Creating folder '${_DOMAIN_FOLDER}'... " \
|
||||||
|
&& mkdir -p "${_DOMAIN_FOLDER}" \
|
||||||
|
&& echo "Done"
|
||||||
|
|
||||||
|
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||||
|
&& return 0
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareAndCheckAliasDomain() {
|
||||||
|
local _ALIAS_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN _DOMAIN_FOLDER _TRIMMED_DOMAIN
|
||||||
|
_DOMAIN="${1:?"prepareAndCheckAliasDomain(): Missing first parameter DOMAIN"}"
|
||||||
|
_ALIAS_DOMAIN="${2:?"prepareAndCheckAliasDomain(): Missing second parameter ALIAS_DOMAIN"}"
|
||||||
|
_DOMAIN_FOLDER="$(printFullDomainFolder "${_DOMAIN}")"
|
||||||
|
_CHALLENGE_ALIAS_DOMAIN_FILE="${_DOMAIN_FOLDER}challenge-alias-domain"
|
||||||
|
|
||||||
|
# cut front '_.' or '*.'
|
||||||
|
_TRIMMED_DOMAIN="${_DOMAIN#_.}" \
|
||||||
|
&& _TRIMMED_DOMAIN="${_TRIMMED_DOMAIN#\*.}"
|
||||||
|
readonly _ALIAS_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN _DOMAIN_FOLDER _TRIMMED_DOMAIN
|
||||||
|
|
||||||
|
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||||
|
&& [ "$(dig +short _acme-challenge.${_TRIMMED_DOMAIN} CNAME)" == "_acme-challenge.${_ALIAS_DOMAIN}." ] \
|
||||||
|
&& echo "${_ALIAS_DOMAIN}" > "${_CHALLENGE_ALIAS_DOMAIN_FILE}" \
|
||||||
|
&& echo "SUCCESS: alias domain '${_ALIAS_DOMAIN}' is used when issuing certificates for '${_TRIMMED_DOMAIN}' via DNS." \
|
||||||
|
&& return 0
|
||||||
|
|
||||||
|
echo "FAILED: unable to use alias domain '${_ALIAS_DOMAIN}' to issue certificates for '${_TRIMMED_DOMAIN}'."
|
||||||
|
echo " You have to configure your domain '${_TRIMMED_DOMAIN}' first before you can use the alias domain as proof."
|
||||||
|
echo " So check if there is a CNAME entry '_acme-challenge.${_TRIMMED_DOMAIN}' pointing to:"
|
||||||
|
echo " - '_acme-challenge.${_ALIAS_DOMAIN}'"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
function single() {
|
function single() {
|
||||||
local _ACME_FILE _DOMAIN _MODE _RESULT_CERTS
|
local _ACME_FILE _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN _DOMAIN_FOLDER _MODE _RESULT_CERTS
|
||||||
_RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}"
|
_RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}"
|
||||||
_ACME_FILE="${ACME_FILE:?"single(): Missing global parameter ACME_FILE"}"
|
_ACME_FILE="${ACME_FILE:?"single(): Missing global parameter ACME_FILE"}"
|
||||||
|
|
||||||
_MODE="${1:?"single(): Missing first parameter MODE"}"
|
_MODE="${1:?"single(): Missing first parameter MODE"}"
|
||||||
_DOMAIN="${2:?"single(): Missing second parameter DOMAIN"}"
|
_DOMAIN="${2:?"single(): Missing second parameter DOMAIN"}"
|
||||||
readonly _ACME_FILE _DOMAIN _MODE _RESULT_CERTS
|
_DOMAIN_FOLDER="$(printFullDomainFolder "${_DOMAIN}")"
|
||||||
|
_CHALLENGE_ALIAS_DOMAIN_FILE="${_DOMAIN_FOLDER}challenge-alias-domain"
|
||||||
|
readonly _ACME_FILE _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN _DOMAIN_FOLDER _MODE _RESULT_CERTS
|
||||||
|
|
||||||
|
local _PRETTY_DOMAIN _TRIMMED_DOMAIN
|
||||||
|
# cut front '_.' or '*.'
|
||||||
|
_TRIMMED_DOMAIN="${_DOMAIN#_.}" \
|
||||||
|
&& _TRIMMED_DOMAIN="${_TRIMMED_DOMAIN#\*.}"
|
||||||
|
|
||||||
|
_PRETTY_DOMAIN="${_TRIMMED_DOMAIN}"
|
||||||
|
isWildcardCertificate "${_DOMAIN}" \
|
||||||
|
&& _PRETTY_DOMAIN="*.${_TRIMMED_DOMAIN}"
|
||||||
|
readonly _PRETTY_DOMAIN _TRIMMED_DOMAIN
|
||||||
|
|
||||||
! [ -f "${_ACME_FILE}" ] \
|
! [ -f "${_ACME_FILE}" ] \
|
||||||
&& echo "Program 'acme.sh' seams not to be installed. Try run 'renewCerts.sh --setup'." \
|
&& echo "Program 'acme.sh' seams not to be installed. Try run 'renewCerts.sh --setup'." \
|
||||||
@@ -231,52 +284,49 @@ function single(){
|
|||||||
! checkConfigViaHttp "${_MODE}" "${_DOMAIN}" \
|
! checkConfigViaHttp "${_MODE}" "${_DOMAIN}" \
|
||||||
&& return 1
|
&& return 1
|
||||||
|
|
||||||
local _DOMAIN_FOLDER
|
# cancel if folder is not prepared
|
||||||
_DOMAIN_FOLDER="$(printDomainFolder "${_MODE}" "${_DOMAIN}")"
|
|
||||||
readonly _DOMAIN_FOLDER
|
|
||||||
|
|
||||||
# create folder for results
|
|
||||||
! [ -d "${_DOMAIN_FOLDER}" ] \
|
! [ -d "${_DOMAIN_FOLDER}" ] \
|
||||||
&& echo -n "Creating folder '${_DOMAIN_FOLDER}'... " \
|
&& echo "Certificate of domain '${_PRETTY_DOMAIN}' skipped because of missing folder:" \
|
||||||
&& mkdir -p "${_DOMAIN_FOLDER}" \
|
&& echo " - '${_DOMAIN_FOLDER}'" \
|
||||||
&& echo "Done"
|
&& return 1
|
||||||
|
|
||||||
# check enddate if third parameter is not --force
|
# check enddate if third parameter is not --force
|
||||||
! isExpiringSoon "${_MODE}" "${_DOMAIN}" "${3:-""}" \
|
! continueIssuingCertificate "${_DOMAIN_FOLDER}fullchain.crt" "${_DOMAIN}" "${3:-""}" \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
# backup the keys
|
# backup the keys
|
||||||
[ -f "${_DOMAIN_FOLDER}/fullchain.crt" ] \
|
[ -f "${_DOMAIN_FOLDER}fullchain.crt" ] \
|
||||||
&& cp "${_DOMAIN_FOLDER}/fullchain.crt" "${_DOMAIN_FOLDER}/fullchain.crt.bak"
|
&& cp "${_DOMAIN_FOLDER}fullchain.crt" "${_DOMAIN_FOLDER}fullchain.crt.bak"
|
||||||
[ -f "${_DOMAIN_FOLDER}/private.key" ] \
|
[ -f "${_DOMAIN_FOLDER}private.key" ] \
|
||||||
&& cp --preserve=mode,ownership "${_DOMAIN_FOLDER}/private.key" "${_DOMAIN_FOLDER}/private.key.bak"
|
&& cp --preserve=mode,ownership "${_DOMAIN_FOLDER}private.key" "${_DOMAIN_FOLDER}private.key.bak"
|
||||||
|
|
||||||
local _OPTIONS
|
local _OPTIONS
|
||||||
# always --force because we check expiring on ourself
|
# always --force because we check expiring on ourself
|
||||||
# _OPTIONS="--issue --force --test"
|
# _OPTIONS="--issue --force --test"
|
||||||
_OPTIONS="--issue --force"
|
_OPTIONS="--issue --force"
|
||||||
[ "${_MODE}" = "dns" ] \
|
if [ "${_MODE}" == "dns" ]; then
|
||||||
&& _OPTIONS="${_OPTIONS}$(printChallengeAliasOption "${_MODE}" "${_DOMAIN}")" \
|
_OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"}"
|
||||||
&& _OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"} --domain *.${_DOMAIN}"
|
[ -f "${_CHALLENGE_ALIAS_DOMAIN_FILE}" ] \
|
||||||
[ "${_MODE}" = "dnsWithAlias" ] \
|
&& _OPTIONS="${_OPTIONS} --challenge-alias $(cat "${_CHALLENGE_ALIAS_DOMAIN_FILE}")"
|
||||||
&& _OPTIONS="${_OPTIONS}$(printChallengeAliasOption "${_MODE}" "${_DOMAIN}")" \
|
isWildcardCertificate "${_DOMAIN}" \
|
||||||
&& _OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"} --domain *.${_DOMAIN}"
|
&& _OPTIONS="${_OPTIONS} --domain ${_PRETTY_DOMAIN}"
|
||||||
[ "${_MODE}" = "http" ] \
|
elif [ "${_MODE}" == "http" ]; then
|
||||||
&& _OPTIONS="${_OPTIONS} --webroot /var/www/letsencrypt"
|
_OPTIONS="${_OPTIONS} --webroot /var/www/letsencrypt"
|
||||||
|
fi
|
||||||
readonly _OPTIONS
|
readonly _OPTIONS
|
||||||
|
|
||||||
${_ACME_FILE} ${_OPTIONS} \
|
${_ACME_FILE} ${_OPTIONS} \
|
||||||
--domain "${_DOMAIN}" \
|
--domain "${_TRIMMED_DOMAIN}" \
|
||||||
--server "letsencrypt" \
|
--server "letsencrypt" \
|
||||||
--keylength "ec-384" \
|
--keylength "ec-384" \
|
||||||
--fullchain-file "${_DOMAIN_FOLDER}/fullchain.crt" \
|
--fullchain-file "${_DOMAIN_FOLDER}fullchain.crt" \
|
||||||
--key-file "${_DOMAIN_FOLDER}/private.key" \
|
--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: \
|
&& 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." \
|
&& echo "Certificate of domain '${_PRETTY_DOMAIN}' was updated." \
|
||||||
&& tryGitPush "${_DOMAIN}" \
|
&& tryGitPush "${_PRETTY_DOMAIN}" \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
echo "Certificate of domain '${_DOMAIN}' remains unchanged."
|
echo "Certificate of domain '${_PRETTY_DOMAIN}' remains unchanged."
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,11 +372,11 @@ function setup(){
|
|||||||
isInstalled \
|
isInstalled \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
! [ $(id -u) = 0 ] \
|
! [ $(id -u) == 0 ] \
|
||||||
&& echo "Setup requires execution as user 'root'." \
|
&& echo "Setup requires execution as user 'root'." \
|
||||||
&& exit 1
|
&& exit 1
|
||||||
|
|
||||||
! [ "$(echo $HOME)" = "/root" ] \
|
! [ "$(echo $HOME)" == "/root" ] \
|
||||||
&& echo "The setup is executed with 'root' privileges but not in the 'root' user environment." \
|
&& echo "The setup is executed with 'root' privileges but not in the 'root' user environment." \
|
||||||
&& exit 1
|
&& exit 1
|
||||||
|
|
||||||
@@ -353,12 +403,14 @@ function setup(){
|
|||||||
function usage() {
|
function usage() {
|
||||||
echo
|
echo
|
||||||
echo 'Commands:'
|
echo 'Commands:'
|
||||||
echo ' (--dns|--http) --own [--force] : Iterates all domains found in RESULT_CERTS.'
|
echo ' --prepare DOMAIN --usingAlias ALIAS-DOMAIN : Prepares a domain to issue certificate using an alias domain in DNS mode.'
|
||||||
echo
|
echo ' See: https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode'
|
||||||
echo ' --dns --single DOMAIN [--force] : Issues a certificate for the given domain using DNS mode.'
|
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 ' --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 ' --http --single DOMAIN [--force] : Issues a certificate for the given domain using HTTP mode.'
|
||||||
echo
|
echo
|
||||||
|
echo ' (--dns|--http) --own [--force] : Iterates all domains found in RESULT_CERTS.'
|
||||||
|
echo
|
||||||
echo 'Current environment:'
|
echo 'Current environment:'
|
||||||
echo " Full name of this script: OWN_FULLNAME='${OWN_FULLNAME}'"
|
echo " Full name of this script: OWN_FULLNAME='${OWN_FULLNAME}'"
|
||||||
echo " Configuration:"
|
echo " Configuration:"
|
||||||
@@ -366,7 +418,6 @@ function usage(){
|
|||||||
echo " Tar file containing the setup of 'acme.sh': ACME_TAR_FILE='${ACME_TAR_FILE}'"
|
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 " 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 " 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 " Output:"
|
||||||
echo " Path were the issued certificate are saved: RESULT_CERTS='${RESULT_CERTS}'"
|
echo " Path were the issued certificate are saved: RESULT_CERTS='${RESULT_CERTS}'"
|
||||||
|
|
||||||
@@ -379,59 +430,78 @@ function main(){
|
|||||||
|
|
||||||
[ -f "/autoACME.env" ] \
|
[ -f "/autoACME.env" ] \
|
||||||
&& source "/autoACME.env" \
|
&& source "/autoACME.env" \
|
||||||
&& echo "Environment '/autoACME.env' loaded."
|
&& echo "[$(date)] Environment '/autoACME.env' loaded."
|
||||||
|
|
||||||
local ACME_CHALLENGE_ALIAS ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS
|
local ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS
|
||||||
OWN_FULLNAME="$(readlink -e ${0})"
|
OWN_FULLNAME="$(readlink -e ${0})"
|
||||||
ACME_CHALLENGE_ALIAS="${AUTOACME_CHALLENGE_ALIAS:-""}"
|
|
||||||
ACME_FILE="/root/.acme.sh/acme.sh"
|
ACME_FILE="/root/.acme.sh/acme.sh"
|
||||||
ACME_VERSION="acme.sh-3.1.1"
|
ACME_VERSION="acme.sh-3.1.1"
|
||||||
ACME_SETUP_FILE="/tmp/acme.sh-setup/${ACME_VERSION}/acme.sh"
|
ACME_SETUP_FILE="/tmp/acme.sh-setup/${ACME_VERSION}/acme.sh"
|
||||||
ACME_TAR_FILE="${OWN_FULLNAME%/*}/${ACME_VERSION}.tar.gz"
|
ACME_TAR_FILE="${OWN_FULLNAME%/*}/${ACME_VERSION}.tar.gz"
|
||||||
RESULT_CERTS="${AUTOACME_RESULT_CERTS%/}" #Removes shortest matching pattern '/' from the end
|
RESULT_CERTS="${AUTOACME_RESULT_CERTS%/}" #Removes shortest matching pattern '/' from the end
|
||||||
RESULT_CERTS="${RESULT_CERTS:-"/etc/nginx/ssl"}/"
|
RESULT_CERTS="${RESULT_CERTS:-"/etc/nginx/ssl"}/"
|
||||||
readonly ACME_CHALLENGE_ALIAS ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS
|
readonly ACME_FILE ACME_VERSION ACME_SETUP_FILE ACME_TAR_FILE OWN_FULLNAME RESULT_CERTS
|
||||||
|
|
||||||
local REPOSITORY_URL
|
local REPOSITORY_URL
|
||||||
isGitRepository \
|
isGitRepository \
|
||||||
&& REPOSITORY_URL="$(git -C ${RESULT_CERTS} config --get remote.origin.url)"
|
&& REPOSITORY_URL="$(git -C ${RESULT_CERTS} config --get remote.origin.url)"
|
||||||
readonly REPOSITORY_URL
|
readonly REPOSITORY_URL
|
||||||
|
|
||||||
case "${1}${2}" in
|
case "${1}" in
|
||||||
--dns--own)
|
--dns)
|
||||||
echo "Renewing own certificates at $(date +%F_%T) via DNS:"
|
case "${2}" in
|
||||||
|
--single)
|
||||||
|
echo "[$(date)] Issue single certificate '${3}' via DNS:" \
|
||||||
|
&& prepareFullDomainFolder "${3}" \
|
||||||
|
&& single "dns" "${3}" "${4}" \
|
||||||
|
&& return 0
|
||||||
|
;;
|
||||||
|
--own)
|
||||||
|
echo "[$(date)] Renewing own certificates via DNS:"
|
||||||
own "dns" "${3}" \
|
own "dns" "${3}" \
|
||||||
&& echo "Finished successfully." \
|
&& echo "Finished successfully." \
|
||||||
&& return 0
|
&& return 0
|
||||||
;;
|
;;
|
||||||
--http--own)
|
esac
|
||||||
echo "Renewing own certificates at $(date +%F_%T) via HTTP:"
|
;;
|
||||||
own "http" "${3}" \
|
--http)
|
||||||
|
case "${2}" in
|
||||||
|
--single)
|
||||||
|
echo "[$(date)] Issue single certificate '${3}' via HTTP:" \
|
||||||
|
&& prepareFullDomainFolder "${3}" \
|
||||||
|
&& single "http" "${3}" "${4}" \
|
||||||
&& echo \
|
&& echo \
|
||||||
&& echo "Checking configuration of nginx and restart the webserver:" \
|
&& echo "Checking configuration of nginx and restart the webserver:" \
|
||||||
&& echo "==========================================================" \
|
&& echo "==========================================================" \
|
||||||
&& nginx -t && systemctl reload nginx \
|
&& nginx -t && systemctl reload nginx \
|
||||||
&& return 0
|
&& return 0
|
||||||
;;
|
;;
|
||||||
--dns--single)
|
--own)
|
||||||
echo "Issue single certificate '${3}' at $(date +%F_%T) via DNS:"
|
echo "[$(date)] Renewing own certificates via HTTP:" \
|
||||||
single "dns" "${3}" "${4}" \
|
&& own "http" "${3}" \
|
||||||
&& 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}" \
|
|
||||||
&& echo \
|
&& echo \
|
||||||
&& echo "Checking configuration of nginx and restart the webserver:" \
|
&& echo "Checking configuration of nginx and restart the webserver:" \
|
||||||
&& echo "==========================================================" \
|
&& echo "==========================================================" \
|
||||||
&& nginx -t && systemctl reload nginx \
|
&& nginx -t && systemctl reload nginx \
|
||||||
&& return 0
|
&& return 0
|
||||||
;;
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
--prepare)
|
||||||
|
case "${3}" in
|
||||||
|
--usingAlias)
|
||||||
|
echo "[$(date)] Prepare domain '${2}' using the alias-domain '${4}' via DNS:" \
|
||||||
|
&& prepareFullDomainFolder "${2}" \
|
||||||
|
&& prepareAndCheckAliasDomain "${2}" "${4}" \
|
||||||
|
&& return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown command '${1}' '${2}' '${3}' '${4}'"
|
||||||
|
usage
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
--setup)
|
--setup)
|
||||||
setup \
|
setup \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ function createEnvironmentFile() {
|
|||||||
&& echo "SUCCESS: there values were exported into file: '${_ENVIRONMENT_FILE}'" \
|
&& echo "SUCCESS: there values were exported into file: '${_ENVIRONMENT_FILE}'" \
|
||||||
&& echo " - AUTOACME_CONTAINER_HOSTNAME: ${AUTOACME_CONTAINER_HOSTNAME}" \
|
&& echo " - AUTOACME_CONTAINER_HOSTNAME: ${AUTOACME_CONTAINER_HOSTNAME}" \
|
||||||
&& echo " - AUTOACME_DNS_PROVIDER: ${AUTOACME_DNS_PROVIDER}" \
|
&& 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_GIT_REPOSITORY_VIA_SSH: ${AUTOACME_GIT_REPOSITORY_VIA_SSH}" \
|
||||||
&& echo " - AUTOACME_PATH_IN_GIT_REPOSITORY: ${AUTOACME_PATH_IN_GIT_REPOSITORY}"
|
&& echo " - AUTOACME_PATH_IN_GIT_REPOSITORY: ${AUTOACME_PATH_IN_GIT_REPOSITORY}"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user