From 12e033388c8c90cefdc74fb8f79edfa4c69711b2 Mon Sep 17 00:00:00 2001 From: Ralf Zerres Date: Sun, 25 Apr 2021 17:38:37 +0200 Subject: [PATCH] introduce a 'nginx' hook - introduce an nginx hook the hook will resolve the new environment variables * NGINX_CONFDIR -> default: /etc/nginx/conf.d * NGINX_CHALLENGE_LOCTION -> default: 001-challenge-letsencrypt.conf * NGINX_TLS_CERTIFICATE -> default:002-tls-certificates.conf - group: nginx-config-challenge-location this will generate -> $NGINX_CONFDIR/$NGINX_CHALLENGE_LOCATION - group: nginx-config-certificate-location this will generate -> $NGINX_CONFDIR/$NGINX_TLS_CERTIFICATE ACMEd amims to minimize the amount of ReadWrite directories. The systemd.unit (acmed.service), takes advantage of sandbox capabilities. For nginx support, we need to ReadWrite to $NGINX_CONFDIR. Since we are running with uid/gid of 'acmed' and probably aren't authorized, an user with administrative rights need to call once chmod g+w $NGINX_CONFIR on systemd managed systems, we are able to correct/rewrite the directory rights at installation time (tmpfiles.d/acmed.conf). A website admin needs to include * $NGINX_CHALLENGE_LOCATION and * $NGINX_TLS_CERTIFICATE inside the [virtual-]host configuration block of websites to activate the templates (non automated task) - adapt acmed.service will hint to NGINX environment variables defaults are handled inside the nginx hook - adapt tempfiles.d/acmed.conf preset default directory to store challenges (if acmed isn't started via systemd) - adapat Makefile add nginx_hooks.toml Signed-off-by: Ralf Zerres --- Makefile | 1 + acmed/config/acmed.toml | 1 + acmed/config/nginx_hooks.toml | 130 ++++++++++++++++++++++++++++++++++ contrib/acmed.service | 21 ++++-- contrib/tmpfiles.d/acmed.conf | 7 +- 5 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 acmed/config/nginx_hooks.toml diff --git a/Makefile b/Makefile index 2f75b75..3936e77 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ install: install -m 0644 $(TARGET_DIR)/man/acmed.toml.5.gz $(DESTDIR)$(MAN5DIR)/acmed.toml.5.gz; \ install -m 0644 acmed/config/acmed.toml $(DESTDIR)$(SYSCONFDIR)/acmed/acmed.toml; \ install -m 0644 acmed/config/default_hooks.toml $(DESTDIR)$(SYSCONFDIR)/acmed/default_hooks.toml; \ + install -m 0644 acmed/config/nginx_hooks.toml $(DESTDIR)$(SYSCONFDIR)/acmed/nginx_hooks.toml; \ install -m 0644 acmed/config/letsencrypt.toml $(DESTDIR)$(SYSCONFDIR)/acmed/letsencrypt.toml; \ fi if test -f "$(TARGET_DIR)/tacd"; then \ diff --git a/acmed/config/acmed.toml b/acmed/config/acmed.toml index a541a34..4f11899 100644 --- a/acmed/config/acmed.toml +++ b/acmed/config/acmed.toml @@ -5,6 +5,7 @@ include = [ "default_hooks.toml", + "nginx_hooks.toml", "letsencrypt.toml", #"my_server.toml", ] diff --git a/acmed/config/nginx_hooks.toml b/acmed/config/nginx_hooks.toml new file mode 100644 index 0000000..1291707 --- /dev/null +++ b/acmed/config/nginx_hooks.toml @@ -0,0 +1,130 @@ +# Copyright (c) 2021 Rodolphe Bréard +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. + +# ------------------------------------------------------------------------ +# ACMEd hooks adapting nginx configuration +# You should not edit this file since it may be overridden by a newer one. +# ------------------------------------------------------------------------ + + +### +# nginx file storing challenge root in "/etc/nginx/conf.d/{{challenge-location}}/" +# env: NGINX_CONFDIR -> /etc/nginx/conf.d +# env: NGINX_CHALLENGE_LOCTION -> 001-challenge-letsencrypt.conf +### + +[[hook]] +name = "nginx-config-challenge-location-chmod" +type = ["challenge-http-01", "post-operation"] +cmd = "chmod" +args = [ + "ug+rw", + "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_CHALLENGE_LOCATION}}{{env.NGINX_CHALLENGE_LOCATION}}{{else}}001-challenge-letsencrypt.conf{{/if}}" +] +allow_failure = true + +[[hook]] +name = "nginx-config-challenge-location-create" +type = ["challenge-http-01", "post-operation"] +cmd = "cat" +args = [ "-" ] +stdin_str = """### +# Let's Encrypt: acme-challenge location +### + +location ^~ /.well-known/acme-challenge/ { + allow all; + root "{{#if env.HTTP_ROOT}}{{env.HTTP_ROOT}}{{else}}/var/lib/acmed/domains{{/if}}"; + default_type "text/plain"; + try_files $uri =404; +} +""" +stdout = "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_CHALLENGE_LOCATION}}{{env.NGINX_CHALLENGE_LOCATION}}{{else}}001-challenge-letsencrypt.conf{{/if}}" + +[[hook]] +name = "nginx-config-challenge-location-echo" +type = ["challenge-http-01", "post-operation"] +cmd = "echo" +args = [ + "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_CHALLENGE_LOCATION}}{{env.NGINX_CHALLENGE_LOCATION}}{{else}}001-challenge-letsencrypt.conf{{/if}}" +] +allow_failure = true + +[[group]] +name = "nginx-config-challenge-location" +# hook execution in order of definition +hooks = [ + "nginx-config-challenge-location-create", + "nginx-config-challenge-location-echo", + "nginx-config-challenge-location-chmod" +] + +### +# nginx reference to TLS certificates +# env NGINX_TLS_CERTIFICATE -> 002-tls-certificates.conf +# global: {{certificates_directory}} +# certificate: {{name}}_{{key_type}}.{{file_type}}.{{ext}} +### + +[[hook]] +name = "nginx-config-certificate-location-chmod" +type = ["challenge-http-01", "post-operation"] +cmd = "chmod" +args = [ + "ug+rw", + "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_TLS_CERTIFICATE}}{{env.NGINX_TLS_CERTIFICATE}}{{else}}002-tls-certificates.conf{{/if}}" +] +allow_failure = true + +[[hook]] +name = "nginx-config-certificate-location-create" +type = ["challenge-http-01", "post-operation"] +cmd = "cat" +args = [ "-" ] +## TODO +# make following handlebars accessible in post-operation +# ssl_certificate {{certificates_directory}}/{{name}}_{{key_type}}.crt.{{ext}}; +# ssl_certificate_key {{certificates_directory}}/{{name}}_{{key_type}}.pk.{{ext}}; +# workaround: define a env:ACMED_CERTS pointing to {{certificates_directory}} +## +stdin_str = """### +# Let's Encrypt TLS certificats +### + +ssl_certificate {{#if env.ACMED_CERTS}}{{env.ACMED_CERTS}}{{else}}/var/lib/acmed/certs{{/if}}/{{identifiers.[0]}}_{{key_type}}.crt.pem; # managed by ACMEd +ssl_certificate_key {{#if env.ACMED_CERTS}}{{env.ACMED_CERTS}}{{else}}/var/lib/acmed/certs{{/if}}/{{identifiers.[0]}}_{{key_type}}.pk.pem; # managed by ACMEd + +ssl_session_cache shared:le_nginx_SSL:10m; +ssl_session_timeout 1440m; +ssl_session_tickets off; + +ssl_protocols TLSv1.2 TLSv1.3; +ssl_prefer_server_ciphers off; + +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-PO +LY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; +""" +stdout = "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_TLS_CERTIFICATE}}{{env.NGINX_TLS_CERTIFICATE}}{{else}}002-tls-certificates.conf{{/if}}" +allow_failure = true + +[[hook]] +name = "nginx-config-certificate-location-echo" +type = ["challenge-http-01", "post-operation"] +cmd = "echo" +args = [ + "{{#if env.NGINX_CONFDIR}}{{env.NGINX_CONFDIR}}{{else}}/etc/nginx/conf.d{{/if}}/{{#if env.NGINX_TLS_CERTIFICATE}}{{env.NGINX_TLS_CERTIFICATE}}{{else}}002-tls-certificates.conf{{/if}}" +] +allow_failure = true + +[[group]] +name = "nginx-config-certificate-location" +# hook execution in order of definition +hooks = [ + "nginx-config-certificate-location-create", + "nginx-config-certificate-location-echo", + "nginx-config-certificate-location-chmod" +] diff --git a/contrib/acmed.service b/contrib/acmed.service index 228a449..0b765ec 100644 --- a/contrib/acmed.service +++ b/contrib/acmed.service @@ -6,13 +6,22 @@ After=network.target User=acmed Group=acmed -# Working directory equals to User-Home -#WorkingDirectory=/var/lib/acmed -WorkingDirectory=/etc/acmed +# Root directory used to store challenges +# prefered: preset HTTP_ROOT env in hooks +#Environment="HTTP_ROOT=/var/lib/acmed/domains" +#Environment="NGINX_CONFDIR=/etc/nginx/conf.d" +#Environment="NGINX_CHALLENGE_LOCATION="001-challenge-letsencrypt.conf" +#Environment="NGINX_TLS_CERTIFICATE="001-tls-certificates.conf" + + +# ACMEd home directory +WorkingDirectory=/var/lib/acmed + +# ACMEd runtime diretory storing pid file RuntimeDirectory=acmed # daemon handling: start, stop, timeouts -#ExecStart=/usr/bin/acmed --foreground --pid-file /run/acmed/acmed.pid --log-level debug --log-stderr +#ExecStart=/usr/bin/acmed --foreground --pid-file /run/acmed/acmed.pid --log-level trace --log-stderr ExecStart=/usr/bin/acmed --foreground --pid-file /run/acmed/acmed.pid --log-level warn TimeoutStartSec=3 TimeoutStopSec=5 @@ -20,7 +29,7 @@ Restart=on-failure KillSignal=SIGINT # Sandboxing: reduce privileges on filesystem and kernel-space -# restrict write access to acmed's directories with variable data +# restrict write access to directories, where acmed will store variable data NoNewPrivileges=yes PrivateDevices=yes PrivateTmp=yes @@ -30,7 +39,7 @@ ProtectHostname=yes ProtectKernelTunables=yes ProtectKernelLogs=yes ProtectSystem=strict -ReadWritePaths=/etc/acmed /var/lib/acmed +ReadWritePaths=/etc/acmed /etc/nginx/conf.d /var/lib/acmed RestrictRealtime=yes RestrictSUIDSGID=yes SystemCallFilter=@system-service diff --git a/contrib/tmpfiles.d/acmed.conf b/contrib/tmpfiles.d/acmed.conf index 0ae94fe..a24ee5d 100644 --- a/contrib/tmpfiles.d/acmed.conf +++ b/contrib/tmpfiles.d/acmed.conf @@ -5,9 +5,12 @@ ### #Type Path Mode User Group Age Argument -d /run/acmed 0755 acmed acmed - - -f /run/acmed/acmed.pid 0644 acmed acmed - - +d /run/acmed 0755 acmed acmed - - +f /run/acmed/acmed.pid 0644 acmed acmed - - d /var/lib/acmed 0755 acmed acmed - - d /var/lib/acmed/accounts 0700 acmed acmed - - d /var/lib/acmed/certs 0755 acmed acmed - - +d /var/lib/acmed/domains 0755 acmed acmed - - + +d /etc/nginx/conf.d 0775 root acmed - - \ No newline at end of file