Hosting a HUGO website with OpenBSD


Intended Audience

This guide is intended for anyone who wants to setup a HUGO website runing on OpenBSD using httpd and relayd.

Document Scope

It is assumed that you have some basic UNIX administration skills and that you have already setup an OpenBSD server to your liking. General setup and administration is outside the scope of this document. Instead, we will focus on setting up OpenBSD’s httpd server, relayd, SSL certificates, and so on.

OpenBSD.Amsterdam - A short plug for my hosting provider

This server is hosted on OpenBSD.Amsterdam. They have really been a pleasure to work with, and they give back to The OpenBSD Foundation and at under 6 euros per month it’s a great value.

OpenBSD Amsterdam - https://openbsd.amsterdam

The OpenBSD Foundation - https://www.openbsdfoundation.org

acme-client, httpd, and relayd

The OpenBSD project has some beautifully designed software and tools. The httpd webserver is intentionally lightweight and doesn’t have too many features. For this reason we will need to use relayd to handle http headers and it is indeed the star of the show.

For now let’s have a look at the official documentation:

httpd manpage - https://man.openbsd.org/httpd.8

httpd.conf manpage - https://man.openbsd.org/httpd.conf.5

relayd manpage - https://man.openbsd.org/relayd.8

relayd.conf manpage - https://man.openbsd.org/relayd.conf.5

acme-client manpage - https://man.openbsd.org/acme-client.1

acme-client.conf manpage - https://man.openbsd.org/acme-client.conf.5

Steps

  • Ensure prerequisites have been managed
    • OpenBSD installed / configured
    • DNS under your control
    • CAA records setup
    • PF setup for TCP ports 80 and 443
  • Install required software ( HUGO )
  • Setup httpd, relayd, acme-client
  • Generate SSL certificates
  • Enable and Start Services
  • Create scripts, and cronjob
  • Setup HUGO ( NOT COVERED IN THIS DOCUMENT )

Prerequisites - NOT COVERED IN THIS DOCUMENT

Will not be covering setting up DNS in this guide. Please double-check to make sure that you have proper CAA records setup for your domain. These CAA records allow your domain SSL certs to be managed by Let’s Encrypt.

This is what they look like:

example.org. 300 IN   CAA   0 issue "letsencrypt.org"

You may check your domain as follows:

# /usr/bin/dig CAA example.org @9.9.9.9
; <<>> dig 9.10.8-P1 <<>> CAA example.org @9.9.9.9
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42490
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.org.		IN	CAA

;; ANSWER SECTION:
example.org.	3600	IN	CAA	0 issue "letsencrypt.org"

;; Query time: 12 msec
;; SERVER: 9.9.9.9#53(9.9.9.9)
;; WHEN: Mon Nov 11 11:11:11 CEST 2024
;; MSG SIZE  rcvd: 81
#

pf.conf

Also ensure that you have opened up ports on the firewall to your web services. Be mindful of the required order: options, queueing, translation and filtering.

Example entries in the /etc/pf.conf file:

NET="em0"
### TCP Flags ###
MOD_STATE="flags S/SA modulate state"
pass in log on $NET inet proto tcp from any to $NET_IP port 80 $MOD_STATE
pass in log on $NET inet proto tcp from any to $NET_IP port 443 $MOD_STATE

Required Software

All of the required software is in base except HUGO. Software in OpenBSD’s base is considered highly secure.

hugo–extended

Install HUGO:

/usr/sbin/pkg_add -vi hugo--extended

httpd

Fire up your favorite editor and edit the /etc/httpd.conf file. Adjust to suit your specific requirements.

types { include "/usr/share/misc/mime.types" }

server "example.org" {
	listen on 127.0.0.1 port 8080
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
	root "/htdocs/example.org/public"
	directory auto index
}

### http -> https ###
server "example.org" {
	listen on 127.0.0.1 port 8181
	block return 301 "https://$HTTP_HOST$REQUEST_URI"
}

### www -> raw domain ###
server "www.example.org" {
	listen on 127.0.0.1 port 8080
	block drop
}

Save your config.

the httpd daemon is chrooted to /var/www by default. It is there where we will need to create the document root directory structure.

mkdir -p /var/www/htdocs/example.org

Let’s check our configs to make sure they are sane.

httpd -n

You should see something like: configuration OK

The httpd daemon can be enabled and started as follows:

rcctl enable httpd
rcctl start httpd

relayd

Fire up your favorite editor again and edit the /etc/relayd.conf file. Adjust to meet your requirements.

EXT4 = "xx.xx.xx.xx" ### <-- INET4 INTERFACE IP GOES HERE

table <LOCAL> { 127.0.0.1 }

log state changes
log connection

