Skip to content

Commit 8a936cc

Browse files
committed
Add standalone certificate feature
Standalone certificates are generated from a static user provided configuration file rather than from the dynamicaly generated (from running containers environment variables) letsencrypt_service_data file.
1 parent 7e3d341 commit 8a936cc

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

app/entrypoint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ if [[ "$*" == "/bin/bash /app/start.sh" ]]; then
161161
check_writable_directory '/etc/nginx/certs'
162162
check_writable_directory '/etc/nginx/vhost.d'
163163
check_writable_directory '/usr/share/nginx/html'
164+
[[ -f /app/letsencrypt_user_data ]] && check_writable_directory '/etc/nginx/conf.d'
164165
check_deprecated_env_var
165166
check_default_cert_key
166167
check_dh_group

app/functions.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,39 @@ function add_location_configuration {
102102
fi
103103
}
104104

105+
function add_standalone_configuration {
106+
local domain="${1:?}"
107+
if grep -q $domain "/etc/nginx/conf.d/default.conf"; then
108+
# If the domain is already present in nginx's conf, use the location configuration.
109+
add_location_configuration "$domain"
110+
else
111+
# Else use the standalone configuration.
112+
cat > "/etc/nginx/conf.d/standalone-cert-$domain.conf" << EOF
113+
server {
114+
server_name $domain;
115+
listen 80;
116+
access_log /var/log/nginx/access.log vhost;
117+
location ^~ /.well-known/acme-challenge/ {
118+
auth_basic off;
119+
allow all;
120+
root /usr/share/nginx/html;
121+
try_files \$uri =404;
122+
break;
123+
}
124+
}
125+
EOF
126+
fi
127+
}
128+
129+
function remove_all_standalone_configurations {
130+
local old_shopt_options=$(shopt -p) # Backup shopt options
131+
shopt -s nullglob
132+
for file in "/etc/nginx/conf.d/standalone-cert-"*".conf"; do
133+
rm -f "$file"
134+
done
135+
eval "$old_shopt_options" # Restore shopt options
136+
}
137+
105138
function remove_all_location_configurations {
106139
for file in "${VHOST_DIR}"/*; do
107140
[[ -e "$file" ]] || continue

app/letsencrypt_service

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ function create_links {
5050
}
5151

5252
function cleanup_links {
53+
local -a LETSENCRYPT_CONTAINERS
54+
local -a LETSENCRYPT_STANDALONE_CERTS
5355
local -a ENABLED_DOMAINS
5456
local -a SYMLINKED_DOMAINS
5557
local -a DISABLED_DOMAINS
@@ -65,9 +67,10 @@ function cleanup_links {
6567
[[ "$DEBUG" == true ]] && echo "Symlinked domains: ${SYMLINKED_DOMAINS[*]}"
6668

6769
# Create an array containing domains that are considered
68-
# enabled (ie present on /app/letsencrypt_service_data).
69-
# shellcheck source=/dev/null
70-
source /app/letsencrypt_service_data
70+
# enabled (ie present on /app/letsencrypt_service_data or /app/letsencrypt_user_data).
71+
[[ -f /app/letsencrypt_service_data ]] && source /app/letsencrypt_service_data
72+
[[ -f /app/letsencrypt_user_data ]] && source /app/letsencrypt_user_data
73+
LETSENCRYPT_CONTAINERS+=( "${LETSENCRYPT_STANDALONE_CERTS[@]}" )
7174
for cid in "${LETSENCRYPT_CONTAINERS[@]}"; do
7275
host_varname="LETSENCRYPT_${cid}_HOST"
7376
hosts_array="${host_varname}[@]"
@@ -80,7 +83,7 @@ function cleanup_links {
8083

8184
# Create an array containing only domains for which a symlinked private key exists
8285
# in /etc/nginx/certs but that no longer have a corresponding LETSENCRYPT_HOST set
83-
# on an active container.
86+
# on an active container or on /app/letsencrypt_user_data
8487
if [[ ${#SYMLINKED_DOMAINS[@]} -gt 0 ]]; then
8588
mapfile -t DISABLED_DOMAINS < <(echo "${SYMLINKED_DOMAINS[@]}" \
8689
"${ENABLED_DOMAINS[@]}" \
@@ -120,15 +123,34 @@ function cleanup_links {
120123
}
121124

122125
function update_certs {
126+
local -a LETSENCRYPT_CONTAINERS
127+
local -a LETSENCRYPT_STANDALONE_CERTS
123128

124129
check_nginx_proxy_container_run || return
125130

126-
[[ -f /app/letsencrypt_service_data ]] || return
127-
128131
# Load relevant container settings
129-
unset LETSENCRYPT_CONTAINERS
130-
# shellcheck source=/dev/null
131-
source /app/letsencrypt_service_data
132+
if [[ -f /app/letsencrypt_service_data ]]; then
133+
source /app/letsencrypt_service_data
134+
else
135+
echo "Warning: /app/letsencrypt_service_data not found, skipping data from containers."
136+
fi
137+
138+
# Load settings for standalone certs
139+
if [[ -f /app/letsencrypt_user_data ]]; then
140+
if source /app/letsencrypt_user_data; then
141+
for cid in "${LETSENCRYPT_STANDALONE_CERTS[@]}"; do
142+
host_varname="LETSENCRYPT_${cid}_HOST"
143+
hosts_array="${host_varname}[@]"
144+
for domain in "${!hosts_array}"; do
145+
add_standalone_configuration "$domain"
146+
done
147+
done
148+
reload_nginx
149+
LETSENCRYPT_CONTAINERS+=( "${LETSENCRYPT_STANDALONE_CERTS[@]}" )
150+
else
151+
echo "Warning: could not source /app/letsencrypt_user_data, skipping user data"
152+
fi
153+
fi
132154

133155
should_reload_nginx='false'
134156
for cid in "${LETSENCRYPT_CONTAINERS[@]}"; do
@@ -145,15 +167,15 @@ function update_certs {
145167

146168
# Use container's LETSENCRYPT_EMAIL if set, fallback to DEFAULT_EMAIL
147169
email_varname="LETSENCRYPT_${cid}_EMAIL"
148-
email_address="${!email_varname}"
170+
email_address="${!email_varname:-"<no value>"}"
149171
if [[ "$email_address" != "<no value>" ]]; then
150172
params_d_arr+=(--email "$email_address")
151173
elif [[ -n "${DEFAULT_EMAIL:-}" ]]; then
152174
params_d_arr+=(--email "$DEFAULT_EMAIL")
153175
fi
154176

155177
keysize_varname="LETSENCRYPT_${cid}_KEYSIZE"
156-
cert_keysize="${!keysize_varname}"
178+
cert_keysize="${!keysize_varname:-"<no value>"}"
157179
if [[ "$cert_keysize" == "<no value>" ]]; then
158180
cert_keysize=$DEFAULT_KEY_SIZE
159181
fi
@@ -173,7 +195,7 @@ function update_certs {
173195
fi
174196

175197
account_varname="LETSENCRYPT_${cid}_ACCOUNT_ALIAS"
176-
account_alias="${!account_varname}"
198+
account_alias="${!account_varname:-"<no value>"}"
177199
if [[ "$account_alias" == "<no value>" ]]; then
178200
account_alias=default
179201
fi
@@ -182,7 +204,7 @@ function update_certs {
182204
[[ $REUSE_PRIVATE_KEYS == true ]] && params_d_arr+=(--reuse_key)
183205

184206
min_validity="LETSENCRYPT_${cid}_MIN_VALIDITY"
185-
min_validity="${!min_validity}"
207+
min_validity="${!min_validity:-"<no value>"}"
186208
if [[ "$min_validity" == "<no value>" ]]; then
187209
min_validity=$DEFAULT_MIN_VALIDITY
188210
fi
@@ -310,6 +332,13 @@ function update_certs {
310332
docker_restart "${cid}"
311333
fi
312334

335+
for domain in "${!hosts_array}"; do
336+
if [[ -f "/etc/nginx/conf.d/standalone-cert-$domain.conf" ]]; then
337+
[[ $DEBUG == true ]] && echo "Debug: removing standalone configuration file /etc/nginx/conf.d/standalone-cert-$domain.conf"
338+
rm -f "/etc/nginx/conf.d/standalone-cert-$domain.conf" && should_reload_nginx='true'
339+
fi
340+
done
341+
313342
done
314343

315344
cleanup_links && should_reload_nginx='true'

app/start.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ term_handler() {
88
# shellcheck source=functions.sh
99
source /app/functions.sh
1010
remove_all_location_configurations
11+
remove_all_standalone_configurations
1112

1213
exit 0
1314
}

0 commit comments

Comments
 (0)