-- Leo's gemini proxy

-- Connecting to gem.twunk.uk:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

This Gemini Server (part two)


Part One


Once I got the basic thing up and running I started working on deployment a little. My little VPS runs Debian 11. I don't use Docker currently, because Docker doesn't (last time I checked) support cleanly integrating with nftables. Docker integrates with iptables and fiddles with your iptables rules in order to route the "exposed" ports on your containers.


Anyway, I use nftables on my little server, with a simple custom ruleset. Docker doesn't play nicely with this. Hence I don't use docker.


I do use systemd, and the stuff running on the server is all set up as systemd services. This starts stuff when the server boots. Systemd is capable of doing various containerisation/sandboxing actions as part of managing its services. It also has this thing called "socket activation", which means that you can set it up so that systemd itself will create the listening socket for you, and then pass it into your service as an already-open file descriptor.


Systemd can also open a file to pass to the service as its stdin (fd 0). In fact more recent systemd (v253+) lets you open other files to pass in as other file descriptors, but previously it only had a mechanism for redirecting stdin like this.


This means I can do a few things with my systemd service configuration to try to make the server a little more secure:


Run as an unprivileged user (this is obvious; every service should run as its own unprivileged user, that's the most basic thing)

Open the certificate file (which has the server's x.509 cert and also the private key) and pass this to the server as its fd 0. This means the server doesn't need to be able to find the cert/key file in its view of the file system.

Open a socket listening on port 1965 and pass it to the server as fd 3. This is used for systemd 'socket activation' where systemd will start the server when the first connection attempt is made. It also means the server doesn't actually need _any_ network access. It can run in an empty network namespace.

Run in a chroot and mount namespace, set up so the server can't actually see anything except the data files that it's going to serve.


Anyway, here's the bucket.service:


[Unit]
Description=Bucket (Gemini sever)
After=network.target network-online.target
Requires=network-online.target

[Service]
User=bucket
Group=bucket
Type=exec
StandardInput=file:/var/lib/bucket/gem.twunk.uk.pem
ExecStart=/usr/local/bin/bucket -d gem.twunk.uk --cert-pem-fd 0 --listen-sock-fd 3 -s /var/lib/serverdata/gem
Restart=always
WorkingDirectory=/var/lib/serverdata
Environment=USER=bucket
Environment=HOME=/var/lib/bucket
Environment=RUST_LOG=info,bucket=debug
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512

CapabilityBoundingSet=
NoNewPrivileges=true
PrivateDevices=true
PrivateNetwork=true
PrivateTmp=true
PrivateUsers=true
ProtectClock=true
ProtectHome=true
ProtectHostname=true
ProtectSystem=strict
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
SystemCallArchitectures=native

# Set up a somewhat restricted filesystem view.
# Note this doesn't even include the server's own config directory,
# since it doesn't need to read any files from there by path.
TemporaryFileSystem=/var:ro
BindReadOnlyPaths=/var/lib/serverdata/gem

[Install]
WantedBy=multi-user.target

And here's the socket unit used for socket activation:


[Unit]
Description=Bucket (Gemini sever) socket

[Socket]
ListenStream=1965

[Install]
WantedBy=sockets.target

I'm not sure that I've got the right dependencies specified yet, and I haven't looked through the other possible socket unit options. And I haven't actually looked through _all_ the service options either (there are so many).


Anyway, I don't hugely like this as it seems like a "start open and lock down many individual features" design rather than a "start with nothing and add just the pieces needed by the server" design. Nevertheless, it's hopefully a little more secure than it was. At least as a second line of defence.

-- Response ended

-- Page fetched on Sun May 12 02:37:43 2024