From 48b40d401f7fd1420eb57e139fbf6daa9075a540 Mon Sep 17 00:00:00 2001 From: Nicolas Duchon Date: Mon, 15 Jul 2024 22:48:12 +0200 Subject: [PATCH] feat: support for DNS-01 challenge w/ acme.sh DNS API Co-authored-by: Nicolas Duchon Co-authored-by: David Michaluk --- app/letsencrypt_service | 48 +++++++++++++++++++++++++++++-- app/letsencrypt_service_data.tmpl | 18 ++++++++++++ app/start.sh | 4 ++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/app/letsencrypt_service b/app/letsencrypt_service index ccca6a2..eb69196 100755 --- a/app/letsencrypt_service +++ b/app/letsencrypt_service @@ -151,7 +151,41 @@ function update_cert { # CLI parameters array used for --issue local -a params_issue_arr - params_issue_arr+=(--webroot /usr/share/nginx/html) + + # ACME challenge type + local -n acme_challenge="ACME_${cid}_CHALLENGE" + if [[ -z "${acme_challenge}" ]]; then + acme_challenge="${ACME_CHALLENGE:-HTTP-01}" + fi + + if [[ "$acme_challenge" == "HTTP-01" ]]; then + # HTTP-01 challenge + params_issue_arr+=(--webroot /usr/share/nginx/html) + elif [[ "$acme_challenge" == "DNS-01" ]]; then + # DNS-01 challenge + local -n acmesh_dns_config="ACMESH_${cid}_DNS_API_CONFIG" + + local acmesh_dns_api="${acmesh_dns_config[DNS_API]}" + if [[ -z "$acmesh_dns_api" ]]; then + echo "Error: missing acme.sh DNS API for DNS challenge" + return 1 + fi + params_issue_arr+=(--dns "$acmesh_dns_api") + + # Loop over defined variable for acme.sh DNS api config + local -a dns_api_keys + for key in "${!acmesh_dns_config[@]}"; do + [[ "$key" == "DNS_API" ]] && continue + dns_api_keys+=("$key") + local value="${acmesh_dns_config[$key]}" + declare -x "$key"="$value" + done + + echo "Info: DNS challenge using $acmesh_dns_api DNS API with the following keys: ${dns_api_keys[*]}" + else + echo "Error: unknown ACME challenge method: $acme_challenge" + return 1 + fi local -n cert_keysize="LETSENCRYPT_${cid}_KEYSIZE" if [[ -z "$cert_keysize" ]] || \ @@ -349,7 +383,7 @@ function update_cert { # Add all the domains to certificate params_issue_arr+=(--domain "$domain") # If enabled, add location configuration for the domain - if parse_true "${ACME_HTTP_CHALLENGE_LOCATION:=false}"; then + if [[ "$acme_challenge" == "HTTP-01" ]] && parse_true "${ACME_HTTP_CHALLENGE_LOCATION:=false}"; then add_location_configuration "$domain" || reload_nginx fi done @@ -361,6 +395,16 @@ function update_cert { local acmesh_return=$? + # DNS challenge: clean environment variables + if [[ "$acme_challenge" == "DNS-01" ]]; then + local -n acmesh_dns_config="ACMESH_${cid}_DNS_API_CONFIG" + # Loop over defined variable for acme.sh DNS api config + for key in "${!acmesh_dns_config[@]}"; do + [[ "$key" == "DNS_API" ]] && continue + unset "$key" + done + fi + # 0 = success, 2 = RENEW_SKIP if [[ $acmesh_return == 0 || $acmesh_return == 2 ]]; then for domain in "${hosts_array[@]}"; do diff --git a/app/letsencrypt_service_data.tmpl b/app/letsencrypt_service_data.tmpl index 4a66960..17c0407 100644 --- a/app/letsencrypt_service_data.tmpl +++ b/app/letsencrypt_service_data.tmpl @@ -26,6 +26,8 @@ LETSENCRYPT_CONTAINERS=( {{ $STAGING := trim (coalesce $container.Env.LETSENCRYPT_TEST "") }} {{ $EMAIL := trim (coalesce $container.Env.LETSENCRYPT_EMAIL "") }} {{ $CA_URI := trim (coalesce $container.Env.ACME_CA_URI "") }} + {{ $ACME_CHALLENGE := trim (coalesce $container.Env.ACME_CHALLENGE "") }} + {{ $ACMESH_DNS_API_CONFIG := fromYaml (coalesce $container.Env.ACMESH_DNS_API_CONFIG "") }} {{ $PREFERRED_CHAIN := trim (coalesce $container.Env.ACME_PREFERRED_CHAIN "") }} {{ $OCSP := trim (coalesce $container.Env.ACME_OCSP "") }} {{ $EAB_KID := trim (coalesce $container.Env.ACME_EAB_KID "") }} @@ -47,6 +49,14 @@ LETSENCRYPT_CONTAINERS=( {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_TEST="{{ $STAGING }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_EMAIL="{{ $EMAIL }}" {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_CA_URI="{{ $CA_URI }}" + {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_CHALLENGE="{{ $ACME_CHALLENGE }}" + {{- if $ACMESH_DNS_API_CONFIG }} + {{- "\n" }}declare -A ACMESH_{{ $cid }}_{{ $hostHash }}_DNS_API_CONFIG=( + {{- range $key, $value := $ACMESH_DNS_API_CONFIG }} + {{- "\n\t" }}['{{ $key }}']='{{ $value }}' + {{- end }} + {{- "\n" }}) + {{- end }} {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}" {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_OCSP="{{ $OCSP }}" {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_EAB_KID="{{ $EAB_KID }}" @@ -69,6 +79,14 @@ LETSENCRYPT_CONTAINERS=( {{- "\n" }}LETSENCRYPT_{{ $cid }}_TEST="{{ $STAGING }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_EMAIL="{{ $EMAIL }}" {{- "\n" }}ACME_{{ $cid }}_CA_URI="{{ $CA_URI }}" + {{- "\n" }}ACME_{{ $cid }}_CHALLENGE="{{ $ACME_CHALLENGE }}" + {{- if $ACMESH_DNS_API_CONFIG }} + {{- "\n" }}declare -A ACMESH_{{ $cid }}_DNS_API_CONFIG=( + {{- range $key, $value := $ACMESH_DNS_API_CONFIG }} + {{- "\n\t" }}['{{ $key }}']='{{ $value }}' + {{- end }} + {{- "\n" }}) + {{- end }} {{- "\n" }}ACME_{{ $cid }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}" {{- "\n" }}ACME_{{ $cid }}_OCSP="{{ $OCSP }}" {{- "\n" }}ACME_{{ $cid }}_EAB_KID="{{ $EAB_KID }}" diff --git a/app/start.sh b/app/start.sh index 94db50f..3ce8737 100755 --- a/app/start.sh +++ b/app/start.sh @@ -7,7 +7,9 @@ term_handler() { # shellcheck source=functions.sh source /app/functions.sh - remove_all_location_configurations + if parse_true "${ACME_HTTP_CHALLENGE_LOCATION:=false}"; then + remove_all_location_configurations + fi remove_all_standalone_configurations exit 0