From da8ae9e511a94d828877d6c12467f4dc8e2a4383 Mon Sep 17 00:00:00 2001
From: W-Mark Kubacki <wmark@hurrikane.de>
Date: Wed, 11 May 2016 20:48:47 +0200
Subject: [PATCH] systemd: Run caddy with even less privileges and more
 confined

The exemplary unit file for systemd is intentionally redundant at times, for
example dropping privileges which an unprivileged user "www-data" did not have
in the first place: To aid as fallback in case the file gets copied and an
operator setting UID to 0 (which reportedly happened in the past).
---
 dist/init/linux-systemd/README.md      | 25 +++++++++++++
 dist/init/linux-systemd/caddy.service  | 50 ++++++++++++++++++++++++++
 dist/init/linux-systemd/caddy@.service | 23 ------------
 3 files changed, 75 insertions(+), 23 deletions(-)
 create mode 100644 dist/init/linux-systemd/caddy.service
 delete mode 100644 dist/init/linux-systemd/caddy@.service

diff --git a/dist/init/linux-systemd/README.md b/dist/init/linux-systemd/README.md
index 61ffb3381..908954c6d 100644
--- a/dist/init/linux-systemd/README.md
+++ b/dist/init/linux-systemd/README.md
@@ -4,6 +4,25 @@ Please do not hesitate to ask if you have any questions.
 
 ## Quickstart
 
+The provided unit file assumes that you want to run caddy as `www-data` and group `www-data`,
+both having UID and GID 33 here.
+Adjust this to your liking according to the preferences of you Linux distribution!
+
+```bash
+groupadd -g 33 www-data
+useradd \
+  -g www-data --no-user-group \
+  --home-dir /var/www --no-create-home \
+  --shell /usr/sbin/nologin \
+  --system --uid 33 www-data
+
+mkdir /etc/caddy
+chown -R root:www-data /etc/caddy
+mkdir /etc/ssl/caddy
+chown -R www-data:root /etc/ssl/caddy
+chmod 0770 /etc/ssl/caddy
+```
+
 - Install the unit configuration file: `cp caddy.service /etc/systemd/system/`
 - Reload the systemd daemon: `systemctl daemon-reload`
 - Make sure to [configure](#configuration) the service unit before starting caddy.
@@ -52,3 +71,9 @@ sudo -u www-data -g www-data -s \
   `systemctl kill --signal=USR1 caddy.service`
 - If you have more files that start with `caddy` – like a `caddy.timer`, `caddy.path`, or `caddy.socket` – then it is important to append `.service`.
   Although if `caddy.service` is all you have, then you can just use `caddy` without any extension, such as in: `systemctl status caddy`
+
+- You can make your other certificates and private key files accessible to a user `www-data` by command `setfacl`, if you must:
+
+```bash
+setfacl -m user:www-data:r-- /etc/ssl/private/my.key
+```
diff --git a/dist/init/linux-systemd/caddy.service b/dist/init/linux-systemd/caddy.service
new file mode 100644
index 000000000..aa5020ebd
--- /dev/null
+++ b/dist/init/linux-systemd/caddy.service
@@ -0,0 +1,50 @@
+[Unit]
+Description=Caddy HTTP/2 web server
+Documentation=https://caddyserver.com/docs
+After=network-online.target
+Wants=network-online.target systemd-networkd-wait-online.service
+
+[Service]
+Restart=on-failure
+
+; User and group the process will run as.
+User=www-data
+Group=www-data
+
+; Letsencrypt-issued certificates will be written to this directory.
+Environment=HOME=/etc/ssl/caddy
+
+; Always set "-root" to something safe in case it gets forgotten in the Caddyfile.
+ExecStart=/usr/bin/caddy -log stdout -agree=true -conf=/etc/caddy/Caddyfile -root=/var/tmp
+ExecReload=/bin/kill -USR1 $MAINPID
+
+; Limit the number of file descriptors; see `man systemd.exec` for more limit settings.
+LimitNOFILE=1048576
+; Unmodified caddy is not expected to use more than that.
+LimitNPROC=64
+
+; Use private /tmp and /var/tmp, which are discarded after caddy stops.
+PrivateTmp=true
+; Use a minimal /dev
+PrivateDevices=true
+; Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
+ProtectHome=true
+; Make /usr, /boot, /etc and possibly some more folders read-only.
+ProtectSystem=full
+; … except /etc/ssl/caddy, because we want Letsencrypt-certificates there.
+;   This merely retains r/w access rights, it does not add any new. Must still be writable on the host!
+ReadWriteDirectories=/etc/ssl/caddy
+
+; Drop all other capabilities. Important if you run caddy as privileged user (which you should not).
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+; … but permit caddy to open ports reserved for system services.
+;   This could be redundant here, but is needed in case caddy runs as nobody:nogroup.
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+; … and prevent gaining any new privileges.
+NoNewPrivileges=true
+
+; Caveat: Some plugins need additional capabilities. Add them to both above lines.
+; - plugin "upload" needs: CAP_LEASE
+
+[Install]
+WantedBy=multi-user.target
diff --git a/dist/init/linux-systemd/caddy@.service b/dist/init/linux-systemd/caddy@.service
deleted file mode 100644
index 0baaac108..000000000
--- a/dist/init/linux-systemd/caddy@.service
+++ /dev/null
@@ -1,23 +0,0 @@
-; see `man systemd.unit` for configuration details
-; the man section also explains *specifiers* `%x`
-
-[Unit]
-Description=Caddy HTTP/2 web server %I
-Documentation=https://caddyserver.com/docs
-After=network-online.target
-Wants=network-online.target
-Wants=systemd-networkd-wait-online.service
-
-[Service]
-; run user and group for caddy
-User=%i
-Group=http
-ExecStart=/usr/bin/caddy -agree=true -conf=/etc/caddy/Caddyfile
-Restart=on-failure
-; create a private temp folder that is not shared with other processes
-PrivateTmp=true
-; limit the number of file descriptors, see `man systemd.exec` for more limit settings
-LimitNOFILE=8192
-
-[Install]
-WantedBy=multi-user.target