1 Reverse Proxy
Kyle Mendell edited this page 2026-10-27 15:06:39 -05:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Overpass can put the web UI, REST API, gRPC, op ssh, and node tunnels behind one ordinary HTTPS reverse proxy. Caddy owns public port 443; Overpass keeps listening on its internal HTTPS port (normally 8443). No Caddy Layer 4 module or extra public ports are required.

Overpass configuration

Enable TLS routing and set the public address clients and nodes should use:

proxy:
  web_addr: 127.0.0.1:8443
  public_addr: gate.acme.com:443
  public_url: https://gate.acme.com
  tls_routing: true
  trusted_proxies:
    - 127.0.0.1/32

tls_routing makes the public web address the advertised endpoint for every protocol. The dedicated 3023–3025 listeners remain available for older clients, but they do not need to be exposed publicly.

public_url gives Overpass the external browser/OIDC URL. Set it explicitly when possible. If it is omitted, Overpass accepts X-Forwarded-Proto and X-Forwarded-Host only from peers listed in trusted_proxies.

Caddy

Use the standard Caddy build and an ordinary reverse_proxy:

gate.acme.com {
    reverse_proxy https://127.0.0.1:8443 {
        transport http {
            tls_insecure_skip_verify
        }
    }
}

That is the complete public routing configuration. Caddy terminates the public TLS connection on port 443, obtains the public certificate, and forwards HTTPS and WebSocket requests to Overpass. tls_insecure_skip_verify applies only to Caddy's connection to Overpass's internal, self-signed HTTPS listener.

If Caddy reaches Overpass by a hostname covered by a trusted internal certificate, verify it normally instead:

gate.acme.com {
    reverse_proxy https://overpass.internal:8443
}

Do not add a layer4 block, TCP proxy, SNI passthrough, or separate route for the connection-upgrade endpoint. Caddy's normal WebSocket handling is enough.

How single-port routing crosses an HTTP proxy

Direct clients first offer Overpass's ALPN protocol on the public TLS connection. When Caddy terminates that connection, the client automatically opens a WebSocket at /api/v1/connection-upgrade and carries a second TLS connection inside it. Overpass then routes that inner connection through the same existing ALPN mux:

Traffic Inner ALPN
Client SSH (op ssh) op-ssh/1
Node reverse tunnel op-tunnel/1
gRPC login, enrollment, and admin op-grpc/1

The outer TLS connection is authenticated using Caddy's public certificate (or the configured --tls-pin). Overpass advertises its inner certificate pin over that authenticated HTTPS connection, and the client verifies the inner TLS connection against it. The fallback does not silently disable certificate verification.

Direct ALPN routing still works when Overpass owns the public port or sits behind TLS passthrough. The HTTP/WebSocket fallback is automatic; operators do not select a client mode.

nginx

nginx also works as a normal HTTP reverse proxy. WebSocket upgrade headers are required:

server {
    listen 443 ssl;
    server_name gate.acme.com;

    # ssl_certificate ...;
    # ssl_certificate_key ...;

    location / {
        proxy_pass https://127.0.0.1:8443;
        proxy_ssl_verify off; # internal self-signed certificate

        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Traefik and other proxies work the same way: use an HTTPS/HTTP router and allow WebSocket upgrades. TCP passthrough remains supported but is not required.

Firewall and ports

Only the reverse proxy needs a public listener:

Port Exposure Purpose
443 Public Caddy/other HTTPS reverse proxy
8443 Private or loopback Overpass HTTPS backend
3023 Private Legacy direct SSH routing
3024 Private Legacy direct node tunnels
3025 Private Legacy direct gRPC

If Caddy and Overpass run in separate containers, bind 8443 to the private container network rather than loopback and use that service address in the Caddyfile.

OIDC

Register this redirect URI with the identity provider:

https://gate.acme.com/oidc/callback

It uses the public 443 URL, not Overpass's internal 8443 listener.

Troubleshooting

First verify the web route and upgrade endpoint reach Overpass:

curl -fsS https://gate.acme.com/api/v1/healthz
op login gate.acme.com:443 --debug

Common failures:

  • 404 for /api/v1/connection-upgrade: Caddy is pointing at an older Overpass server, or a path matcher excludes the endpoint.
  • WebSocket upgrade failure: an intermediary is removing the Upgrade and Connection headers. The simple Caddy configuration above forwards them automatically.
  • Outer certificate error: the client does not trust Caddy's public certificate, or --tls-pin refers to a different certificate.
  • Inner certificate-pin error: the HTTP response and inner TLS connection reached different Overpass instances with different persisted TLS certificates. Give replicas a shared TLS identity or use sticky routing.
  • Login works but the advertised SSH/tunnel address is wrong: set public_addr: gate.acme.com:443, public_url, and tls_routing: true.

Enable tls_routing only after upgrading op and node agents to a build that supports the HTTP connection-upgrade transport. Older clients do not know how to cross a TLS-terminating HTTP proxy.