💾 Archived View for dyn.fussycoder.ninja › personal › linux › geminiserver.gmi captured on 2022-07-16 at 14:23:20. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
For security reasons, I stopped running a gemini server on my main PC, and opted instead to use an old Raspberry Pi 3 I had found instead.
The idea is that this server would contain only non-critical files that aren't a huge deal if they were to be leaked online.
Security measures that I hope to implement are:
The outcome is that this is, hopefully, slightly more secure than a typical gemini server hosted on a home PC.
I installed the aarch64 version, but I had to figure out how to install this without a keyboard or display connected at all.
It turns out this is quite well documented on the wiki.
I knew that this system supported aarch64 because my board is the "B+" version, previous models are probably not going to support aarch64.
Raspberry Pi Headless Installation
Set up a root account with a secure password.
Then of course, set up a "gemini" server account and an admin account. We will use these to actually run the service later.
This is... Remarkably trivial:
apk add rsync
Again, very simple. I like alpine!
apk add bubblewrap
Using it is pretty simple, but one gotcha is that the program interpreter must exist, so in alpine, that means ensuring that `/lib` exists.
Example:
bwrap --unshare-all --ro-bind /lib /lib --ro-bind /bin /bin ash
Another example is provided by "ericonr" at:
ericonr's example of running gemini in bubblewrap
ssh is *constantly* being attacked on the internet, so it is sensible to lock it down tight, by editing /etc/ssh/sshd_config
This is less trivial, we ideally don't want the build system installed on the rpi system.
For Go, this is very very simple:
env GOOS=linux GOARCH=arm64 go build
For haskell, this is far less simple. We need to set up an arm64 emulator and install haskell in there.
Update: Actually setting up haskell on an aarch64 musl based linux system is non-trivial.
Writing an init system service is documented here for alpine linux:
And even better, the authoritative docsa are at:
With that in mind, the following seems sensible:
#!/sbin/openrc-run command=/usr/bin/bwrap command_args="--unshare-all --share-net --ro-bind /home/gemadmin/bin /bin --ro-bind /home/gemadmin/certs /certs --ro-bind /home/gemadmin/public /public /bin/gmifs -cert /certs/cert.pem -key /certs/key.rsa pidfile="/var/run/${RC_SVCNAME}.pid" output_log="/var/log/${RC_SVCNAME}" error_log="/var/log/${RC_SVCNAME}.error" command_background=true command_user="gemini:gemini"
In general, everything is owned by gemadmin, but run by gemini, to try and keep the privilages as separate as possible.
The logs do need to be owned by "gemini:gemini"
The user running bwrap and gmifs is gemini, gemadmin is never used by the service.
(We use gemadmin to set up the files, etc, though, but not for running anything).