diff --git a/cookbooks/lego/Makefile b/cookbooks/lego/Makefile index 8a4336a..54052a7 100644 --- a/cookbooks/lego/Makefile +++ b/cookbooks/lego/Makefile @@ -9,3 +9,6 @@ PROJECT_GID = 10000 # Include common Makefile include ../../scripts/common.mk +TARGET_FILES += $(TARGET_CHROOT)/etc/quadlets/lego/renew-hooks.d +$(TARGET_CHROOT)/etc/quadlets/lego/renew-hooks.d: + install -m 0700 -o root -g root -D -d $@ diff --git a/cookbooks/lego/config/examples/config.env b/cookbooks/lego/config/examples/config.env index 6ea1f7e..59d0915 100644 --- a/cookbooks/lego/config/examples/config.env +++ b/cookbooks/lego/config/examples/config.env @@ -1,3 +1,3 @@ LEGO_GLOBAL_ARGS=-a -m nicolas.masse@itix.fr -d changeme.example.tld --http LEGO_RUN_ARGS= -LEGO_RENEW_ARGS=--days 30 --renew-hook=/etc/lego/hooks/flag-as-renewed.sh +LEGO_RENEW_ARGS=--days 30 --renew-hook=/etc/lego/renew-hook/flag-as-renewed.sh diff --git a/cookbooks/lego/config/examples/lego-dev.sh b/cookbooks/lego/config/examples/lego-dev.sh new file mode 100755 index 0000000..8a5620f --- /dev/null +++ b/cookbooks/lego/config/examples/lego-dev.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -Eeuo pipefail + +# In development mode, it is not possible to get a certificate from Let's Encrypt, so we just create a self-signed certificate for localhost, so that other services can still use it. +mkdir -p /var/lib/quadlets/lego/certificates +if [ -f /var/lib/quadlets/lego/certificates/localhost.crt ] && [ -f /var/lib/quadlets/lego/certificates/localhost.key ]; then + renewal="yes" +else + renewal="no" +fi + +echo "Generating self-signed certificate for localhost..." +openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /var/lib/quadlets/lego/certificates/localhost.key -out /var/lib/quadlets/lego/certificates/localhost.crt -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" + +if [[ "$renewal" == "yes" ]]; then + echo "Flagging certificate as renewed..." + touch /var/lib/quadlets/lego/certificates/localhost.renewed +fi diff --git a/cookbooks/lego/config/process-hooks.sh b/cookbooks/lego/config/process-hooks.sh new file mode 100755 index 0000000..b6e969e --- /dev/null +++ b/cookbooks/lego/config/process-hooks.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -Eeuo pipefail + +for hook in /etc/quadlets/lego/renew-hooks.d/*.sh; do + if [[ -x "$hook" ]]; then + echo "Running renew hook: $hook" + if ! "$hook"; then + echo "Error: Renew hook failed: $hook" >&2 + fi + else + echo "Skipping non-executable hook: $hook" + fi +done + +rm -f /var/lib/quadlets/lego/certificates/*.renewed + +exit 0 \ No newline at end of file diff --git a/cookbooks/lego/config/hooks/flag-as-renewed.sh b/cookbooks/lego/config/renew-hook/flag-as-renewed.sh similarity index 100% rename from cookbooks/lego/config/hooks/flag-as-renewed.sh rename to cookbooks/lego/config/renew-hook/flag-as-renewed.sh diff --git a/cookbooks/lego/dropins/lego-renew.container.d/lego-dev.conf b/cookbooks/lego/dropins/lego-renew.container.d/lego-dev.conf new file mode 100644 index 0000000..b29053f --- /dev/null +++ b/cookbooks/lego/dropins/lego-renew.container.d/lego-dev.conf @@ -0,0 +1,7 @@ +[Container] +# When in development mode, it is not possible to renew a certificate from Let's Encrypt, so we just skip the entrypoint. +Entrypoint=/bin/true + +[Service] +# And we update the self-signed certificate and flag it as renewed. +ExecStartPost=/etc/quadlets/lego/lego-dev.sh diff --git a/cookbooks/lego/dropins/lego-run.container.d/lego-dev.conf b/cookbooks/lego/dropins/lego-run.container.d/lego-dev.conf new file mode 100644 index 0000000..4f6d2fb --- /dev/null +++ b/cookbooks/lego/dropins/lego-run.container.d/lego-dev.conf @@ -0,0 +1,7 @@ +[Container] +# When in development mode, it is not possible to get a certificate from Let's Encrypt, so we just skip the entrypoint. +Entrypoint=/bin/true + +[Service] +# And we create a self-signed certificate for localhost, so that other services can still use it. +ExecStartPost=/etc/quadlets/lego/lego-dev.sh diff --git a/cookbooks/lego/hooks.mk b/cookbooks/lego/hooks.mk new file mode 100644 index 0000000..7dba0e6 --- /dev/null +++ b/cookbooks/lego/hooks.mk @@ -0,0 +1,5 @@ +# Lego renewal hooks +TARGET_LEGO_FILES = $(patsubst other/lego/%.sh, $(TARGET_CHROOT)/etc/quadlets/lego/renew-hooks.d/%.sh, $(wildcard other/lego/*.sh)) +TARGET_EXAMPLE_FILES += $(TARGET_LEGO_FILES) +$(TARGET_CHROOT)/etc/quadlets/lego/renew-hooks.d/%.sh: other/lego/%.sh + install -D -m 0755 -o root -g root $< $@ diff --git a/cookbooks/lego/lego-renew-hooks.service b/cookbooks/lego/lego-renew-hooks.service new file mode 100644 index 0000000..cd9048a --- /dev/null +++ b/cookbooks/lego/lego-renew-hooks.service @@ -0,0 +1,12 @@ +[Unit] +Description=Process Lego certificate renewal hooks +# Lego touches .renewed files when renewed certificates are available +ConditionPathExistsGlob=/var/lib/quadlets/lego/certificates/*.renewed +After=lego-renew.service + +[Service] +Type=oneshot +ExecStart=/etc/quadlets/lego/process-hooks.sh + +[Install] +WantedBy=lego-renew.service diff --git a/cookbooks/lego/lego-renew.container b/cookbooks/lego/lego-renew.container index f0ed199..4cae08a 100644 --- a/cookbooks/lego/lego-renew.container +++ b/cookbooks/lego/lego-renew.container @@ -27,7 +27,7 @@ EnvironmentFile=/etc/quadlets/lego/config.env # Volume mounts Volume=/var/lib/quadlets/lego:/.lego:z -Volume=/etc/quadlets/lego/hooks:/etc/lego/hooks:ro +Volume=/etc/quadlets/lego/renew-hook:/etc/lego/renew-hook:ro # Be safe, set the umask to 0077 so that private keys are not world-readable PodmanArgs=--umask=0077 diff --git a/cookbooks/quay/config/quay_load_tls_certs.sh b/cookbooks/quay/config/quay_load_tls_certs.sh index 672e6e6..68a66f3 100755 --- a/cookbooks/quay/config/quay_load_tls_certs.sh +++ b/cookbooks/quay/config/quay_load_tls_certs.sh @@ -2,13 +2,5 @@ set -Eeuo pipefail -if ls /var/lib/quadlets/lego/certificates/*.crt &> /dev/null; then - echo "Lego-issued certificates found, loading them for Quay..." - install -o 10026 -g 10000 -m 0600 $(ls /var/lib/quadlets/lego/certificates/*.crt | head -1) /etc/quadlets/quay/app/ssl.cert - install -o 10026 -g 10000 -m 0600 $(ls /var/lib/quadlets/lego/certificates/*.key | head -1) /etc/quadlets/quay/app/ssl.key -elif [ ! -f /etc/quadlets/quay/app/ssl.cert ] && [ ! -f /etc/quadlets/quay/app/ssl.key ]; then - echo "No Lego-issued certificates found, generating self-signed certificates for Quay..." - openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/quadlets/quay/app/ssl.key -out /etc/quadlets/quay/app/ssl.cert -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" - chown 10026:10000 /etc/quadlets/quay/app/ssl.{key,cert} - chmod 0600 /etc/quadlets/quay/app/ssl.{key,cert} -fi +install -o 10026 -g 10000 -m 0600 $(ls /var/lib/quadlets/lego/certificates/*.crt | head -1) /run/quadlets/quay/tls/ssl.cert +install -o 10026 -g 10000 -m 0600 $(ls /var/lib/quadlets/lego/certificates/*.key | head -1) /run/quadlets/quay/tls/ssl.key diff --git a/cookbooks/quay/other/lego/quay.sh b/cookbooks/quay/other/lego/quay.sh new file mode 100644 index 0000000..3294d9b --- /dev/null +++ b/cookbooks/quay/other/lego/quay.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -Eeuo pipefail + +/etc/quadlets/quay/quay_load_tls_certs.sh +systemctl --no-block restart quay-app.service diff --git a/cookbooks/quay/quay-app.container b/cookbooks/quay/quay-app.container index 69483e6..bb8b4cc 100644 --- a/cookbooks/quay/quay-app.container +++ b/cookbooks/quay/quay-app.container @@ -1,8 +1,8 @@ [Unit] Description=Quay Container Registry Application Documentation=https://docs.projectquay.io/ -After=network.target redis-server.service quay-init-certificate.service var-lib-virtiofs-data.mount -Requires=redis-server.service quay-init-certificate.service var-lib-virtiofs-data.mount +After=network.target redis-server.service var-lib-virtiofs-data.mount +Requires=redis-server.service var-lib-virtiofs-data.mount # Only start if Quay has been configured ConditionPathExists=/etc/quadlets/quay/app/config.yaml @@ -32,6 +32,8 @@ Volume=/var/lib/virtiofs/data/quay/storage:/datastorage:Z # Configuration Volume=/etc/quadlets/quay/app:/quay-registry/conf/stack:Z,ro +Volume=/run/quadlets/quay/tls/ssl.cert:/quay-registry/conf/stack/ssl.cert:Z,ro +Volume=/run/quadlets/quay/tls/ssl.key:/quay-registry/conf/stack/ssl.key:Z,ro # Health check HealthCmd=curl -sk https://localhost:8443/health/instance @@ -46,6 +48,9 @@ RestartSec=10 TimeoutStartSec=120 TimeoutStopSec=30 +# Load TLS certificates from Lego before starting the container +ExecStartPre=/etc/quadlets/quay/quay_load_tls_certs.sh + # Wait for PostgreSQL to be ready on localhost ExecStartPre=/bin/sh -c 'exec 2>/dev/null; for try in $(seq 0 12); do if ! /bin/true 5<> /dev/tcp/127.0.0.1/5432; then echo "Waiting for PostgreSQL to be available..."; sleep 5; else exit 0; fi; done; exit 1' diff --git a/cookbooks/quay/quay-init-certificate.service b/cookbooks/quay/quay-init-certificate.service deleted file mode 100644 index 49f6a72..0000000 --- a/cookbooks/quay/quay-init-certificate.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Initialize Quay TLS certificates if not already present -Before=quay-app.service -After=lego.target - -# Start/stop this unit when the target is started/stopped -PartOf=quay.target - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/etc/quadlets/quay/quay_load_tls_certs.sh - -[Install] -WantedBy=quay.target diff --git a/cookbooks/quay/quay-load-renewed-certificate.service b/cookbooks/quay/quay-load-renewed-certificate.service deleted file mode 100644 index 481f876..0000000 --- a/cookbooks/quay/quay-load-renewed-certificate.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Reload Quay TLS certificate after Lego renewal -# Lego touches .renewed files when renewed certificates are available -ConditionPathExistsGlob=/var/lib/quadlets/lego/certificates/*.renewed -After=lego-renew.service - -[Service] -Type=oneshot -# Copy the renewed certificates to the Quay TLS directory -ExecStart=/etc/quadlets/quay/quay_load_tls_certs.sh -# Restart Quay to load the new certificate -ExecStart=systemctl --no-block restart quay-app.service -# Remove the flag files after restarting Quay -ExecStartPost=/bin/sh -Eeuo pipefail -c 'rm -f /var/lib/quadlets/lego/certificates/*.renewed' - -[Install] -WantedBy=lego-renew.service diff --git a/cookbooks/quay/quay.target b/cookbooks/quay/quay.target index 08f4255..a3e50bb 100644 --- a/cookbooks/quay/quay.target +++ b/cookbooks/quay/quay.target @@ -1,8 +1,8 @@ [Unit] Description=Quay Container Registry Target Documentation=https://docs.projectquay.io/ -Requires=postgresql.target redis.target quay-clair.service quay-app.service quay-init-certificate.service -After=postgresql.target redis.target quay-clair.service quay-app.service quay-init-certificate.service +Requires=postgresql.target redis.target lego.target quay-clair.service quay-app.service +After=postgresql.target redis.target lego.target quay-clair.service quay-app.service # Allow isolation - can stop/start this target independently AllowIsolate=yes diff --git a/cookbooks/vsftpd/other/lego/vsftpd.sh b/cookbooks/vsftpd/other/lego/vsftpd.sh new file mode 100644 index 0000000..b5913b0 --- /dev/null +++ b/cookbooks/vsftpd/other/lego/vsftpd.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -Eeuo pipefail + +install -o 10015 -g 10000 -m 0600 -t /run/quadlets/vsftpd/tls /var/lib/quadlets/lego/certificates/*.crt /var/lib/quadlets/lego/certificates/*.key +systemctl --no-block restart vsftpd.service diff --git a/cookbooks/vsftpd/vsftpd-load-renewed-certificate.service b/cookbooks/vsftpd/vsftpd-load-renewed-certificate.service deleted file mode 100644 index af45ede..0000000 --- a/cookbooks/vsftpd/vsftpd-load-renewed-certificate.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Restart Vsftpd if a new TLS certificate is available -# Lego touch .renewed files when renewed certificates are available -ConditionPathExistsGlob=/var/lib/quadlets/lego/certificates/*.renewed -After=lego-renew.service - -[Service] -Type=oneshot -# Copy the renewed certificates to the vsftpd /run directory -ExecStartPre=/bin/sh -Eeuo pipefail -c 'install -o 10015 -g 10000 -m 0600 -t /run/quadlets/vsftpd/tls /var/lib/quadlets/lego/certificates/*.crt /var/lib/quadlets/lego/certificates/*.key' -# Restart vsftpd to load the new certificates -ExecStart=systemctl --no-block restart vsftpd.service -# Remove the flag files after restarting vsftpd -ExecStartPost=/bin/sh -Eeuo pipefail -c 'rm -f /var/lib/quadlets/lego/certificates/*.renewed' - -[Install] -WantedBy=lego-renew.service