Reverse proxy TCP et proxy SSH avec Caddy et caddy-l4
Caddy de base ne proxifie que du HTTP. Pour du TCP brut et SSH, compilez Caddy avec l'app layer4 caddy-l4, transférez des ports ou partagez le port 443 entre SSH et HTTPS selon le protocole.
En bref : Caddy standard ne peut pas proxifier du TCP brut ni SSH, car son `reverse_proxy` ne parle que HTTP. Pour transférer du TCP, compilez Caddy avec le module caddy-l4 (github.com/mholt/caddy-l4) via xcaddy, puis configurez son app `layer4` pour écouter sur un port et proxifier les connexions, en choisissant éventuellement l'upstream selon le protocole, afin que SSH et HTTPS partagent le port 443.
caddy-l4 est un module distinct, absent de la distribution par défaut de Caddy, et son README le décrit encore comme un travail en cours dont la configuration peut changer d'une version à l'autre. Ce guide utilise la configuration JSON, qui correspond directement à la structure documentée du module. Si vous n'avez besoin que d'un proxy HTTP ou HTTPS, le guide reverse proxy Caddy suffit.
Pourquoi Caddy de base se limite à HTTP
Les apps intégrées de Caddy sont `http`, `tls` et `pki`. Le handler `reverse_proxy` vit dans l'app HTTP : il lui faut une requête HTTP analysée avant de pouvoir router quoi que ce soit. Un client SSH, un client PostgreSQL ou un protocole de jeu n'en envoie jamais, et Caddy répondrait par une erreur TLS ou HTTP.
caddy-l4 ajoute une app `layer4` séparée qui travaille sur des connexions brutes. Elle peut reconnaître les premiers octets d'une connexion (bannière SSH, ClientHello TLS avec SNI, ligne de requête HTTP, démarrage PostgreSQL, etc.), puis proxifier, terminer TLS ou passer le flux à un autre handler. Comme c'est une app au même titre que `http`, les deux peuvent tourner dans le même processus.
| Besoin | Caddy de base | Caddy + caddy-l4 |
|---|---|---|
| Reverse proxy HTTPS avec TLS automatique | Oui | Oui |
| Simple redirection de port TCP | Non | Oui (handler `proxy`) |
| Transfert UDP | Non | Oui (écoute sur `udp/:port`) |
| TLS passthrough par SNI | Non | Oui (matcher `tls` avec `sni`) |
| SSH et HTTPS sur le même port | Non | Oui (matchers `ssh` et `tls`) |
| Prise en charge du Caddyfile | Complète | Partielle ; le JSON est le format de référence |
Compiler Caddy avec le module layer4
xcaddy compile Caddy avec des modules supplémentaires. Vous pouvez aussi sélectionner `mholt/caddy-l4` sur la page de téléchargement officielle de Caddy et récupérer un binaire prêt à l'emploi. Dans les deux cas, vérifiez avec `list-modules` que les modules `layer4` apparaissent avant d'écrire la configuration.
# 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 layer4Remplacer le binaire du paquet en toute sécurité
Si Caddy a été installé depuis le dépôt apt officiel, la méthode documentée pour substituer un build personnalisé est `dpkg-divert` associé à `update-alternatives`, afin que les mises à jour du paquet n'écrasent pas votre binaire. N'oubliez pas que vous devez désormais recompiler pour obtenir les mises à jour de 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 caddyExemple 1 : reverse proxy TCP simple
Cette configuration écoute sur le port 5432 et transfère chaque connexion vers un serveur PostgreSQL sur une adresse privée, par exemple l'autre extrémité d'un tunnel WireGuard. Sans matcher, toutes les connexions empruntent la route. Les upstreams acceptent plusieurs adresses `dial` pour le basculement, et le handler `proxy` gère la répartition de charge et les health checks comme le `reverse_proxy` HTTP.
Le backend voit l'IP de Caddy, pas celle du client. Si le backend comprend le protocole PROXY, définissez `"proxy_protocol": "v2"` sur le handler proxy pour transmettre l'adresse d'origine.
{
"apps": {
"layer4": {
"servers": {
"postgres": {
"listen": [":5432"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [{"dial": ["10.8.0.2:5432"]}]
}
]
}
]
}
}
}
}
}Exemple 2 : SSH et HTTPS multiplexés sur le port 443
Les clients SSH envoient leur bannière de version (`SSH-2.0-...`) dès la connexion, ce qui permet à caddy-l4 de reconnaître SSH avant toute réponse du serveur. La configuration ci-dessous envoie SSH vers le sshd local, le TLS de `git.example.com` vers un service qui termine lui-même son TLS, et tout autre TLS vers l'app HTTP de Caddy en écoute sur 8443. Comme layer4 possède désormais le port 443, l'app HTTP ne doit pas s'y lier aussi : définissez `https_port 8443` dans les options globales du Caddyfile, ou l'adresse `listen` équivalente en JSON, et gardez le port 80 ouvert pour les challenges HTTP-01. Avec `https_port 8443`, la redirection automatique HTTP vers HTTPS de Caddy pointerait vers le port 8443 : définissez donc `auto_https disable_redirects` et ajoutez votre propre site `http:// { redir https://{host}{uri} permanent }`.
Les protocoles où le serveur parle en premier, comme SMTP, FTP ou MySQL, ne peuvent pas être détectés ainsi, car le client attend en silence. Donnez-leur leur propre port.
{
"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"]}]}
]
}
]
}
}
}
}
}Lancer et tester
- 01.Enregistrez le JSON sous `/etc/caddy/caddy.json` et validez-le.
- 02.Arrêtez le service et lancez Caddy au premier plan pour suivre le journal pendant vos connexions.
- 03.Faites pointer l'unité systemd vers le fichier JSON avec un override drop-in, puis `sudo systemctl daemon-reload` et `sudo systemctl start caddy`.
- 04.Connectez-vous avec `ssh -p 443` et ouvrez le site HTTPS dans un navigateur pour confirmer que les deux routes fonctionnent.
# 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]Quand préférer HAProxy ou sslh
caddy-l4 convient bien si vous utilisez déjà Caddy et voulez un seul processus pour HTTPS et quelques routes TCP. Pour une configuration très orientée TCP, des alternatives matures des dépôts Debian et Ubuntu peuvent être plus simples à exploiter : HAProxy en `mode tcp` route par SNI avec `req.ssl_sni` et offre des statistiques et health checks détaillés, tandis que sslh est un petit démon conçu uniquement pour partager un port entre SSH, TLS, OpenVPN et d'autres. Les deux s'installent avec apt et se mettent à jour avec le reste du système, sans build personnalisé à maintenir.
Exposer SSH sur 443 aide sur les réseaux qui n'autorisent que les ports web, mais ce n'est pas une mesure de sécurité. Gardez l'authentification par clé uniquement décrite dans le guide de durcissement SSH. Sur un VPS NoctisNode, vous avez l'accès root complet : n'importe lequel de ces outils peut se lier aux ports nécessaires ; pour le comportement des certificats côté HTTPS, voir HTTPS automatique avec Caddy et, pour les conteneurs, Caddy comme reverse proxy Docker.
| Outil | Points forts | Compromis |
|---|---|---|
| Caddy + caddy-l4 | Un seul processus pour HTTPS automatique et TCP, matchers de protocole, API JSON | Build personnalisé, module encore en évolution |
| HAProxy | Proxy TCP/HTTP mature, routage SNI, health checks et statistiques riches | Plus de configuration, pas de client ACME intégré |
| sslh | Minuscule, dédié au partage de port SSH/TLS/VPN | Multiplexe seulement ; ni terminaison TLS ni répartition de charge |
FAQ
QCaddy peut-il proxifier du trafic TCP ?
APas nativement. Caddy de base ne proxifie que du HTTP ; pour du TCP ou de l'UDP brut, il faut un build de Caddy incluant le module tiers caddy-l4 (layer4).
QComment faire passer SSH par Caddy ?
ACompilez Caddy avec caddy-l4, ajoutez un serveur layer4 qui reconnaît `ssh` et proxifie vers `127.0.0.1:22`, puis connectez-vous avec `ssh -p <port>`. Sur le port 443, vous pouvez le combiner avec une route `tls` pour que HTTPS continue de fonctionner.
QSSH et HTTPS peuvent-ils partager le port 443 ?
AOui. caddy-l4, sslh et HAProxy savent tous inspecter les premiers octets d'une connexion et router SSH et TLS vers des backends différents sur le même port.
Qcaddy-l4 prend-il en charge le Caddyfile ?
AIl prend en charge le Caddyfile via une option globale `layer4`, mais le JSON reste le format le plus complet et le mieux documenté. Consultez le README du module pour la version que vous compilez.