diff --git a/README.md b/README.md index ff1bc12f..6cc98094 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,13 @@ It handles the automated creation, renewal and use of SSL certificates for proxi * Support creation of [Multi-Domain (SAN) Certificates](https://github.com/nginx-proxy/acme-companion/blob/main/docs/Let's-Encrypt-and-ACME.md#multi-domains-certificates). * Creation of a strong [RFC7919 Diffie-Hellman Group](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A) at startup. * Work with all versions of docker. +* Support DNS challenges (the list of supported providers is available here: https://github.com/acmesh-official/acme.sh/tree/master/dnsapi) ### Requirements: * Your host **must** be publicly reachable on **both** port [`80`](https://letsencrypt.org/docs/allow-port-80/) and [`443`](https://github.com/nginx-proxy/acme-companion/discussions/873#discussioncomment-1410225). * Check your firewall rules and [**do not attempt to block port `80`**](https://letsencrypt.org/docs/allow-port-80/) as that will prevent `http-01` challenges from completing. * For the same reason, you can't use nginx-proxy's [`HTTPS_METHOD=nohttp`](https://github.com/nginx-proxy/nginx-proxy#how-ssl-support-works). +* If for any reason, either the port `80` or `443` are not publicly reachable (firewall, ...), please use dns challenge (see below). * The (sub)domains you want to issue certificates for must correctly resolve to the host. * Your DNS provider must [answer correctly to CAA record requests](https://letsencrypt.org/docs/caa/). * If your (sub)domains have AAAA records set, the host must be publicly reachable over IPv6 on port `80` and `443`. @@ -112,6 +114,38 @@ $ docker run --detach \ Repeat [Step 3](#step-3---proxied-containers) for any other container you want to proxy. +## DNS challenge +If you want to use the DNS challenge, you have to add the following environment variable to your proxied container as following : + +For our example, we want to setup the DNS challenge using the provider OVH. + +### Check if your provider is supported by acme.sh +Go to https://github.com/acmesh-official/acme.sh/tree/master/dnsapi and search for your provider. +In our example, we are searching for `ovh` and we see that this provider is supported (`dns_ovh.sh` file). +Then, we use the name of that file without the extension as the value for the `LETSENCRYPT_DNS_CHALLENGE_API` environment variable for our proxied container, i.e. : `LETSENCRYPT_DNS_CHALLENGE_API=dns_ovh` + +### Add additional credentials for your provider +Refer to the content of the file your found in the previous step (`dns_ovh.sh` in our example) and note the variable(s) required by the provider. + +In our example, our provider needs at least the following credentials : `OVH_AK`, `OVH_AS`, `OVH_CK`, `OVH_END_POINT` as stated in the `dns_ovh.sh` file. +Then, we add the following environment variable to our proxied container `LETSENCRYPT_DNS_CHALLENGE_ENV=OVH_AK, OVH_AS, OVH_CK, OVH_END_POINT` which is the list of variable (seperated by `,`) related to our provider that should be passed to `acme.sh`. + +Finally, we define all of these environment variables inside our proxied container as following : + +```shell +$ docker run --detach \ + --name your-proxied-app \ + --env "VIRTUAL_HOST=subdomain.yourdomain.tld" \ + --env "LETSENCRYPT_HOST=subdomain.yourdomain.tld" \ + --env "LETSENCRYPT_DNS_CHALLENGE_API=dns_ovh" \ + --env "LETSENCRYPT_DNS_CHALLENGE_ENV=OVH_AK, OVH_AS, OVH_CK, OVH_END_POINT" \ + --env "OVH_AK=appKey" \ + --env "OVH_AS=appSecret" \ + --env "OVH_CK=consumerKey" \ + --env "OVH_END_POINT=consumerKey" \ + nginx +``` + ## Additional documentation Please check the [docs section](https://github.com/nginx-proxy/acme-companion/tree/main/docs). diff --git a/app/letsencrypt_service b/app/letsencrypt_service index ea9c4864..0aa3cb34 100755 --- a/app/letsencrypt_service +++ b/app/letsencrypt_service @@ -151,6 +151,21 @@ function update_cert { local -a params_issue_arr params_issue_arr+=(--webroot /usr/share/nginx/html) + # DNS challenge + local -n dns_challenge_api="LETSENCRYPT_${cid}_DNS_CHALLENGE_API" + local -n dns_challenge_env="LETSENCRYPT_${cid}_DNS_CHALLENGE_ENV" + if [[ -z "$dns_challenge_api" ]]; then + : + else + echo "DNS challenge using API: $dns_challenge_api" + params_base_arr+=(--dns "$dns_challenge_api") + # Loop over defined variable for DNS challenge + for var_name in ${dns_challenge_env//,/ } ; do + local -n var_value="LETSENCRYPT_${cid}_DNS_CHALLENGE_VAR_${var_name}" + declare -x $var_name=$var_value + done + fi + local -n cert_keysize="LETSENCRYPT_${cid}_KEYSIZE" if [[ -z "$cert_keysize" ]] || \ [[ ! "$cert_keysize" =~ ^(2048|3072|4096|ec-256|ec-384)$ ]]; then @@ -354,6 +369,16 @@ function update_cert { local acmesh_return=$? + # DNS challenge : clean environment variables + if [[ -z "$dns_challenge_api" ]]; then + : + else + # Loop over defined variable for DNS challenge + for var_name in ${dns_challenge_env//,/ } ; do + unset $var_name + 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 b7077ff2..14a9947b 100644 --- a/app/letsencrypt_service_data.tmpl +++ b/app/letsencrypt_service_data.tmpl @@ -24,6 +24,8 @@ LETSENCRYPT_CONTAINERS=( {{ $KEYSIZE := trim (coalesce $container.Env.LETSENCRYPT_KEYSIZE "") }} {{ $STAGING := trim (coalesce $container.Env.LETSENCRYPT_TEST "") }} {{ $EMAIL := trim (coalesce $container.Env.LETSENCRYPT_EMAIL "") }} + {{ $DNS_CHALLENGE_API := trim (coalesce $container.Env.LETSENCRYPT_DNS_CHALLENGE_API "") }} + {{ $DNS_CHALLENGE_ENV := trim (coalesce $container.Env.LETSENCRYPT_DNS_CHALLENGE_ENV "") }} {{ $CA_URI := trim (coalesce $container.Env.ACME_CA_URI "") }} {{ $PREFERRED_CHAIN := trim (coalesce $container.Env.ACME_PREFERRED_CHAIN "") }} {{ $OCSP := trim (coalesce $container.Env.ACME_OCSP "") }} @@ -44,6 +46,14 @@ LETSENCRYPT_CONTAINERS=( {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_KEYSIZE="{{ $KEYSIZE }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_TEST="{{ $STAGING }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_EMAIL="{{ $EMAIL }}" + {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_DNS_CHALLENGE_API="{{ $DNS_CHALLENGE_API }}" + {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_DNS_CHALLENGE_ENV="{{ $DNS_CHALLENGE_ENV }}" + {{/* Forwarding env variables for DNS API challenge */}} + {{ range $varName := split $DNS_CHALLENGE_ENV "," }} + {{ $varName := trim $varName }} + {{ $varValue := trim (coalesce (index $container.Env $varName) "") }} + {{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_DNS_CHALLENGE_VAR_{{ $varName }}="{{ $varValue }}" + {{ end }} {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_CA_URI="{{ $CA_URI }}" {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}" {{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_OCSP="{{ $OCSP }}" @@ -66,6 +76,14 @@ LETSENCRYPT_CONTAINERS=( {{- "\n" }}LETSENCRYPT_{{ $cid }}_KEYSIZE="{{ $KEYSIZE }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_TEST="{{ $STAGING }}" {{- "\n" }}LETSENCRYPT_{{ $cid }}_EMAIL="{{ $EMAIL }}" + {{- "\n" }}LETSENCRYPT_{{ $cid }}_DNS_CHALLENGE_API="{{ $DNS_CHALLENGE_API }}" + {{- "\n" }}LETSENCRYPT_{{ $cid }}_DNS_CHALLENGE_ENV="{{ $DNS_CHALLENGE_ENV }}" + {{/* Forwarding env variables for DNS API challenge */}} + {{ range $varName := split $DNS_CHALLENGE_ENV "," }} + {{ $varName := trim $varName }} + {{ $varValue := trim (coalesce (index $container.Env $varName) "") }} + {{- "\n" }}LETSENCRYPT_{{ $cid }}_DNS_CHALLENGE_VAR_{{ $varName }}="{{ $varValue }}" + {{ end }} {{- "\n" }}ACME_{{ $cid }}_CA_URI="{{ $CA_URI }}" {{- "\n" }}ACME_{{ $cid }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}" {{- "\n" }}ACME_{{ $cid }}_OCSP="{{ $OCSP }}"