SSL Certificate maintenance using ACME with legacy applications
Apache
Config
/etc/apache2/sites-enabled/custom.conf |
---|
| Define server_name "custom-site"
Define domain_name "custom-domain.tld"
Define server_fqdn "${server_name}.${domain_name}"
Define persistent_volume_path "/mnt/persistent-volumes/resinfo-go-acme-lego"
Define acme_challenge_path "${persistent_volume_path}/data/www/.well-known/acme-challenge/"
Alias "/.well-known/acme-challenge/" "${acme_challenge_path}"
<IfModule mod_access_compat.c>
<Directory "${acme_challenge_path}">
Satisfy any
</Directory>
</IfModule>
<VirtualHost ${server_fqdn}:80>
ServerName ${server_fqdn}
RewriteEngine on
RewriteCond %{REQUEST_URI} !^\.well-known/acme-challenge/.*
RewriteRule ^https://%{HTTP_HOST}%{REQUEST_URI} [END,NE]
#LogLevel trace5
LogLevel info
ErrorLog ${APACHE_LOG_DIR}/vh_${server_name}_80_.error.log
CustomLog ${APACHE_LOG_DIR}/vh_${server_name}_80_.access.log combined
</VirtualHost>
<VirtualHost ${server_fqdn}:443>
ServerName ${server_fqdn}
DocumentRoot /var/www/${server_name}/html
#LogLevel trace5
LogLevel info
ErrorLog ${APACHE_LOG_DIR}/vh_${server_name}_443_.error.log
CustomLog ${APACHE_LOG_DIR}/vh_${server_name}_443_.access.log combined
# configure SSL
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/${server_fqdn}.crt
SSLCertificateKeyFile /etc/apache2/ssl/${server_fqdn}.key
SSLProtocol All -SSLv2 -SSLv3
SSLOpenSSLConfCmd DHParameters /etc/ssl/certs/dhparam.pem
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
...
</VirtualHost>
|
Systemd
Filesystem monitoring
/etc/systemd/system/apache2-reload-ssl-config.path |
---|
| [Unit]
Description="Monitor the apache reload file"
[Path]
PathChanged=/mnt/persistent-volumes/resinfo-go-acme-lego/data/reload
Unit=apache2-reload-ssl-config.service
[Install]
WantedBy=multi-user.target
|
Service reloading
/etc/systemd/system/apache2-reload-ssl-config.service |
---|
| [Unit]
Description="Reload apache2 with the new certificate"
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c "cp -R /mnt/persistent-volumes/resinfo-go-acme-lego/data/certificates/* /etc/apache2/ssl/"
ExecStart=/usr/sbin/apachectl restart
[Install]
WantedBy=multi-user.target
|
$ systemctl enable --now apache2-reload-ssl-config.path apache2-reload-ssl-config.service
Docker
Systemd
Service
/etc/systemd/system/resinfo-go-acme-lego.service |
---|
| [Unit]
Description=resinfo-go-acme-lego
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/etc/docker/compose/resingo-go-acme-lego
# Compose up
ExecStart=/usr/bin/docker compose --file docker-compose.json up
# Compose down, remove containers and volumes
ExecStop=/usr/bin/docker compose --file docker-compose.json down --volumes
[Install]
WantedBy=multi-user.target
|
Timer
/etc/systemd/system/resinfo-go-acme-lego.timer |
---|
| [Unit]
Description=resinfo-go-acme-lego
Requires=resinfo-go-acme-lego.service
[Timer]
OnCalendar=weekly
Persistent=true
Unit=resinfo-go-acme-lego.service
[Install]
WantedBy=timers.target
|
Docker
compose
/etc/docker/compose/resinfo-go-acme-lego/.env |
---|
| RESINFO_GO_ACME_LEGO_RELEASE_VERSION='v4.25.2'
RESINFO_GO_ACME_LEGO_UID='1002'
RESINFO_GO_ACME_LEGO_GID='1002'
RESINFO_GO_ACME_LEGO_PERSISTENT_CONF_DIR_PATH='/mnt/persistent-volumes/resinfo-go-acme-lego/conf'
RESINFO_GO_ACME_LEGO_PERSISTENT_DATA_DIR_PATH='/mnt/persistent-volumes/resinfo-go-acme-lego/data'
|
/etc/docker/compose/resinfo-go-acme-lego/Dockerfile |
---|
| ARG ARG_RESINFO_GO_ACME_LEGO_RELEASE_VERSION
FROM goacme/lego:${ARG_RESINFO_GO_ACME_LEGO_RELEASE_VERSION}
COPY entrypoint.bash /entrypoint.bash
COPY lego-hook.bash /lego-hook.bash
RUN apk add bash \
&& chmod +x /entrypoint.bash \
&& chmod +x /lego-hook.bash
ENTRYPOINT [ "/entrypoint.bash" ]
|
/etc/docker/compose/resinfo-go-acme-lego/docker-compose.json |
---|
| {
"networks": {
"external": {
"external": true
}
},
"services": {
"resinfo-go-acme-lego": {
"container_name": "resinfo-go-acme-lego",
"build": {
"context": ".",
"dockerfile": "Dockerfile",
"args": [
"ARG_RESINFO_GO_ACME_LEGO_RELEASE_VERSION=${RESINFO_GO_ACME_LEGO_RELEASE_VERSION}"
]
},
"image": "goacme-lego:${RESINFO_GO_ACME_LEGO_RELEASE_VERSION}-p0",
"user": "${RESINFO_GO_ACME_LEGO_UID}:${RESINFO_GO_ACME_LEGO_GID}",
"networks": {
"external": {}
},
"volumes": [
"${RESINFO_GO_ACME_LEGO_PERSISTENT_CONF_DIR_PATH}:/conf:ro",
"${RESINFO_GO_ACME_LEGO_PERSISTENT_DATA_DIR_PATH}:/data:rw"
]
}
}
}
|
/etc/docker/compose/resinfo-go-acme-lego/entrypoint.bash |
---|
| #!/bin/bash
set -e
[ "${DEBUG:=UNSET}" == 'UNSET' ] || set -x
function acme_create {
local -r acme_domain="${1}"
local -r http_webroot="${2}"
# lego --server "${ACME_SERVER_URL}" \
# --accept-tos \
# --email "${ACME_EMAIL}" \
# --eab --kid "${ACME_EAB_KEY_ID}" --hmac "${ACME_EAB_HMAC_KEY}" \
# --http --http.webroot "${http_webroot}" \
# --path "/data" \
# --domains "${acme_domain}" \
# run --run-hook /lego-hook.bash
/lego --accept-tos \
--email "${ACME_EMAIL}" \
--http --http.webroot "${http_webroot}" \
--path "/data" \
--domains "${acme_domain}" \
run --run-hook /lego-hook.bash
}
function acme_renew {
local -r acme_domain="${1}"
local -r http_webroot="${2}"
# lego --server "${ACME_SERVER_URL}" \
# --email "${ACME_EMAIL}" \
# --eab --kid "${ACME_EAB_KEY_ID}" \
# --http --http.webroot "${http_webroot}" \
# --path "/data" \
# --domains "${acme_domain}" \
# renew --renew-hook /lego-hook.bash
/lego --email "${ACME_EMAIL}" \
--http --http.webroot "${http_webroot}" \
--path "/data" \
--domains "${acme_domain}" \
renew --renew-hook /lego-hook.bash
}
function main {
local -r env_file='/conf/.env'
source "${env_file}"
printenv
local -r http_webroot="/data/www"
mkdir -p "${http_webroot}"
set -x
for acme_domain in "${ACME_DOMAINS}"; do
if [ -f "/data/certificates/${acme_domain}.crt" ]; then
acme_renew "${acme_domain}" \
"${http_webroot}"
else
acme_create "${acme_domain}" \
"${http_webroot}"
fi
done
}
main
|
/etc/docker/compose/resinfo-go-acme-lego/lego-hook.bash |
---|
| #!/usr/bin/env bash
set -e
touch /data/reload
|