From ccb111aed599fa56d01b79b6f42770fda3daaf30 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Mon, 11 May 2026 15:37:48 -0300 Subject: [PATCH] FEATURE: Add Let's Encrypt template using the nginx ACME module Adds a new web.letsencrypt-nginx.ssl template as an alternative to the existing acme.sh-based one. The nginx ACME module (built into the base image alongside ngx_brotli) handles certificate issuance and renewal natively via http-level directives, removing the need for the acme.sh download and the configure-letsencrypt/letsencrypt shell scripts. The existing web.letsencrypt.ssl template remains the default so current setups are unaffected. --- image/base/install-nginx | 7 +- .../web.letsencrypt-nginx.ssl.template.yml | 82 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 templates/web.letsencrypt-nginx.ssl.template.yml diff --git a/image/base/install-nginx b/image/base/install-nginx index 4cf600ac5..bd446d34b 100755 --- a/image/base/install-nginx +++ b/image/base/install-nginx @@ -21,9 +21,13 @@ git clone https://github.com/google/ngx_brotli.git cd /tmp/ngx_brotli git submodule update --init +# nginx ACME module (Let's Encrypt support without acme.sh) +cd /tmp +git clone --depth 1 https://github.com/nginx/nginx-acme.git + cd /tmp/nginx-$VERSION # ignoring deprecations with -Wno-deprecated-declarations while we wait for this https://github.com/google/ngx_brotli/issues/39#issuecomment-254093378 -./configure --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_v2_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads --add-module=/tmp/ngx_brotli +./configure --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_v2_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads --add-module=/tmp/ngx_brotli --add-module=/tmp/nginx-acme make install @@ -35,4 +39,5 @@ rm -f /tmp/nginx-$VERSION.tar.gz rm -f /tmp/nginx-$VERSION.tar.gz.asc rm -fr /tmp/libbrotli rm -fr /tmp/ngx_brotli +rm -fr /tmp/nginx-acme rm -fr /etc/nginx/modules-enabled/* diff --git a/templates/web.letsencrypt-nginx.ssl.template.yml b/templates/web.letsencrypt-nginx.ssl.template.yml new file mode 100644 index 000000000..bef7b11f6 --- /dev/null +++ b/templates/web.letsencrypt-nginx.ssl.template.yml @@ -0,0 +1,82 @@ +env: + DISCOURSE_FORCE_HTTPS: true + LETSENCRYPT_ACCOUNT_EMAIL: # optional: contact address for ACME account registration + +run: + - file: + path: "/usr/local/bin/configure-letsencrypt-nginx" + chmod: "+x" + contents: | + #!/bin/bash + set -e + + STATE_DIR="/shared/letsencrypt-nginx" + install -d -m 0700 -g root -o root "$STATE_DIR" + + if [ -n "$LETSENCRYPT_ACCOUNT_EMAIL" ]; then + CONTACT_LINE="contact mailto:${LETSENCRYPT_ACCOUNT_EMAIL};" + else + CONTACT_LINE="" + fi + + # http {} level configuration for the nginx ACME module. + # /etc/nginx/conf.d/*.conf is included from the main http block. + cat << EOF > /etc/nginx/conf.d/letsencrypt-acme.conf + resolver 1.1.1.1 8.8.8.8 ipv6=off; + + acme_issuer letsencrypt { + uri https://acme-v02.api.letsencrypt.org/directory; + ${CONTACT_LINE} + state_path ${STATE_DIR}; + accept_terms_of_service; + } + + acme_shared_zone zone=acme_shared:1M; + EOF + + # server {} outlet — replace the static ssl_certificate paths written by + # web.ssl.template.yml with the ACME-managed variables and enable the + # issuer for this server. + cat << EOF > /etc/nginx/conf.d/outlets/server/20-https.conf + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + acme_certificate letsencrypt; + ssl_certificate \$acme_certificate; + ssl_certificate_key \$acme_certificate_key; + ssl_certificate_cache max=2; + + ssl_session_tickets off; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:1m; + + add_header Strict-Transport-Security 'max-age=31536000'; + + if (\$http_host != ${DISCOURSE_HOSTNAME}) { + rewrite (.*) https://${DISCOURSE_HOSTNAME}\$1 permanent; + } + EOF + + # The port 80 redirect server written by web.ssl.template.yml already + # serves /.well-known/* — the ACME module piggybacks on that location + # path for HTTP-01 challenges. No further changes needed there. + + grep -q 'force_https' /var/www/discourse/config/discourse.conf \ + || echo "force_https = 'true'" >> /var/www/discourse/config/discourse.conf + +hooks: + after_ssl: + - replace: + filename: /etc/runit/1.d/install-ssl + from: "# after ssl" + to: | + if [ -z "$DISABLE_LETSENCRYPT" ] || [ -n "$ENABLE_LETSENCRYPT" ]; then + /usr/local/bin/configure-ssl + /usr/local/bin/configure-letsencrypt-nginx + fi + # after ssl