r/mod_md: Configure Apache for ACME certificates

Apache supports fetching server certificates via ACME (e.g. from Let's
Encrypt) using a new module called _mod_md_.  Configuring the module is
fairly straightforward, mostly consisting of `MDomain` directives that
indicate what certificates to request.  Unfortunately, there is one
rather annoying quirk: the certificates it obtains are not immediately
available to use, and the server must be reloaded in order to start
using them.  Fortunately, the module provides a notification mechanism
via the `MDNotifyCmd` directive, which will run the specified command
after obtaining a certificate.  The command is executed with the
privileges of the web server, which does not have permission to reload
itself, so we have to build in some indirection in order to trigger the
reload: the notification runs a script that creates an empty file in the
server's state directory; systemd is watching for that file to be
created, then starts another service unit to trigger the actual reload,
then removes trigger file.

Website roles, etc. that want to switch to using _mod_md_ to manage
their certificates should depend on this role and add an `MDomain`
directive to their Apache configuration file fragments.
unifi-restore
Dustin 2025-07-23 09:55:53 -05:00
parent 9690234203
commit 0eb6220672
9 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,5 @@
mod_md_contact_email: dustin@hatch.name
mod_md_private_keys: secp256r1
mod_md_status_enabled: true
mod_md_status_config: |-
Allow from 172.30.0.0/16

View File

@ -0,0 +1 @@
mod_md_status_enabled: false

View File

@ -0,0 +1,8 @@
[Unit]
Description=Apache auto-reload watch
[Path]
PathExists=/var/lib/httpd/reload
[Install]
WantedBy=paths.target

View File

@ -0,0 +1,9 @@
[Unit]
Description=Reload Apache HTTPD on demand
After=httpd.service
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl reload httpd
ExecStopPost=+-/bin/rm -f /var/lib/httpd/reload
CapabilityBoundingSet=

View File

@ -0,0 +1,5 @@
#!/bin/sh
logger -p local7.debug "$0 $*"
logger -p local7.info 'Signaling systemd to reload httpd'
touch /var/lib/httpd/reload

View File

@ -0,0 +1,2 @@
dependencies:
- systemd-base

View File

@ -0,0 +1,54 @@
- name: ensure mod_md is installed
package:
name: mod_md
state: present
tags:
- install
- name: ensure mod_md is configured
template:
src: mod_md.conf.j2
dest: /etc/httpd/conf.d/01-mod_md.conf
owner: root
group: root
mode: u=rw,go=r
notify:
- reload httpd
tags:
- config
- name: ensure mod_md notify script is installed
copy:
src: md-notify.sh
dest: /usr/local/libexec/md-notify
owner: root
group: root
mode: u=rwx,go=rx
tags:
- notify-script
- name: ensure httpd-reload systemd units are installed
copy:
src: '{{ item }}'
dest: /etc/systemd/system/{{ item }}
owner: root
group: root
mode: u=rw,go=r
loop:
- httpd-reload.path
- httpd-reload.service
notify: reload systemd
tags:
- systemd
- name: ensure httpd-reload.path unit is enabled
systemd:
name: httpd-reload.path
enabled: true
tags:
- service
- name: ensure httpd-reload.path unit is active
systemd:
name: httpd-reload.path
state: started
tags:
- service

View File

@ -0,0 +1,16 @@
MDCertificateAgreement accepted
MDContactEmail {{ mod_md_contact_email }}
MDNotifyCmd /usr/local/libexec/md-notify
{% if mod_md_private_keys is defined %}
MDPrivateKeys {{ mod_md_private_keys }}
{% endif %}
{% if mod_md_status_enabled %}
<Location "/md-status">
SetHandler md-status
{% if mod_md_status_config %}
{{ mod_md_status_config | indent(2) }}
{% endif %}
</Location>
{% endif %}

View File

@ -3,6 +3,9 @@
apache_default_ssl_vhost: false
roles:
- apache
- role: mod_md
tags:
- mod_md
- role: formsubmit
tags: formsubmit
- role: websites/pyrocufflink.net