http protocol "HTTP_FILTER" {
 ### DEBUG ###
 # Uncomment during testing to return HTTP 403 Forbidden when request is blocked.
 # Comment out and relayd will close connection with TCP FIN ( for scanners and crap ) 
 #return error
 http headerlen 4096

 tls edh
 tls keypair "example.org"
 tls { no tlsv1.0, no tlsv1.1, ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256" }
 
 match request header set "X-Forwarded-For" value "$REMOTE_ADDR"
 match request header set "X-Forwarded-SPort" value "$REMOTE_PORT"
 match request header set "X-Forwarded-DPort" value "$SERVER_PORT"
 match response header remove "Server" value "*"

 match header log "Host"
 match header log "X-Forwarded-For"
 match header log "User-Agent"
 match header log "X-Req-Status"
 match url log

 match response header set "Referrer-Policy" value "same-origin"
 match response header set "Referrer-Policy" value "no-referrer"
 match response header set "X-Frame-Options" value "deny"
 match response header set "X-XSS-Protection" value "1; mode=block"
 match response header set "Content-Security-Policy" value "default-src 'none'; style-src 'self'; img-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"
 match response header set "Permissions-Policy: microphone=(), camera=()"
 match response header set "Strict-Transport-Security" value "max-age=31536000; includeSubDomains; preload"
 match response header set "X-Content-Type-Options" value "nosniff"
 match response header set "Cache-Control" value "max-age=86400"

 match request tag "BAD_METHOD"

 match request method GET tag "OK_METH"
 match request method HEAD tag "OK_METH"

 block request quick tagged "BAD_METHOD"

 match request header "Host" value "example.org" tag "OK_REQ"
 match request header "Host" value "www.example.org" tag "OK_REQ"

 block request quick tagged "OK_METH" tag "BAD_HH"
 
 #block request quick path "*.php" tag "NO_PHP"
 #block request quick path "*.cgi" tag "NO_CGI"
 #block request quick path "*.js" tag "NO_JS"
 #block request quick path "*.asp" tag "NO_ASP"

 block tag "BAD_REQ"

 pass request tagged "OK_REQ"
}

relay "IPV4_WEB_HTTPS" {
 listen on $EXT4 port 443 tls
 protocol "HTTP_FILTER"
 forward to <LOCAL> port 8080
}


relay "IPV4_WEB_HTTP" {
 listen on $EXT4 port 80 
 protocol "HTTP_FILTER"
 forward to <LOCAL> port 8181
}

The relayd daemon can be enabled and started as follows:

rcctl enable relayd
rcctl start relayd

acme-client

SSL certificates will be managed using OpenBSD’s acme-client.

Open up the /etc/acme-client.conf file with your favorite editor and add something like this:

authority letsencrypt {
	api url "https://acme-v02.api.letsencrypt.org/directory"
	account key "/etc/acme/letsencrypt-privkey.pem"
	contact "mailto:your.email@example.org"
}

domain example.org {
	domain key "/etc/ssl/private/example.org.key"
	domain full chain certificate "/etc/ssl/example.org.fullchain.pem"
	sign with letsencrypt
}

Create the directory structure:

mkdir -p -m 700 /etc/ssl/private
mkdir -p -m 755 /var/www/acme

Check the configuration and restart httpd:

# httpd -n
configuration ok
# rcctl restart httpd
httpd(ok)
httpd(ok)
#

Restart relayd as well:

# rcctl restart relayd
relayd(ok)
relayd(ok)
#

Run the acme-client ( this should setup our SSL certificates ):

# acme-client -v example.org
...
acme-client: /etc/ssl/www.example.crt: created
acme-client: /etc/ssl/www.example.pem: created

To renew the certificates automagically create a script /usr/local/sbin/acme.sh with the following contents:

#!/bin/sh
/usr/sbin/acme-client example.org >/dev/null 2>&1 && /usr/sbin/rcctl reload httpd >/dev/null 2>&1 && /usr/sbin/rcctl reload relayd

Make sure the permissions are correct.

# /bin/chmod 750 /usr/local/sbin/acme.sh
# ls -l /usr/local/sbin/acme.sh
-rwxr-x---  1 root  wheel   116 Jan  1 07:11 acme.sh
#

Add it a cronjob to always keep the SSL certs up-to-date:


###############################################################################
#minute hour    mday    month   wday    [flags] command                       #
###############################################################################
### Keep SSL Certs Up-To-Date ### 
0       0 	* 	* 	* 	/usr/local/sbin/acme.sh

HUGO

Follow the instructions for the HUGO theme of your choosing.

HUGO Main Site - https://gohugo.io

HUGO Documentation - https://gohugo.io/documentation

HUGO Themes - https://themes.gohugo.io