🍄Setup Mycorrhiza On Openbsd

I would like to use core packages on OpenBSD to setup Mycorrhiza. These core packages are:

  • relayd(8) – our relay, reverse proxy, the facade or whatever. It will accept all incoming connections and route them to the services running on localhost.

  • httpd(8) – HTTP web server, will be used to pass the ACME HTTP-01 challenge to acquire SSL certificates.

  • acme-client(1) – Automatic Certificate Management Environment (ACME) client. It will fetch SSL certificates for us and keep them updated.

  • cron(8) – command scheduler, it will keep our certificates updated. Let's Encrypt issues certificates for 90 days, they should be updated before they get expired.

Set up Mycorrhiza daemon

First let's install the only non-core package, Mycorrhiza itself, all commands are run as root:

pkg_add mycorrhiza

I also use the curl external package to check that connection works, you can remove it after installing Mycorrhiza:

pkg_add curl

Let's make sure that Mycorrhiza is set up correctly:

# Use default rc script
rcctl start mycorrhiza

# Modify the configuration
cd /var/mycorrhiza
# Open the file in any editor. In mg: modify anything as needed,
# then press Ctrl+x Ctrl+s to save and Ctrl+x Ctrl+c to exit.
mg config.ini

# Create users.json and the first user
rcctl stop mycorrhiza
mycorrhiza -create-admin greenfork /var/mycorrhiza
# Type the password for this user and press Enter
# Stop the daemon by pressing Ctrl+C
chown _mycorrhiza:_mycorrhiza /var/mycorrhiza/users.json

# Check that it works properly. If it fails to start, wait a bit.
rcctl start
curl localhost:1737

Expose to the internet

Now let's acquire DNS name and make sure that it points to our server. In my case I added a garden.greenfork.me DNS name with an A record pointing to my website address 95.216.161.229. Let's have our relay set up to connect to Mycorrhiza. In my /etc/relayd.conf I roughly have:

ipv4=95.216.161.229

table <local> { 127.0.0.1 }

# Digital garden with Mycorrhiza
table <garden> { 127.0.0.1 }

http protocol https {
        #tls keypair garden.greenfork.me

        # Better logging for httpd when "log style forwarded" is specified
        match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
        match request header append "X-Forwarded-Port" value "$REMOTE_PORT"

        # Best-practice headers for maximum security.
        match response header set "Content-Security-Policy" value "default-src 'none'; style-src 'self'; img-src 'self'; font-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; frame-src www.youtube-nocookie.com"
        match response header set "Permissions-Policy" value "camera=(), microphone=()"
        match response header set "Referrer-Policy" value "no-referrer"
        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 "X-Frame-Options" value "deny"
        match response header set "X-XSS-Protection" value "1; mode=block"

        return error

        pass request quick url "garden.greenfork.me/.well-known/acme-challenge/" forward to <local>
        pass request quick header "Host" value "garden.greenfork.me" forward to <garden>
        pass
}

# Forward to httpd
relay www {
        listen on $ipv4 port http
        forward to <local> port 8090
}

# Forward to httpd
relay wwwtls {
        listen on $ipv4 port https tls
        protocol https
        forward to <local> port 8080
        forward to <garden> port 1737
}

This allows me to special-case the ACME challenge (HTTP-01 challenge) for Mycorrhiza, which will be handled by httpd, and otherwise route all traffic to the running mycorrhiza daemon. Note that tls keypair garden.greenfork.me is commented because we don't have the certificate yet, we will uncomment it later.

Then in /etc/httpd.conf:

local=127.0.0.1

# Redirect http to https
server 95.216.161.229 {
        listen on $local port 8090
        block return 301 "https://$HTTP_HOST$REQUEST_URI"
}

server garden.greenfork.me {
        listen on $local port 8080
        log style forwarded
        location "/.well-known/acme-challenge/*" {
                root "/acme"
                request strip 2
        }
}

Here we redirect http to https and we run an httpd server ONLY for the acme challenge to acquire certificates. For the changes to work, we should reload the daemons, or start and enable if they were not set up:

rcctl reload httpd
rcctl reload relayd

Now it's time to get our certificates. In /etc/acme-client.conf:

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

domain garden.greenfork.me {
        domain key "/etc/ssl/private/garden.greenfork.me.key"
        domain full chain certificate "/etc/ssl/garden.greenfork.me.crt"
        sign with letsencrypt
}

With this configuration we can acquire our certificates with:

acme-client garden.greenfork.me

And make sure to set it up in cron to automatically update certificates:

EDITOR=mg crontab -e

# Insert this line at the end, then save and quit
~       *       *       *       *       acme-client garden.greenfork.me && rcctl reload relayd

If everything is alright, we can uncomment the tls keypair garden.greenfork.me line in /etc/relayd.conf and reload it with rcctl reload relayd.

Now you should be able to visit https://garden.greenfork.me.