Skip to main content

Obtian TLS-certificates for sub-domains running on diffrent machines with OpenBSD, RelayD and acme-client

·632 words·3 mins
Table of Contents

Getting a hold of Lets Encrypt certs are actually really easy with acme-client(1) on OpenBSD. But when you have multiple machines in your network in need of a TLS certificate, it gets a bit complicated.

The Problem
#

The acme-client shipped in openBSD only supports the http-challenge. So you have to expose the machine to the internet in order for the LetsEncrypt server verify the challenge. On its own, this is a non-issue. However, not when you are hosting various websites and/or services.

 ┌─────────────────────┐
 │ acme-challenge      │
 │ for sub.domain.com  │
 └─────────┬───────────┘
           │
           │
 ~~~~~~~~~~│~~~~~~Dest.NAT
           │
   port 80 ▼
 ┌───────────┐ ┌───────────────┐
 │domain.com │ │sub.domain.com │
 └───────────┘ └───────────────┘
  Host1         Host2

My Example
#

A mail server might be running on a diffrent machine inside your netork, yet it has to share the same ipv4 gateway via destination NAT (port forward). The issue is: I’m already running a webserver on port 80/443. So I can’t expose the mail sever as well in order to complete the challange. I could request a separate cert on my httpd box and manually copy certs. But that does not scale well. Just don’t manually copy certs if you can do it automatically.

Some kind of proxy is needed to forward the HTTP-request for the acme-challenge to the appropiate box.

Everything needs TLS
#

Relayd(8) is where it’s at.

I’m already utilizing relayd to terminate all TLS connections coming from the internet. So we can use that to forward the acme-challenge to the mail server. In this case, no TLS is needed. So the config is actually really easy.

It will look like this:


    ┌─────────────────────┐
    │ acme challenge      │
    │ for sub.domain.com  │
    └─────────┬───────────┘
              │
              │
    ~~~~~~~~~~│~~~~~~Dest.NAT
              │
  port 80/443 ▼
    ┌───────────┐match: host  ┌───────────────┐
    │relayd     ├────────────►│sub.domain.com │
    └─────────┬─┘             └───────────────┘
     Host1    │                Host2
              │other stuff    ┌───────────────┐
              └──────────────►│other services │
                              └───────────────┘
                               Host3

relayd config
#

Protocol definition.

It is important to note that relayd works in a ’last match wins’. I have a ‘catch-all’ rule that redirects to my local httpd server that listens on localhost:8080 in order to 301 the request to https://

I’m assuming this to be an addition to an already working relayd.conf. This is no deep-dive.

# /etc/relayd.conf
http protocol "http" {
    # forward * to local httpd:8080 for upgrading to https
	match request header "Host" value "*" forward to <httpd>

    # forward ACME-challenge request matching the corresponding host 
	match request header "Host" value "mail.dings.tech" forward to <your_mailserver>
}

And the relay itself:

# /etc/relayd.conf
relay "http" {
	listen on egress port 80
	protocol "http"
	
	forward to <httpd> port 8080 check tcp
	# last wins
	forward to <your_mailserver> port 80	
}

<httpd> and <your_mailserver> are ’table’ definitions, refer to relayd.conf(5).

Getting Certified
#

So, now the machine facing the internet is forwading http to this host. At the machine in need of certs: create the following config and start the service.

Assuming the machine requesting certs is running OpenBSD.

httpd configuration
#

# /etc/httpd.conf
server "mail.dings.tech" {
	listen on * port 80
	location "/.well-known/acme-challenge/*" {
		root "/acme"
		request strip 2
	}
}
# rcctl enable httpd
# rcctl start httpd

acme-client configuration
#

Copy the example file from /etc/examples/acme-client.conf in to /etc/. and change the defailt domain config to your needs:

# /etc/acme-client.conf
domain mail.dings.tech {
	domain key "/etc/ssl/private/mail.dings.tech.key"
	domain full chain certificate "/etc/ssl/mail.dings.tech.fullchain.pem"
	sign with letsencrypt
}

Request you certificate!

# acme-client -v your.domain.com

This works for non-OpenBSD too
#

Duh! You can use relayd to forward the request to any webserver in order to do the http-challenge. Doing this is makes it very easy to manage the http-endoints. The certs and keys are all local to their machines and makes it very safe to operate.

The usual on linux is certbot. you can even run a standalone instance of certbot. No config for webserver needed!