Skip to content

Support for DNS challenge #1022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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).
25 changes: 25 additions & 0 deletions app/letsencrypt_service
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions app/letsencrypt_service_data.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 "") }}
Expand All @@ -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 }}"
Expand All @@ -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 }}"
Expand Down