💾 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

View Raw

More Information

-=-=-=-=-=-=-

OpenBSD Server Details

← Back

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:

OpenBSD Server Setup

IPv6

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.

TLS Certificates

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.

HTTP/FTP Server

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.

Gemini Server

I chose Solène Rapenne's vger as my gemini server.

vger 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.

Updating the Gemini and HTTP Server Content

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/

Finger Server

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.

Git Server

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.

stagit

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