← Back to All Writeups

Reverse Proxy and Load Balancing with OpenBSD relayd(8): Multi-Tenant Case Study for XYZ Corp

In enterprise environments managing multiple internal applications and microservices, the standard practice is often deploying complex stacks such as NGINX, Traefik, or container-based balancers. However, where stability, minimalism, and kernel-level security are paramount, OpenBSD’s native relayd(8) daemon delivers a clean, lightweight, and auditable solution.

This case study demonstrates how XYZ Corp centralizes public HTTPS traffic across three distinct internal workloads using domain-based L7 routing, strict TLS termination, and active backend health monitoring.


1. Scenario and Requirements for XYZ Corp

XYZ Corp operates 3 separate services hosted in isolated subnets:

  1. app.xyz.corp: Core customer portal (Go backend running on 10.0.1.10:3000).
  2. api.xyz.corp: High-availability REST API (Load balanced between 10.0.1.20:8000 and 10.0.1.21:8000).
  3. portal.xyz.corp: Corporate static portal and documentation (Served by OpenBSD httpd(8) on 127.0.0.1:8080).

2. Topology and Flow Diagram

               [ INTERNET / Public HTTPS:443 Traffic ]
                               |
                               v
                     +-------------------+
                     |   OpenBSD Edge    |
                     |  - PF Firewall    |
                     |  - relayd(8) TLS  | (Public IP: 198.51.100.1)
                     +-------------------+
                               |
       +-----------------------+-----------------------+
       | Host: app.xyz.corp    | Host: api.xyz.corp    | Host: portal.xyz.corp
       v                       v                       v
+---------------+     +------------------+     +------------------+
| Backend APP   |     | REST API Cluster |     | Static Portal    |
| 10.0.1.10:3000|     | .20:8000  .21:8000|    | 127.0.0.1:8080   |
+---------------+     +------------------+     +------------------+

3. Full /etc/relayd.conf Implementation

# /etc/relayd.conf - Multi-Tenant Setup for XYZ Corp

# 1. Backend Pools with Active Health Checks
table <backend_app> {
    10.0.1.10
}

table <cluster_api> {
    10.0.1.20
    10.0.1.21
}

table <backend_portal> {
    127.0.0.1
}

# 2. HTTP Protocol & Security Policies
http protocol "https_corporate_filter" {
    tcp { nodelay, sack, socket buffer 65536 }
    
    # Proxy Headers
    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 "X-Forwarded-Proto" value "https"

    # Global Security Headers
    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 "Referrer-Policy" value "strict-origin-when-cross-origin"

    # L7 Routing based on Host Header
    match request header "Host" value "app.xyz.corp" forward to <backend_app>
    match request header "Host" value "api.xyz.corp" forward to <cluster_api>
    match request header "Host" value "portal.xyz.corp" forward to <backend_portal>
}

# 3. Public TLS Relay Definition
relay "tls_gateway" {
    listen on 198.51.100.1 port 443 tls
    protocol "https_corporate_filter"

    # Default fallback
    forward to <backend_portal> port 8080 check http "/healthz" code 200

    # Specific forwards with dedicated backend ports and health probes
    forward to <backend_app> port 3000 check http "/status" code 200
    forward to <cluster_api> port 8000 check http "/v1/health" code 200
}

4. Live Inspection with relayctl(8)

Verify runtime health status across all backend tables:

doas relayctl show summary

Output:

Id      Type            Name                            Avlblty Status
1       relay           tls_gateway                             active
1       table           backend_app:3000                        active
1       host            10.0.1.10                       100.00% up
2       table           cluster_api:8000                        active
2       host            10.0.1.20                       100.00% up
3       host            10.0.1.21                       100.00% up
3       table           backend_portal:8080                     active
4       host            127.0.0.1                       100.00% up