Skip to content

Commit 8566d4e

Browse files
committed
Periodically clean LetsEncrypt certificates.
1 parent ec9c82c commit 8566d4e

File tree

2 files changed

+108
-0
lines changed
  • rootfs
    • etc/services.d/cert_cleanup
    • opt/nginx-proxy-manager

2 files changed

+108
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/with-contenv sh
2+
3+
set -e # Exit immediately if a command exits with a non-zero status.
4+
5+
# Run weekly.
6+
INTERVAL_IN_SECS=604800
7+
8+
# Make sure we appear with a proper name under `ps`.
9+
if [ ! -L "$0" ]; then
10+
SV_NAME="$(basename "$(pwd)")"
11+
ln -sf run "$SV_NAME"
12+
exec ./"$SV_NAME" "$@"
13+
fi
14+
15+
log() {
16+
if [ -n "${1-}" ]; then
17+
echo "[$(basename "$0")] $*"
18+
else
19+
while read OUTPUT; do
20+
echo "[$(basename "$0")] $OUTPUT"
21+
done
22+
fi
23+
}
24+
25+
log "starting..."
26+
s6-applyuidgid -u $USER_ID -g $GROUP_ID -G ${SUP_GROUP_IDS:-$GROUP_ID} /usr/local/bin/watch -i "$INTERVAL_IN_SECS" /opt/nginx-proxy-manager/lecleaner | log
27+
28+
# vim:ft=sh:ts=4:sw=4:et:sts=4
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/python3
2+
3+
import os.path
4+
import os
5+
6+
from datetime import datetime
7+
from pathlib import Path
8+
9+
BASE = "/etc/letsencrypt/"
10+
11+
live_dir = os.path.join(BASE, "live")
12+
archive_dir = os.path.join(BASE, "archive")
13+
csr_dir = os.path.join(BASE, "csr")
14+
key_dir = os.path.join(BASE, "keys")
15+
16+
# Set of certificate paths actively used.
17+
in_use = set()
18+
19+
keep_count = 0
20+
delete_count = 0
21+
error_count = 0
22+
23+
def remove_file(f):
24+
success = False
25+
try:
26+
os.remove(f)
27+
success = True
28+
except OSError as e:
29+
print("ERROR: Could not remove {}: {}.".format(f, e))
30+
return success
31+
32+
# Build the set of certificates in use.
33+
for domain in os.path.isdir(live_dir) and os.listdir(live_dir) or [] :
34+
domain_dir = os.path.join(live_dir, domain)
35+
if not os.path.isdir(domain_dir):
36+
continue
37+
for certlink in os.listdir(domain_dir):
38+
f = os.path.join(domain_dir, certlink)
39+
if not os.path.islink(f):
40+
continue
41+
target = Path(f).resolve()
42+
in_use.add(target)
43+
44+
print("----------------------------------------------------------")
45+
print("Let's Encrypt certificates cleanup - {}".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")))
46+
print("----------------------------------------------------------")
47+
48+
# Remove all unused certificates from the archive directory.
49+
for domain in os.path.isdir(archive_dir) and os.listdir(archive_dir) or []:
50+
domain_dir = os.path.join(archive_dir, domain)
51+
if not os.path.isdir(domain_dir):
52+
continue
53+
for certfile in os.listdir(domain_dir):
54+
f = Path(os.path.join(domain_dir, certfile))
55+
if f.resolve() in in_use:
56+
print("Keeping {}.".format(f))
57+
keep_count += 1
58+
else:
59+
print("Deleting {}.".format(f))
60+
if remove_file(f):
61+
delete_count += 1
62+
else:
63+
error_count += 1
64+
65+
# Remove all files from the csr and key directories.
66+
for dir in [ csr_dir, key_dir ]:
67+
for file in os.path.isdir(dir) and os.listdir(dir) or []:
68+
f = os.path.join(dir, file)
69+
if not os.path.isfile(f):
70+
continue
71+
print("Deleting {}.".format(f))
72+
if remove_file(f):
73+
delete_count += 1
74+
else:
75+
error_count += 1
76+
77+
print("{} file(s) kept.".format(keep_count))
78+
print("{} file(s) deleted.".format(delete_count))
79+
if error_count > 0:
80+
print("{} file(s) failed to be deleted.".format(error_count))

0 commit comments

Comments
 (0)