A TLS Reverse Proxy for SNI and ALPN routing.
Supports both static and dynamic TLS routing.
go run ./cmd/tlsrouter/ \
--config ~/.config/tlsrouter/backends.csv \
--vault ~/.config/tlsrouter/secrets.tsv \
--ip-domains vm.example.com \
--networks 192.168.1.0/24 \
--bind 0.0.0.0 \
--port 443Configured backends are loaded statically, while URLs like https://tls-192-168-1-100.vm.example.com are dynamically proxied - provided that the ip domain and ip-as-subdomain addresses match the allowed domain and networks.
Sites are configured through DNS.
Both http/1.1 and ssh (terminated) can be enabled by setting a CNAME to the direct IP domain:
# terminates https to 3080
CNAME site-a.whatever.com tls-192-168-1-100.vm.example.net 300
# proxies non-terminated to 443
CNAME site-a.whatever.com tcp-192-168-1-100.vm.example.net 300
If you'd like to use a CNAME for convenience for multiple records, use cname.<ip-domain>, such as cname.vm.example.net.
CNAME sites.whatever.com cname.vm.example.net 300
Note: all ports in the table below are public and you should bind to localhost or use a firewall if you wish to run things on those ports privately.
A whatever.com 123.1.2.3 300
SRV _http._tcp.whatever.com 10 3080 tls-10-11-1-123.a.bnna.net 300 10
SRV _ssh._tcp.whatever.com 10 22 tls-10-11-1-123.a.bnna.net 300 10
Note: ports must be selected according to the table below. Arbitrary ports are not allowed for security reasons (anyone can set records on their domain to your IP address).
Whether using CNAME or A records, SRV records will enable additional proxying.
SRV _h2._tcp.whatever.com 10 443 tcp-10-11-1-123.a.bnna.net 300 10
SRV _postgresql._tcp.whatever.com 10 3080 tls-10-11-1-123.a.bnna.net 300 10
ALPN names can be translated to service names in one of two ways:
- replace all
.(periods) with-, and replace/with_ - drop anything after
/and replace all.(periods) with-
For example: http and http_1-1 are both valid for http/1.1
Note: ports must be selected according to the table below. Arbitrary ports are not allowed for security reasons (anyone can set records on their domain to your IP address).
The URL pattern is There are two URL patterns:
<layer4>-<ipv4-octets>-<ip-domain>- tls-192-168-1-100.example.com (Handles / Terminates TLS)
- tcp-192-168-1-100.example.com (Raw TCP Passthrough / Non-Terminating)
ALL TRAFFIC uses port 443 externally.
If the Raw TCP URL is used, then the Raw Port will be used - proxied traffic will remain encrypted.
If the Terminating URL is used, then the Decrypted Port will be used.
Why non-standard ports? So that unencrypted services, which may have been intended for private networking, aren't exposed to the Internet by default.
| ALPN | Raw Port | Decrypted Port | Comment |
|---|---|---|---|
| http/1.1 | 443 | 3080 | 3080 to be familiar, but non-default like 3000, 8080, and 80 |
| ssh | 44322 | 22 | sshd can't handle sclient tls directly, hence 44322 for tls |
| --- | --- | --- | special protocols |
| acme-tls/1 | 443 | - | for ACME / Let's Encrypt TLS SNI ALPN challenges |
| h2 | 443 | - | proper HTTP/2 requires raw passthrough and has no plain port |
| h2c | - | 3080 | plain HTTP/2, for testing/debugging |
| --- | --- | --- | 10,000 is added to the default ports below |
| coap | 5684 | 15683 | IoT, plain port is 5683 |
| dicom | 2762 | 10104 | biomedical imaging, plain port is 104 |
| dot | 853 | 10053 | dns-over-tls, normal plain port is 53 (udp and tcp) |
| ftp | 990 | 10021 | normal plain port is 21, but it's more complicated than that |
| imap | 993 | 10143 | normal plain port is 143 |
| irc | 6697 | 16667 | normal plain port is 6667 |
| managesieve | 4190 | 14190 | for mail filtering, plain is also 4190 |
| mqtt | 8883 | 11883 | normal plain port is 1883 |
| nntp | 563 | 10119 | for News Servers, plain port is 119 |
| ntske/1 | 4460 | 10123 | for NTP, normal plain port is 123 |
| pop3 | 995 | 10110 | normal plain port is 110 |
| postgresql | 5432 | 15432 | Postgres 17+ supports direct TLS |
| tds/8.0 | 1433 | 11433 | MS SQL 2025+ supports direct TLS |
| radius/1.0 | 2083 | 12083 | legacy TLS optional |
| radius/1.1 | 2083 | 12083 | direct TLS required |
| sip | 5061 | 15060 | normal plain port is 5060 (or 5080) |
| smb | 10445 | 10445 | Either use requires tunneling (native SMB TLS requires QUIC) |
| webrtc | 443 | 10080 | 10080 to be familiar, but not 18080, 8080, 8081, or 9000 |
| c-webrtc | 443 | 10080 | " |
| xmpp-client | 5223 | 15222 | client-to-server communication, default 5222 (plain) |
| xmpp-server | 5270 | 15269 | server-to-server communication, default 5269 (plain) |
For all registered ALPNs, see https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml.
Excluded:
cois UDP-onlydoqDNS over QUIC is UDP-onlyhttp/0.9,http/1.0superseded byhttp/1.1h3HTTP over QUIC is UDP-onlynnsphas no port designation (and isn't actually referenced in the RFC)spdy/*superseded byh2stun.turnhas more complex implications that I'm ready to considerstun.nat-discovery(same)sunrpcprobably not relevant