Reverse proxy TCP e proxy SSH con Caddy e caddy-l4
Caddy di base fa da proxy solo per HTTP. Per TCP grezzo e SSH compila Caddy con l'app layer4 di caddy-l4, inoltra porte o condividi la porta 443 tra SSH e HTTPS in base al protocollo.
In breve: Caddy standard non può fare da proxy per TCP grezzo o SSH, perché il suo `reverse_proxy` parla solo HTTP. Per inoltrare TCP compili Caddy con il modulo caddy-l4 (github.com/mholt/caddy-l4) usando xcaddy, poi configuri la sua app `layer4` per ascoltare su una porta e inoltrare le connessioni, scegliendo eventualmente l'upstream in base al protocollo, così SSH e HTTPS possono condividere la porta 443.
caddy-l4 è un modulo separato, non incluso nella distribuzione predefinita di Caddy, e il suo README lo descrive ancora come work in progress la cui configurazione può cambiare tra una versione e l'altra. Questa guida usa la configurazione JSON, che corrisponde direttamente alla struttura documentata del modulo. Se ti serve solo un proxy HTTP o HTTPS, la guida al reverse proxy con Caddy è tutto ciò che ti occorre.
Perché Caddy di base gestisce solo HTTP
Le app integrate di Caddy sono `http`, `tls` e `pki`. L'handler `reverse_proxy` vive dentro l'app HTTP, quindi ha bisogno di una richiesta HTTP già interpretata prima di poter instradare qualsiasi cosa. Un client SSH, un client PostgreSQL o un protocollo di gioco non ne inviano mai una, quindi Caddy risponderebbe con un errore TLS o HTTP.
caddy-l4 aggiunge un'app `layer4` separata che lavora sulle connessioni grezze. Può riconoscere i primi byte di una connessione (banner SSH, ClientHello TLS con SNI, request line HTTP, startup di PostgreSQL e altro), poi fare da proxy, terminare il TLS o passare il flusso a un altro handler. Essendo un'app come `http`, entrambe possono girare nello stesso processo.
| Esigenza | Caddy di base | Caddy + caddy-l4 |
|---|---|---|
| Reverse proxy HTTPS con TLS automatico | Sì | Sì |
| Port forwarding TCP semplice | No | Sì (handler `proxy`) |
| Inoltro UDP | No | Sì (ascolto su `udp/:port`) |
| TLS passthrough per SNI | No | Sì (matcher `tls` con `sni`) |
| SSH e HTTPS sulla stessa porta | No | Sì (matcher `ssh` e `tls`) |
| Supporto Caddyfile | Completo | Parziale; il formato di riferimento è JSON |
Compila Caddy con il modulo layer4
xcaddy compila Caddy con moduli aggiuntivi. In alternativa, seleziona `mholt/caddy-l4` nella pagina di download ufficiale di Caddy e scarica un binario pronto. In entrambi i casi, verifica con `list-modules` che i moduli `layer4` siano elencati prima di scrivere la configurazione.
# Current Caddy needs a recent Go toolchain; distro golang-go packages are
# often too old, so install the latest release from https://go.dev/dl/
# and make sure /usr/local/go/bin is in PATH, then:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# Build Caddy with the layer4 app
~/go/bin/xcaddy build --with github.com/mholt/caddy-l4
# Confirm the modules are present
./caddy list-modules | grep layer4Sostituisci in sicurezza il binario del pacchetto
Se Caddy è stato installato dal repository apt ufficiale, il modo documentato per sostituirlo con una build personalizzata è `dpkg-divert` più `update-alternatives`, così gli aggiornamenti del pacchetto non sovrascrivono il tuo binario. Ricorda che d'ora in poi dovrai ricompilare per ottenere gli aggiornamenti di Caddy.
sudo dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
sudo mv ./caddy /usr/bin/caddy.custom
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
sudo update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
sudo systemctl restart caddyEsempio 1: reverse proxy TCP semplice
Questa configurazione ascolta sulla porta 5432 e inoltra ogni connessione a un server PostgreSQL su un indirizzo privato, come l'altro capo di un tunnel WireGuard. L'assenza di matcher significa che ogni connessione segue la route. Gli upstream accettano più indirizzi `dial` per il failover, e l'handler `proxy` supporta load balancing e health check simili a quelli di `reverse_proxy` per HTTP.
Il backend vede l'IP di Caddy, non quello del client. Se il backend supporta il PROXY protocol, imposta `"proxy_protocol": "v2"` sull'handler proxy per passare l'indirizzo originale.
{
"apps": {
"layer4": {
"servers": {
"postgres": {
"listen": [":5432"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [{"dial": ["10.8.0.2:5432"]}]
}
]
}
]
}
}
}
}
}Esempio 2: SSH e HTTPS multiplexati sulla porta 443
I client SSH inviano il loro banner di versione (`SSH-2.0-...`) appena si connettono, quindi caddy-l4 può riconoscere SSH prima di qualsiasi risposta del server. La configurazione qui sotto manda SSH al sshd locale, il TLS per `git.example.com` a un servizio che termina il proprio TLS e tutto il resto del TLS all'app HTTP di Caddy in ascolto su 8443. Poiché ora la porta 443 appartiene a layer4, l'app HTTP non deve occuparla: imposta `https_port 8443` nelle opzioni globali del Caddyfile, o l'indirizzo `listen` equivalente in JSON, e tieni aperta la porta 80 per le challenge HTTP-01. Con `https_port 8443`, il redirect automatico di Caddy da HTTP a HTTPS punterebbe alla porta 8443, quindi imposta `auto_https disable_redirects` e aggiungi un tuo sito `http:// { redir https://{host}{uri} permanent }`.
I protocolli in cui parla per primo il server, come SMTP, FTP o MySQL, non possono essere riconosciuti in questo modo, perché il client resta in attesa in silenzio. Assegna loro una porta dedicata.
{
"apps": {
"layer4": {
"servers": {
"mux443": {
"listen": [":443"],
"routes": [
{
"match": [{"ssh": {}}],
"handle": [
{"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:22"]}]}
]
},
{
"match": [{"tls": {"sni": ["git.example.com"]}}],
"handle": [
{"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:3443"]}]}
]
},
{
"match": [{"tls": {}}],
"handle": [
{"handler": "proxy", "upstreams": [{"dial": ["127.0.0.1:8443"]}]}
]
}
]
}
}
}
}
}Avvialo e testalo
- 01.Salva il JSON come `/etc/caddy/caddy.json` e validalo.
- 02.Ferma il servizio ed esegui Caddy in primo piano per seguire il log mentre ti connetti.
- 03.Fai puntare l'unit systemd al file JSON con un override drop-in, poi `sudo systemctl daemon-reload` e `sudo systemctl start caddy`.
- 04.Connettiti con `ssh -p 443` e apri il sito HTTPS in un browser per confermare che entrambe le route funzionano.
# Validate and test in the foreground first
sudo caddy validate --config /etc/caddy/caddy.json
sudo systemctl stop caddy
sudo caddy run --config /etc/caddy/caddy.json
# Make the service use the JSON file: sudo systemctl edit caddy
[Service]
ExecStart=
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/caddy.json
ExecReload=
ExecReload=/usr/bin/caddy reload --config /etc/caddy/caddy.json --force
# Client side
ssh -p 443 [email protected]Quando usare invece HAProxy o sslh
caddy-l4 è una buona scelta quando usi già Caddy e vuoi un solo processo per l'HTTPS e qualche route TCP. Per un setup prevalentemente TCP, alternative mature presenti nei repository di Debian e Ubuntu possono essere più semplici da gestire: HAProxy in `mode tcp` instrada per SNI con `req.ssl_sni` e offre statistiche e health check dettagliati, mentre sslh è un piccolo demone creato solo per condividere una porta tra SSH, TLS, OpenVPN e altri. Entrambi si installano con apt e si aggiornano con il resto del sistema, senza build personalizzate da mantenere.
Esporre SSH sulla 443 aiuta sulle reti che consentono solo le porte web, ma non è una misura di sicurezza. Mantieni l'autenticazione solo con chiavi come nella guida all'hardening SSH. Su un VPS NoctisNode hai root completo, quindi qualsiasi di questi strumenti può occupare le porte che ti servono; per il comportamento dei certificati lato HTTPS vedi HTTPS automatico con Caddy e, per i container, Caddy come reverse proxy Docker.
| Strumento | Punti di forza | Compromessi |
|---|---|---|
| Caddy + caddy-l4 | Un solo processo per HTTPS automatico e TCP, matcher di protocollo, API JSON | Build personalizzata, modulo ancora in evoluzione |
| HAProxy | Proxy TCP/HTTP maturo, routing per SNI, health check e statistiche avanzati | Più configurazione, nessun client ACME integrato |
| sslh | Minimale, nato apposta per condividere porte tra SSH/TLS/VPN | Fa solo multiplexing; niente terminazione TLS né load balancing |
FAQ
QCaddy può fare da proxy per il traffico TCP?
ANon di serie. Caddy di base fa da proxy solo per HTTP; per TCP o UDP grezzo ti serve una build di Caddy che includa il modulo di terze parti caddy-l4 (layer4).
QCome faccio a passare SSH attraverso Caddy?
ACompila Caddy con caddy-l4, aggiungi un server layer4 che riconosca `ssh` e inoltri a `127.0.0.1:22`, poi connettiti con `ssh -p <port>`. Sulla porta 443 puoi combinarlo con una route `tls` così l'HTTPS continua a funzionare.
QSSH e HTTPS possono condividere la porta 443?
ASì. caddy-l4, sslh e HAProxy possono tutti ispezionare i primi byte di una connessione e instradare SSH e TLS verso backend diversi sulla stessa porta.
Qcaddy-l4 supporta il Caddyfile?
AHa un supporto al Caddyfile tramite un'opzione globale `layer4`, ma JSON resta il formato più completo e meglio documentato. Controlla il README del modulo per la versione che compili.