Skip to content

Commit 7dd2cd6

Browse files
authored
Merge pull request #485 from Greek64/PR
Add LETSENCRYPT_MIN_VALIDITY variable
2 parents 6a90d53 + a0e54d8 commit 7dd2cd6

File tree

7 files changed

+146
-2
lines changed

7 files changed

+146
-2
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ If you want to create multi-domain ([SAN](https://www.digicert.com/subject-alter
166166
If you want to create test certificates that don't have the 5 certs/week/domain limits define the `LETSENCRYPT_TEST` environment variable with a value of `true` (in the containers where you request certificates with `LETSENCRYPT_HOST`). If you want to do this globally for all containers, set `ACME_CA_URI` as described below.
167167

168168
##### Automatic certificate renewal
169-
Every hour (3600 seconds) the certificates are checked and every certificate that will expire in the next [30 days](https://github.com/kuba/simp_le/blob/ecf4290c4f7863bb5427b50cdd78bc3a5df79176/simp_le.py#L72) (90 days / 3) are renewed.
169+
Every hour (3600 seconds) the certificates are checked and per default every certificate that will expire in the next [30 days](https://github.com/zenhack/simp_le/blob/a8a8013c097910f8f3cce046f1077b41b745673b/simp_le.py#L73) (90 days / 3) is renewed.
170+
171+
If you want to manually set a different minimum validity for certificates, you can set the `LETSENCRYPT_MIN_VALIDIDTY` environment variable (in each container that defines the `LETSENCRYPT_HOST` variable) to the desired period in seconds.
172+
Note that the possible values are internally capped at an upper bound of 7603200 (88 days) and a lower bound of 7200 (2 hours) as a security margin, considering that the Let's Encrypt CA does only issues certificates with a lifetime of [90 days](https://letsencrypt.org/2015/11/09/why-90-days.html) (upper bound), the rate limits imposed on certificate renewals are [5 per week](https://letsencrypt.org/docs/rate-limits/) (upper bound), and the fact that the certificates are checked and renewed accordingly every hour (lower bound).
170173

171174
##### Force certificates renewal
172175
If needed, you can force a running letsencrypt-nginx-proxy-companion container to renew all certificates that are currently in use. Replace `nginx-letsencrypt` with the name of your letsencrypt-nginx-proxy-companion container in the following command:

app/letsencrypt_service

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ACME_CA_URI="${ACME_CA_URI:-https://acme-v01.api.letsencrypt.org/directory}"
88
DEFAULT_KEY_SIZE=4096
99
REUSE_ACCOUNT_KEYS="$(lc ${REUSE_ACCOUNT_KEYS:-true})"
1010
REUSE_PRIVATE_KEYS="$(lc ${REUSE_PRIVATE_KEYS:-false})"
11+
MIN_VALIDITY_CAP=7603200
12+
DEFAULT_MIN_VALIDITY=2592000
1113

1214
function create_link {
1315
local -r source=${1?missing source argument}
@@ -174,7 +176,28 @@ function update_certs {
174176

175177
[[ "$(lc $DEBUG)" == true ]] && params_d_str+=" -v"
176178
[[ $REUSE_PRIVATE_KEYS == true ]] && params_d_str+=" --reuse_key"
177-
[[ "${1}" == "--force-renew" ]] && params_d_str+=" --valid_min 7776000"
179+
180+
min_validity="LETSENCRYPT_${cid}_MIN_VALIDITY"
181+
min_validity="${!min_validity}"
182+
if [[ "$min_validity" == "<no value>" ]]; then
183+
min_validity=$DEFAULT_MIN_VALIDITY
184+
fi
185+
# Sanity Check
186+
# Upper Bound
187+
if [[ $min_validity -gt $MIN_VALIDITY_CAP ]]; then
188+
min_validity=$MIN_VALIDITY_CAP
189+
fi
190+
# Lower Bound
191+
if [[ $min_validity -lt $(($seconds_to_wait * 2)) ]]; then
192+
min_validity=$(($seconds_to_wait * 2))
193+
fi
194+
195+
if [[ "${1}" == "--force-renew" ]]; then
196+
# Manually set to highest certificate lifetime given by LE CA
197+
params_d_str+=" --valid_min 7776000"
198+
else
199+
params_d_str+=" --valid_min $min_validity"
200+
fi
178201

179202
# Create directory for the first domain,
180203
# make it root readable only and make it the cwd

app/letsencrypt_service_data.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LETSENCRYPT_{{ $cid }}_KEYSIZE="{{ $container.Env.LETSENCRYPT_KEYSIZE }}"
1111
LETSENCRYPT_{{ $cid }}_TEST="{{ $container.Env.LETSENCRYPT_TEST }}"
1212
LETSENCRYPT_{{ $cid }}_ACCOUNT_ALIAS="{{ $container.Env.LETSENCRYPT_ACCOUNT_ALIAS }}"
1313
LETSENCRYPT_{{ $cid }}_RESTART_CONTAINER="{{ $container.Env.LETSENCRYPT_RESTART_CONTAINER }}"
14+
LETSENCRYPT_{{ $cid }}_MIN_VALIDITY="{{ $container.Env.LETSENCRYPT_MIN_VALIDITY }}"
1415
{{ end }}
1516

1617
{{ end }}

test/config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ imageTests+=(
1212
certs_single
1313
certs_san
1414
force_renew
15+
certs_validity
1516
container_restart
1617
permissions_default
1718
permissions_custom

test/setup/setup-boulder.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@ setup_boulder() {
1111
$GOPATH/src/github.com/letsencrypt/boulder
1212
pushd $GOPATH/src/github.com/letsencrypt/boulder
1313
if [[ "$(uname)" == 'Darwin' ]]; then
14+
# Set Standard Ports
1415
sed -i '' 's/ 5002/ 80/g' test/config/va.json
1516
sed -i '' 's/ 5001/ 443/g' test/config/va.json
17+
# Set certificate lifetime to 88 days
18+
sed -i '' 's/2160h/2112h/g' test/config/ca-a.json
19+
sed -i '' 's/2160h/2112h/g' test/config/ca-b.json
20+
# Modify custom rate limit
1621
sed -i '' 's/le.wtf,le1.wtf/le1.wtf,le2.wtf,le3.wtf/g' test/rate-limit-policies.yml
1722
else
23+
# Set Standard Ports
1824
sed --in-place 's/ 5002/ 80/g' test/config/va.json
1925
sed --in-place 's/ 5001/ 443/g' test/config/va.json
26+
# Set certificate lifetime to 88 days
27+
sed --in-place 's/2160h/2112h/g' test/config/ca-a.json
28+
sed --in-place 's/2160h/2112h/g' test/config/ca-b.json
29+
# Modify custom rate limit
2030
sed --in-place 's/le.wtf,le1.wtf/le1.wtf,le2.wtf,le3.wtf/g' test/rate-limit-policies.yml
2131
fi
2232
docker-compose build --pull
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Started letsencrypt container for test certs_validity
2+
Started test web server for le1.wtf
3+
Started test web server for le2.wtf
4+
Started test web server for le3.wtf
5+
Symlink to le1.wtf certificate has been generated.
6+
The link is pointing to the file ./le1.wtf/fullchain.pem
7+
Symlink to le2.wtf certificate has been generated.
8+
The link is pointing to the file ./le2.wtf/fullchain.pem
9+
Symlink to le3.wtf certificate has been generated.
10+
The link is pointing to the file ./le3.wtf/fullchain.pem
11+
Certificate for le1.wtf was not renewed.
12+
Certificate for le2.wtf was not renewed.
13+
Certificate for le3.wtf was renewed.

test/tests/certs_validity/run.sh

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/bin/bash
2+
3+
## Test for the LETSENCRYPT_MIN_VALIDITY environment variable.
4+
5+
if [[ -z $TRAVIS_CI ]]; then
6+
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
7+
else
8+
le_container_name="$(basename ${0%/*})"
9+
fi
10+
run_le_container ${1:?} "$le_container_name"
11+
12+
# Create the $domains array from comma separated domains in TEST_DOMAINS.
13+
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
14+
15+
# Cleanup function with EXIT trap
16+
function cleanup {
17+
# Remove any remaining Nginx container(s) silently.
18+
for domain in "${domains[@]}"; do
19+
docker rm --force "$domain" > /dev/null 2>&1
20+
done
21+
# Cleanup the files created by this run of the test to avoid foiling following test(s).
22+
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
23+
# Stop the LE container
24+
docker stop "$le_container_name" > /dev/null
25+
}
26+
trap cleanup EXIT
27+
28+
# Run a separate nginx container for each domain in the $domains array.
29+
# Default validity
30+
docker run --rm -d \
31+
--name "${domains[0]}" \
32+
-e "VIRTUAL_HOST=${domains[0]}" \
33+
-e "LETSENCRYPT_HOST=${domains[0]}" \
34+
--network boulder_bluenet \
35+
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]}"
36+
# Manual validity (same as default)
37+
docker run --rm -d \
38+
--name "${domains[1]}" \
39+
-e "VIRTUAL_HOST=${domains[1]}" \
40+
-e "LETSENCRYPT_HOST=${domains[1]}" \
41+
-e "LETSENCRYPT_MIN_VALIDITY=2592000" \
42+
--network boulder_bluenet \
43+
nginx:alpine > /dev/null && echo "Started test web server for ${domains[1]}"
44+
# Manual validity (few seconds shy of MIN_VALIDITY_CAP=7603200)
45+
docker run --rm -d \
46+
--name "${domains[2]}" \
47+
-e "VIRTUAL_HOST=${domains[2]}" \
48+
-e "LETSENCRYPT_HOST=${domains[2]}" \
49+
-e "LETSENCRYPT_MIN_VALIDITY=7603190" \
50+
--network boulder_bluenet \
51+
nginx:alpine > /dev/null && echo "Started test web server for ${domains[2]}"
52+
53+
# Wait for a symlinks
54+
wait_for_symlink "${domains[0]}" "$le_container_name"
55+
wait_for_symlink "${domains[1]}" "$le_container_name"
56+
wait_for_symlink "${domains[2]}" "$le_container_name"
57+
# Grab the expiration times of the certificates
58+
first_cert_expire_1="$(get_cert_expiration_epoch "${domains[0]}" "$le_container_name")"
59+
first_cert_expire_2="$(get_cert_expiration_epoch "${domains[1]}" "$le_container_name")"
60+
first_cert_expire_3="$(get_cert_expiration_epoch "${domains[2]}" "$le_container_name")"
61+
62+
# Wait for ${domains[2]} set certificate validity to expire
63+
sleep 10
64+
65+
# Manually trigger letsencrypt_service
66+
docker exec "$le_container_name" /bin/bash -c "source /app/letsencrypt_service --source-only; update_certs" > /dev/null 2>&1
67+
68+
# Grab the new expiration times of the certificates
69+
second_cert_expire_1="$(get_cert_expiration_epoch "${domains[0]}" "$le_container_name")"
70+
second_cert_expire_2="$(get_cert_expiration_epoch "${domains[1]}" "$le_container_name")"
71+
second_cert_expire_3="$(get_cert_expiration_epoch "${domains[2]}" "$le_container_name")"
72+
73+
if [[ $second_cert_expire_1 -eq $first_cert_expire_1 ]]; then
74+
echo "Certificate for ${domains[0]} was not renewed."
75+
else
76+
echo "Certificate for ${domains[0]} was incorrectly renewed."
77+
echo "First certificate expiration epoch : $first_cert_expire_1."
78+
echo "Second certificate expiration epoch : $second_cert_expire_1."
79+
fi
80+
if [[ $second_cert_expire_2 -eq $first_cert_expire_2 ]]; then
81+
echo "Certificate for ${domains[1]} was not renewed."
82+
else
83+
echo "Certificate for ${domains[1]} was incorrectly renewed."
84+
echo "First certificate expiration epoch : $first_cert_expire_2."
85+
echo "Second certificate expiration epoch : $second_cert_expire_2."
86+
fi
87+
if [[ $second_cert_expire_3 -gt $first_cert_expire_3 ]]; then
88+
echo "Certificate for ${domains[2]} was renewed."
89+
else
90+
echo "Certificate for ${domains[2]} was not renewed."
91+
echo "First certificate expiration epoch : $first_cert_expire_3."
92+
echo "Second certificate expiration epoch : $second_cert_expire_3."
93+
fi

0 commit comments

Comments
 (0)