💾 Archived View for paritybit.ca › arboretum › sysadmin › openbsd-server-details.gmi captured on 2023-01-29 at 03:01:58. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
The server runs on a Hetzner CPX11 VPS which has:
The server hosts an http server, a gemini server, a finger server, a git server, and a file sharing server.
In addition to my normal OpenBSD Server Setup:
Hetzner supports IPv6, but seemingly only through DHCPv6 or manual configuration. OpenBSD supports IPv6, but only using SLAAC or manual configuration. Therefore, some manual configuration in hostname.vio0 was needed to get IPv6 to work:
dhcp inet6 alias 2a01:4ff:f0:f61::1 64 !route add -inet6 default fe80::1%vio0
Note that Hetzner routes all IPv6 traffic for their cloud instances through fe80::1.
OpenBSD's acme-client is used to request certificates. This is the configuration:
authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } domain paritybit.ca { alternative names { www.paritybit.ca, ftp.paritybit.ca, git.paritybit.ca, jbauer.ca } domain key "/etc/ssl/private/paritybit.ca.key" domain full chain certificate "/etc/ssl/paritybit.ca.fullchain.pem" sign with letsencrypt }
Renewing the certificates is handled by /etc/monthly.local, which is run by cron once a month. The output is sent to me in an email.
next_part "Renewing TLS certificate(s):" acme-client -v -F paritybit.ca rcctl reload relayd httpd
My certificate and key are symlinked to /etc/ssl/server.crt and /etc/ssl/private/server.key so I can avoid having to specify their locations in httpd.conf.
The HTTP server uses OpenBSD's httpd which is very easy to configure and very light on resources.
The file server is also hosted over HTTP also using httpd. Although the subdomain is "ftp", the ftp daemon is not active as it doesn't actually provide any benefit or use over just serving files with HTTP. There are no users who need to upload their own files to the server and httpd and ftpd chroot to different locations which would complicate administration.
All of the domains are served by the following httpd configuration. It also handles the file server since that is done over http.
types { include "/usr/share/misc/mime.types" } # For certificate renewal server "default" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 301 "$REQUEST_SCHEME://$HTTP_HOST$REQUEST_URI" } } # Redirect to WWW server paritybit.ca { listen on * port 80 listen on * tls port 443 hsts { max-age 31536000 preload subdomains } location * { block return 301 "$REQUEST_SCHEME://www.paritybit.ca$REQUEST_URI" } } server www.paritybit.ca { listen on * port 80 listen on * tls port 443 hsts { max-age 31536000 preload subdomains } root "/paritybit.ca" gzip-static location match "/blog$" { block return 301 "$REQUEST_SCHEME://www.paritybit.ca/blog/" } location match "/projects$" { block return 301 "$REQUEST_SCHEME://www.paritybit.ca/projects/" } location match "/([^.]*[^/])$" { request rewrite "/%1.html" } } server ftp.paritybit.ca { listen on * port 80 listen on * tls port 443 hsts { max-age 31536000 preload subdomains } root "/ftp.paritybit.ca" directory auto index location "/paste/" { directory no index } } server git.paritybit.ca { listen on * port 80 listen on * tls port 443 hsts { max-age 31536000 preload subdomains } root "/git.paritybit.ca" }
Note that each site is available over HTTP in addition to HTTPS. This is to accommodate older clients that do not have up-to-date browsers, those that are slow enough such that TLS causes significant delay, or those that don't have a TLS library available. For a site like mine, where user data is not being sent to the server, this is acceptable. Users are free to choose HTTPS and, in doing so, the HSTS header will ensure a modern browser keeps using HTTPS.
I chose Solène Rapenne's vger as my gemini server.
vger configuration is extremely simple since it just uses inetd and relayd:
This is the inetd configuration:
127.0.0.1:11965 stream tcp nowait _vger /usr/local/bin/vger vger
And this is the relayd configuration:
log connection tcp protocol "gemini" { tls keypair paritybit.ca } relay "gemini" { listen on egress port 1965 tls protocol "gemini" forward to 127.0.0.1 port 11965 }
/etc/ssl/paritybit.ca.fullchain.pem is symlinked to /etc/ssl/paritybit.ca.crt for relayd.
The content of the gemini server is a git repository that lives in /var/gemini. When updates to the wiki are made, I can simply SSH into the server and run `git pull` to update the content. The _vger group has the ability to read the contents of /var/gemini but only root has permissions for the .git folder so the gemini server can't serve it.
When pushing new content to my web site or gemini capsule, a few things need to be done. The script below handles gzipping new or updated content on the site to take advantage of httpd's static gzip capabilities, and it updates the gemini capsule with the latest changes. It also makes sure all files have the correct ownership.
#!/bin/sh # Update the website echo "Chowning site contents..." chown -R www:daemon /var/www/paritybit.ca echo "Gzipping site contents..." gzip -fkrq /var/www/paritybit.ca/ 2>/dev/null # Update the Gemini site cd ~ if [ -d paritybit.ca ]; then cd paritybit.ca; git pull; cd .. else git clone git://git.paritybit.ca/paritybit.ca fi echo "Updating gemini capsule contents..." cp -r paritybit.ca/content/garden/* paritybit.ca/static/img /var/gemini/ echo "Chowning gemini capsule contents..." chown -R _vger:_vger /var/gemini/
OpenBSD's inetd is used to call OpenBSD's fingerd for the finger server.
The configuration in inetd for fingerd is:
finger stream tcp nowait _fingerd /usr/libexec/fingerd fingerd -lsmu finger stream tcp6 nowait _fingerd /usr/libexec/fingerd fingerd -lsmu
A user (jbauer) was created with ~/.plan and ~/.project files which are displayed by fingerd.
The "git server" is really nothing more than a git daemon to handle cloning/fetching/pulling and stagit to generate static pages for each repository so code and changes can be browsed from a web browser. SSH is used to push changes to the server, and the git daemon is invoked using OpenBSD's inetd.
The static pages generated by stagit are served using the configuration in httpd.conf. Git repositories live in /var/git and updates are pushed there using SSH. The git daemon for cloning using the git:// protocol is invoked using inetd with the following configuration:
git stream tcp nowait _gitdaemon /usr/local/bin/git git daemon --inetd --verbose --base-path=/var/git --export-all /var/git/ git stream tcp6 nowait _gitdaemon /usr/local/bin/git git daemon --inetd --verbose --base-path=/var/git --export-all /var/git/
The following script is run as an hourly cronjob to update the static pages and incorporate recently pushed changes. I may switch to using a post-receive hook instead of a cronjob if this doesn't end up fitting my needs.
#!/bin/sh # Update all individual repos for repo in /var/git/*; do cd /var/www/git.paritybit.ca/"$(basename "$repo" .git)" /usr/local/bin/stagit "$repo" done # Re-generate the index page cd /var/www/git.paritybit.ca /usr/local/bin/stagit-index /var/git/* > index.html
The following script is used to make adding a new repository quicker and easier:
#!/bin/sh printf "Project Name: " read name printf "Project Description: " read desc #printf "Project URL: " #read url url="https://git.sr.ht/~jbauer/$name" #printf "Project Owner: " #read owner owner="Jake Bauer" cd /var/www/git.paritybit.ca mkdir "$name" && cd "$name" ln -s ../favicon.png . ln -s ../logo.png . ln -s ../style.css . cd /var/git git clone --bare "$url" echo "$desc" > "$name".git/description echo "$owner" > "$name".git/owner echo "$url" > "$name".git/url