Skip to content

Commit 9772acc

Browse files
buchdagpini-gh
andcommitted
feat: wildcard certificates support
Co-authored-by: Nicolas Duchon <[email protected]> Co-authored-by: Gilles Filippini <[email protected]>
1 parent 712dd94 commit 9772acc

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ It handles the automated creation, renewal and use of SSL certificates for proxi
1313
* Let's Encrypt / ACME domain validation through `HTTP-01` (by default) or [`DNS-01`](https://github.com/nginx-proxy/acme-companion/blob/main/docs/Let's-Encrypt-and-ACME.md#dns-01-acme-challenge) challenge.
1414
* Automated update and reload of nginx config on certificate creation/renewal.
1515
* 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).
16+
* Support creation of [Wildcard Certificates](https://community.letsencrypt.org/t/acme-v2-production-environment-wildcards/55578) (with `DNS-01` challenge only).
1617
* Creation of a strong [RFC7919 Diffie-Hellman Group](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A) at startup.
1718
* Work with all versions of docker.
1819

app/letsencrypt_service

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ RENEW_PRIVATE_KEYS="$(lc "${RENEW_PRIVATE_KEYS:-true}")"
1212
# Backward compatibility environment variable
1313
REUSE_PRIVATE_KEYS="$(lc "${REUSE_PRIVATE_KEYS:-false}")"
1414

15+
function strip_wildcard {
16+
# Remove wildcard prefix if present
17+
# https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#wildcard-certificates
18+
local -r domain="${1?missing domain argument}"
19+
if [[ "${domain:0:2}" == "*." ]]; then
20+
echo "${domain:2}"
21+
else
22+
echo "$domain"
23+
fi
24+
}
25+
1526
function create_link {
1627
local -r source=${1?missing source argument}
1728
local -r target=${2?missing target argument}
@@ -27,7 +38,8 @@ function create_link {
2738

2839
function create_links {
2940
local -r base_domain=${1?missing base_domain argument}
30-
local -r domain=${2?missing base_domain argument}
41+
local domain=${2?missing base_domain argument}
42+
domain="$(strip_wildcard "$domain")"
3143

3244
if [[ ! -f "/etc/nginx/certs/$base_domain/fullchain.pem" || \
3345
! -f "/etc/nginx/certs/$base_domain/key.pem" ]]; then
@@ -75,6 +87,7 @@ function cleanup_links {
7587
for cid in "${LETSENCRYPT_CONTAINERS[@]}"; do
7688
local -n hosts_array="LETSENCRYPT_${cid}_HOST"
7789
for domain in "${hosts_array[@]}"; do
90+
domain="$(strip_wildcard "$domain")"
7891
# Add domain to the array storing currently enabled domains.
7992
ENABLED_DOMAINS+=("$domain")
8093
done
@@ -128,6 +141,11 @@ function update_cert {
128141
# First domain will be our base domain
129142
local base_domain="${hosts_array[0]}"
130143

144+
local wildcard_certificate='false'
145+
if [[ "${base_domain:0:2}" == "*." ]]; then
146+
wildcard_certificate='true'
147+
fi
148+
131149
local should_restart_container='false'
132150

133151
# Base CLI parameters array, used for both --register-account and --issue
@@ -160,6 +178,10 @@ function update_cert {
160178

161179
if [[ "$acme_challenge" == "HTTP-01" ]]; then
162180
# HTTP-01 challenge
181+
if [[ "$wildcard_certificate" == 'true' ]]; then
182+
echo "Error: wildcard certificates (${base_domain}) can't be obtained with HTTP-01 challenge"
183+
return 1
184+
fi
163185
params_issue_arr+=(--webroot /usr/share/nginx/html)
164186
elif [[ "$acme_challenge" == "DNS-01" ]]; then
165187
# DNS-01 challenge
@@ -240,23 +262,28 @@ function update_cert {
240262
local ca_path_dir
241263
ca_path_dir="$(echo "$acme_ca_uri" | cut -d : -f 2- | tr -s / | cut -d / -f 3-)"
242264

243-
local certificate_dir
265+
local relative_certificate_dir
266+
if [[ "$wildcard_certificate" == 'true' ]]; then
267+
relative_certificate_dir="wildcard_${base_domain:2}"
268+
else
269+
relative_certificate_dir="$base_domain"
270+
fi
244271
# If we're going to use one of LE stating endpoints ...
245272
if [[ "$acme_ca_uri" =~ ^https://acme-staging.* ]]; then
246273
# Unset accountemail
247274
# force config dir to 'staging'
248275
unset accountemail
249276
config_home="/etc/acme.sh/staging"
250277
# Prefix test certificate directory with _test_
251-
certificate_dir="/etc/nginx/certs/_test_$base_domain"
252-
else
253-
certificate_dir="/etc/nginx/certs/$base_domain"
278+
relative_certificate_dir="_test_${relative_certificate_dir}"
254279
fi
280+
281+
local absolute_certificate_dir="/etc/nginx/certs/$relative_certificate_dir"
255282
params_issue_arr+=( \
256-
--cert-file "${certificate_dir}/cert.pem" \
257-
--key-file "${certificate_dir}/key.pem" \
258-
--ca-file "${certificate_dir}/chain.pem" \
259-
--fullchain-file "${certificate_dir}/fullchain.pem" \
283+
--cert-file "${absolute_certificate_dir}/cert.pem" \
284+
--key-file "${absolute_certificate_dir}/key.pem" \
285+
--ca-file "${absolute_certificate_dir}/chain.pem" \
286+
--fullchain-file "${absolute_certificate_dir}/fullchain.pem" \
260287
)
261288

262289
[[ ! -d "$config_home" ]] && mkdir -p "$config_home"
@@ -376,8 +403,8 @@ function update_cert {
376403
[[ "${2:-}" == "--force-renew" ]] && params_issue_arr+=(--force)
377404

378405
# Create directory for the first domain
379-
mkdir -p "$certificate_dir"
380-
set_ownership_and_permissions "$certificate_dir"
406+
mkdir -p "$absolute_certificate_dir"
407+
set_ownership_and_permissions "$absolute_certificate_dir"
381408

382409
for domain in "${hosts_array[@]}"; do
383410
# Add all the domains to certificate
@@ -408,21 +435,15 @@ function update_cert {
408435
# 0 = success, 2 = RENEW_SKIP
409436
if [[ $acmesh_return == 0 || $acmesh_return == 2 ]]; then
410437
for domain in "${hosts_array[@]}"; do
411-
if [[ $acme_ca_uri =~ ^https://acme-staging.* ]]; then
412-
create_links "_test_$base_domain" "$domain" \
413-
&& should_reload_nginx='true' \
414-
&& should_restart_container='true'
415-
else
416-
create_links "$base_domain" "$domain" \
417-
&& should_reload_nginx='true' \
418-
&& should_restart_container='true'
419-
fi
438+
create_links "$relative_certificate_dir" "$domain" \
439+
&& should_reload_nginx='true' \
440+
&& should_restart_container='true'
420441
done
421-
echo "${COMPANION_VERSION:-}" > "${certificate_dir}/.companion"
422-
set_ownership_and_permissions "${certificate_dir}/.companion"
442+
echo "${COMPANION_VERSION:-}" > "${absolute_certificate_dir}/.companion"
443+
set_ownership_and_permissions "${absolute_certificate_dir}/.companion"
423444
# Make private key root readable only
424445
for file in cert.pem key.pem chain.pem fullchain.pem; do
425-
local file_path="${certificate_dir}/${file}"
446+
local file_path="${absolute_certificate_dir}/${file}"
426447
[[ -e "$file_path" ]] && set_ownership_and_permissions "$file_path"
427448
done
428449
local acme_private_key

0 commit comments

Comments
 (0)