TLS Acceleration for Multiple Domains with relayd

Before You Begin

Please see the TLS acceleration guide with relayd before following this guide.

In this guide, we assume you are following the openhttpd hosting guide and your http services are listening on port 80.

Request SSL Certs

You will need the SSL certs for the domains you want to provide TLS acceleration for. Request them using acme-client if you have not already.

By default, relayd searches /etc/ssl/name:port.crt and /etc/ssl/private/name:port.key for the public/private keypair. If those are not present, it uses /etc/ssl/name.crt and /etc/ssl/private/name.key.

If your public cert and private key have different names, you should update /etc/acme-client.conf. It's recommended to use /etc/ssl/name.crt and /etc/ssl/private/name.key, where name is replaced with your actual domain name.

Optional: If your public cert ends in .fullchain.pem instead of .crt, you can create symbolic links:

$ doas ln -s /etc/ssl/example.com.fullchain.pem /etc/ssl/example.com.crt

You will want to replace example.com with your real domain. This will allow relayd to detect your public key. However, it is still highly recommended that you change acme-client.conf to create public keys that end with the extension .crt.

Make sure to replace example.com with your actual domain.

Edit relayd.conf

Let's create /etc/relayd.conf. This configuration will provide TLS acceleration for three services: for a webserver that listens on port 80, a second which listens on port 8000, and a third which listens on port 8080. Here is what we will put, one block at a time:

ip4="192.168.1.1"
ip6="2001:db8::"
table <www> { 127.0.0.1 }
table <service1> { 127.0.0.1 }
table <service2> { 127.0.0.1 }
log connection

Replace 192.168.1.1 and 2001:db8:: with your real IPv4 and IPv6 address?. Make sure the IPv4 is DDoS-filtered if you have that option.

Replace service1 and service2 with the names of your real services, such as bnc, www, and mail.

Do NOT replace 127.0.0.1. You want relayd to forward its requests to the web server listening on localhost.

http protocol https {
        match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
        match request header append "X-Forwarded-By" \
            value "$SERVER_ADDR:$SERVER_PORT"
        match request header set "Connection" value "close"
        tcp { sack, backlog 128 }
        tls { keypair www.example.com }
        tls { keypair www.sub.example.com }
        tls { keypair service1.example.com }
        tls { keypair service2.example.com }
        match request header "Host" value "*" forward to <www>
        match request header "Host" value "service1.example.com" forward to <service1>
        match request header "Host" value "service2.example.com" forward to <service2>
}

relayd will inspect the headers of the HTTP requests that users send. By default, request for any hostnames are forwarded to port 80 for openhttpd to handle. However, if the header says service1.example.com, it will forward to port 8000 for service1 to handle. If it says service2, then it will forward to port 8080 for service2. Note that relayd does last rule matching, so that the wildcard * must be placed as the first match rule.

We also define how to handle the http protocol. We add X-Forwarded-For, X-Forwarded-By, and Connection headers to HTTP requests before forwarding it to openhttpd.

We turn on selective acknowledgments and set the maximum queue to 128 connections in the tcp block.

We define the keypair names. Below is a table which shows the order in which relayd searches for them. You will want to replace service1.example.com and service2.example.com with your real hostnames.

The last two lines in relayd.conf forward to the proper service based on the Host HTTP header.

Hostname: service1.example.com
PriorityPublic CertPrivate Key
1/etc/ssl/service1.example.com:443.crt/etc/ssl/private/service1.example.com:443.key
2/etc/ssl/service1.example.com.crt/etc/ssl/private/service1.example.com.key
Hostname: service2.example.com
PriorityPublic CertPrivate Key
1/etc/ssl/service2.example.com:443.crt/etc/ssl/private/service2.example.com:443.key
2/etc/ssl/service2.example.com.crt/etc/ssl/private/service2.example.com.key
relay wwwtls {
        listen on $ip4 port 443 tls
        protocol https
        forward to <www> port 80 check icmp
        forward to <service1> port 8000 check icmp
        forward to <service2> port 8080 check icmp
}
relay www6tls {
        listen on $ip6 port 443 tls
        protocol https
        forward to <www> port 80 check icmp
        forward to <service1> port 8000 check icmp
        forward to <service2> port 8080 check icmp
}

We create two relays, one for IPv4 and another for IPv6. Both of them listen on port 443 using TLS. They use the protocol template for https and forward to the proper service on port 80 (see the above openhttpd hosting guide). Both check ICMP to see if the service is available.

We create two relays, one for IPv4 and another for IPv6. Both of them listen on port 443 using TLS. They handle use the protocol template for https and forward to the proper service on ports 80, 8000, and 8080 (see the above openhttpd hosting guide). Both check ICMP to see if the service is available.

Complete relayd.conf

Here is the entire /etc/relayd.conf without commentary:

ip4="192.168.1.1"
ip6="2001:db8::"
table <www> { 127.0.0.1 }
table <service1> { 127.0.0.1 }
table <service2> { 127.0.0.1 }
log connection

http protocol https {
        match request header append "X-Forwarded-For" value "$REMOTE_ADDR"
        match request header append "X-Forwarded-By" \
            value "$SERVER_ADDR:$SERVER_PORT"
        match request header set "Connection" value "close"
        tcp { sack, backlog 128 }
        tls { keypair www.example.com }
        tls { keypair www.sub.example.com }
        tls { keypair service1.example.com }
        tls { keypair service2.example.com }
        match request header "Host" value "*" forward to <www>
        match request header "Host" value "service1.example.com" forward to <service1>
        match request header "Host" value "service2.example.com" forward to <service2>
}

relay wwwtls {
        listen on $ip4 port 443 tls
        protocol https
        forward to <www> port 80 check icmp
        forward to <service1> port 8000 check icmp
        forward to <service2> port 8080 check icmp
}
relay www6tls {
        listen on $ip6 port 443 tls
        protocol https
        forward to <www> port 80 check icmp
        forward to <service1> port 8000 check icmp
        forward to <service2> port 8080 check icmp
}

We create two relays, one for IPv4 and another for IPv6. Both of them listen on port 443 using TLS. They use the protocol template for https and forward to the proper service on port 80 (see the above openhttpd hosting guide). Both check ICMP to see if the service is available.

We create two relays, one for IPv4 and another for IPv6. Both of them listen on port 443 using TLS. They handle use the protocol template for https and forward to the proper service on ports 80, 8000, and 8080 (see the above openhttpd hosting guide). Both check ICMP to see if the service is available.

Login class permissions

If you have a large number of TLS certs, you will need to increase the maximum number of files that relayd can open. Add this to the bottom of /etc/login.conf:

relayd:\
        :openfiles=4096:\
        :stacksize-cur=96M:\
        :stacksize-max=96M:\
        :tc=daemon:

Make sure there is no login.conf.db database, which would prevent the changes in login.conf from being applied:

$ doas rm /etc/login.conf.db