Christine Dodrill - Blog - Contact - Resume | GraphViz - When Then Zen

Site to Site WireGuard: Part 4 - HTTPS

This is the fourth post in my Site to Site WireGuard VPN series. You can read the other articles here:

In this article, we are going to install Caddy and set up the following:

HTTPS and Caddy

Caddy is a general-purpose HTTP server. One of its main features is automatic Let’s Encrypt support. We are using it here to serve HTTPS because it has a very, very simple configuration file format.

Caddy doesn’t have a stable package in Ubuntu yet, but it is fairly simple to install it by hand.

Installing Caddy

One of the first things you should do when installing Caddy is picking the list of extra plugins you want in addition to the core ones. I generally suggest the following plugins:

First we are going to need to download Caddy (please do this as root):

curl >
bash -s personal http.cors,http.git,http.supervisor
chown root:root /usr/local/bin/caddy
chmod 755 /usr/local/bin/caddy

These permissions are set as such:

Facet Read Write Directory Listing
User (root) Yes Yes Yes
Group (root) Yes No Yes
Others Yes No Yes

In order for Caddy to bind to the standard HTTP and HTTPS ports as non-root (this is a workaround for the fact that Go can’t currently drop permissions with suid() cleanly), run the following:

setcap 'cap_net_bind_service=+eip' /usr/local/bin/caddy

Caddy expects configuration file/s to exist at /etc/caddy, so let’s create the folders for them:

mkdir -p /etc/caddy
touch /etc/caddy/Caddyfile
chown -R root:www-data /etc/caddy

Let’s Encrypt Certificate Permissions

Caddy’s systemd unit expects to be able to create new certificates at /etc/ssl/caddy:

mkdir -p /etc/ssl/caddy
chown -R www-data:root /etc/ssl/caddy
chmod 770 /etc/ssl/caddy

These permissions are set as such:

Facet Read Write Directory Listing
User (www-data) Yes Yes Yes
Group (root) Yes Yes Yes
Others No No No

This will allow only Caddy and root to manage certificates in that folder.

Custom CA Certificate Permissions

In the last post, custom certificates were created at /srv/within/certs. Caddy is going to need to have the correct permissions in order to be able to read them.

chmod -R 750 .
chown -R root:www-data .
chmod 600 minica-key.pem

Then mark it executable:

chmod +x

These permissions are set as such:

Facet Read Write Execute/Directory Listing
User (root) Yes Yes Yes
Group (www-data) Yes No Yes
Others No No No

This will allow Caddy to be able to read the certificates later in the post. Run this after certificates are created.

cd /srv/within/certs

HTTP Root Permissions

I dypically store all of my websites under /srv/http/ To create a folder like this:

mkdir -p /srv/http
chown www-data:www-data /srv/http
chmod 755 /srv/http

These permissions are set as such:

Facet Read Write Directory Listing
User (www-data) Yes Yes Yes
Group (www-data) Yes No Yes
Others Yes No Yes


To install the upstream systemd unit, run the following:

curl -L \
      | sed "s/;CapabilityBoundingSet/CapabilityBoundingSet/" \
      | sed "s/;AmbientCapabilities/AmbientCapabilities/" \
      | sed "s/;NoNewPrivileges/NoNewPrivileges/" \
      | tee /etc/systemd/system/caddy.service
chown root:root /etc/systemd/system/caddy.service
chmod 744 /etc/systemd/system/caddy.service
systemctl daemon-reload
systemctl enable caddy.service

These permissions are set as such:

Facet Read Write Execute
User (root) Yes Yes Yes
Group (root) Yes No No
Others Yes No No

This will also configure Caddy to start on boot.

* Configure Caddy for static file serving for aloha.pele
    * root directive
    * browse directive
* Link to Caddy documentation

Configure aloha.pele

In the last post, we created the domain and TLS certificates for aloha.pele. Let’s create a website for it.

Open /etc/caddy/Caddyfile and add the following:

# /etc/caddy/Caddyfile

aloha.pele:80 {
  tls off
  redir / https://aloha.pele:443

aloha.pele:443 {
  tls /srv/within/certs/aloha.pele/cert.pem /srv/within/certs/aloha.pele/key.pem
  internal /templates
  markdown / {
    template templates/page.html
  ext .md
  browse /
  root /srv/http/aloha.pele

And create /srv/http/aloha.pele/templates:

mkdir -p /srv/http/aloha.pele/templates
chown -R www-data:www-data /srv/http/aloha.pele/templates

And open /srv/http/aloha.pele/templates/page.html:

<!-- /srv/http/aloha.pele/templates/page.html -->

    <title>{{ .Doc.title }}</title>
      main {
        max-width: 38rem;
        padding: 2rem;
        margin: auto;
        <a href="/">Aloha</a>
      {{ .Doc.body }}

This will give a nice simple style kind of like this using Caddy’s built-in markdown templating support. Now create /srv/http/aloha.pele/

<!-- /srv/http/aloha.pele/ -->

# Aloha!

This is an example page, but it doesn't have anything yet. If you see me, HTTPS is probably working.

Now let’s enable and test it:

systemctl restart caddy
systemctl status caddy

If Caddy shows as running, then testing it via LibTerm should work:

curl -v https://aloha.pele

URL Shortener

I have created a simple URL shortener backend on my GitHub. I personally have it accessible at https://g.o for my internal network. It is very simple to configure:

Environment Variable Value
THEME solarized.css (or gruvbox.css)

surl requires a SQLite database to function. To store it, create a docker volume:

docker volume create surl

And to create the surl container and register it for automatic restarts:

docker run --name surl -dit -p \
  --restart=always \
  -e DOMAIN=g.o \
  -e THEME=solarized.css \
  -v surl:/data xena/surl:v0.4.0

Now create a DNS record for g.o.:


;; URL shortener
g.o. IN CNAME oho.pele.

And a TLS certificate:

cd /srv/within/certs
minica -domains g.o

And add Caddy configuration for it:

# /etc/caddy/Caddyfile

g.o:80 {
  tls off
  redir / https://g.o

g.o:443 {
  tls /srv/within/certs/g.o/cert.pem /srv/within/certs/g.o/key.pem
  proxy /

Now restart Caddy to load the configuration and make sure it works:

systemctl restart caddy
systemctl status caddy

And open https://g.o on your iOS device:

An image of the URL shortener in action

You can use the other directives in the Caddy documentation to do more elaborate things. When Then Zen is hosted completely with Caddy using the markdown directive; but even this is ultimately a simple configuration.

This seems like enough for this time. Next time we are going to approach adding other devices of yours to this network: iOS, Android, macOS and Linux.

Please give me feedback on my approach to this. I also have a Patreon and a Ko-Fi in case you want to support this series. I hope this is useful to you all in some way. Stay tuned for the future parts of this series as I build up the network infrastructure from scratch. If you would like to give feedback on the posts as they are written, please watch this page for new pull requests.

Be well. The sky is the limit, Creator!