diff options
Diffstat (limited to 'config-parabola-mgmt-certbot.PKGBUILD')
-rw-r--r-- | config-parabola-mgmt-certbot.PKGBUILD | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/config-parabola-mgmt-certbot.PKGBUILD b/config-parabola-mgmt-certbot.PKGBUILD new file mode 100644 index 0000000..0573889 --- /dev/null +++ b/config-parabola-mgmt-certbot.PKGBUILD @@ -0,0 +1,239 @@ +. ${BUILDFILE%/*}/common.sh +pkgver=20180826 + +package() { +preamble +# #### SSL + +# Use the [certbot][] ACME client to get certificates from +# [Let's Encrypt][]. +# +# [certbot]: https://www.parabola.nu/packages/community/any/certbot/ +# [Let's Encrypt]: https://letsencrypt.org/ + +depends+=(certbot dialog) + +# All domains handled by the server are shoved in as Subject +# Alternative Names in a single certificate. This makes configuring +# nginx easier. + +# ##### keys user and group + +# Files affected manually: +# +# * `/etc/passwd` +# * `/etc/shadow` +# * `/etc/group` +# * `/etc/gshadow` +# * `/etc/letsencrypt` +# * `/var/lib/letsencrypt` +# * `/var/log/letsencrypt` +# +# In order to run certbot as a non-root user, the keys user and group +# have been created: +# +# useradd --system --user-group --no-create-home --home-dir /etc/ssl --shell /usr/bin/nologin keys +# chown -R keys:keys /etc/letsencrypt /var/log/letsencrypt /var/lib/letsencrypt +# chmod 750 /etc/letsencrypt/archive /etc/letsencrypt/live +# +# The associated keys group allows users to read the (private) keys in +# /etc/letsencrypt/live. + +# ##### issuance, renewal, and installation + +# Unlike acmetool, certbot doesn't have an easy way of saying "please +# add this domain as a Subject Alternative Name". You have to re-run +# the same (long) command to get the cert, but with the domain added. +# So, I've encapsulated this into the script +# `/etc/ssl/misc/certbot-get`. Edit `/etc/ssl/misc/certbot-get.d/` to +# manipulate the list of domains, then run the script. +install -d etc/ssl/misc/certbot-get.d +add-file -m755 etc/ssl/misc/certbot-get <<<'#!/bin/bash +{ + confdir="$(readlink -f -- "$0.d")" + set -eu + cd / + + # The first name listed should be the canonical host name + domains=( + $(hostname -f) + $(find -L "$confdir" -type f -executable -exec {} \;) + ) + + if [[ "`whoami`" != '\''keys'\'' ]]; then + >&2 printf '\''%q: This script must be run as user `%s'\''\'\'''\''\n'\'' "$0" keys + exit 1 + fi + + msg=( + + Our "\`${0##*/}\`" script is used to '\''*add*'\'' or + '\''*remove*'\'' certificates\; use '\''`certbot renew`'\'' to + renew them. To use "${0##*/}," edit "\`${0##*/}.d/\`" to + manipulate the list of domains, '\''then'\'' run it to get a + new certificate with a new Subject Alternative Name field + matching the new list of domains. + + $'\''\n\n'\''Are you sure that you are ready to run this? + It will eat into the "Let'\''s Encrypt" usage limit. + + ) + + dialog --yesno "${msg[*]}" '\'''\'' '\'''\'' || { echo; exit 0; } + + cmd=( + certbot certonly + --cert-name "${domains[0]}" + --email "`whoami`@${domains[0]}" + --webroot -w /var/lib/letsencrypt + ) + + for domain in "${domains[@]}"; do + cmd+=(-d "$domain") + done + + umask 0027 + "${cmd[@]}" + sudo /etc/ssl/misc/certbot-hook +}' + +# Renewal, however, is very simple. It is handled by +# `certbot-renew.service` (triggered by the associated `.timer`). It +# runs `certbot renew` with a couple of flags. +add-file etc/systemd/system/certbot-renew.timer <<EOF +[Unit] +Description=Daily renewal of Let's Encrypt's certificates + +[Timer] +OnCalendar=daily +Persistent=true + +[Install] +WantedBy=timers.target +EOF +add-file etc/systemd/system/certbot-renew.service <<EOF +[Unit] +Description=Let's Encrypt certificate renewal + +[Service] +Type=oneshot +ExecStart=/usr/bin/certbot renew --quiet --renew-hook 'sudo /etc/ssl/misc/certbot-hook' + +User=keys +UMask=0027 +EOF +add-unit etc/systemd/system/timers.target.wants/certbot-renew.timer + +# Both `certbot-get` and `certbot-renew.service` prove ownership of +# the domain via the `http-01` challenge. `/etc/nginx/nginx.conf` +# includes `/etc/nginx/snippets/ssl.conf`, which has a `server{}` +# block that handles ACME http-01 challenges. + +# Both `certbot-get` and `certbot-renew.service` have been written to +# run `sudo /etc/ssl/misc/certbot-hook` after certificates have been +# updated, and `sudo` has been configured to allow the keys user to do +# this without a password. Right now `certbot-hook` just runs +# `systemctl reload nginx.service`. +add-file -m755 etc/ssl/misc/certbot-hook <<EOF +#!/bin/bash +systemctl reload nginx.service +EOF +install -dm750 etc/sudoers.d +add-file etc/sudoers.d/10-certbot <<EOF +keys ALL=(ALL) NOPASSWD: /etc/ssl/misc/certbot-hook +EOF + +# ##### other + +# Files affected manually: +# +# * `/etc/nginx/nginx.conf` +# * `/etc/ssl/private/dhparam-2048.pem` + +# `nginx.conf` includes `snippets/ssl.conf`, which is primarily based +# on the output of [Mozilla Security's recommended web server +# configuration generator][0]. It has had the main SSL information +# promoted to be directly into the `http{}` block, instead of having +# to be in each `server{}` block. The HTTP->HTTPS redirector has had +# an exception added to it to have it respond to ACME http-01 +# challenges. +# +# [0]: https://mozilla.github.io/server-side-tls/ssl-config-generator/ +add-file etc/nginx/snippets/ssl.conf <<EOF +# -*- Mode: nginx; nginx-indent-level: 8; indent-tabs-mode: t -*- + +# This is based on Mozilla Security's recommended web server +# configuration generator[1]: +# Generated date: 2016-06-28 +# Server: Nginx +# Clients: Intermediate +# Server Version: 1.10.1 +# OpenSSL Version: 1.0.2h +# HSTS Enabled: yes +# +# [1]: https://mozilla.github.io/server-side-tls/ssl-config-generator/ +# +# Obviously, all of the '/path/to/' paths have been filled in. The +# 'resolver' line has been commented out. The SSL information has +# been promoted to be in the http{} block directly, instead of having +# to be in each server{} block. The HTTP->HTTPS redirector has had +# ACME support added. + +# Redirect everything on port 80 to HTTPS +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name _; + + # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. + location / { return 301 https://\$host\$request_uri; } + + # Except for ACME http-01 validations + location /.well-known/acme-challenge { + root /var/lib/letsencrypt; + default_type "text/plain"; + } +} + +# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate +ssl_certificate /etc/ssl/private/myhostname/fullchain.pem; +ssl_certificate_key /etc/ssl/private/myhostname/privkey.pem; +ssl_session_timeout 1d; +ssl_session_cache shared:SSL:50m; +ssl_session_tickets off; + +# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits +ssl_dhparam /etc/ssl/private/dhparam-2048.pem; + +# intermediate configuration. tweak to your needs. +ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; +ssl_prefer_server_ciphers on; + +# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) +add_header Strict-Transport-Security max-age=15768000; + +# OCSP Stapling --- +# fetch OCSP records from URL in ssl_certificate and cache them +ssl_stapling on; +ssl_stapling_verify on; + +## verify chain of trust of OCSP response using Root CA and Intermediate certs +#ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates; + +#resolver <IP DNS resolver>; +EOF + +# Because certbot is only configured to use http-01 challenges, the +# all challenges happen over pain HTTP, which means that the +# configurations for each subdomain (which only serve over +# HTTPS/HTTP2) do not need to include anything about ACME or SSL +# (other than mentioning `ssl` in the `listen` directive). + +# `ssl.conf` needs to refer to a dhparam PEM file. This has been +# generated with the command +# +# openssl dhparam -out /etc/ssl/private/dhparam-2048.pem 2048 + +postamble +} |