Caddy как TCP reverse proxy и SSH-прокси с caddy-l4
Базовый Caddy проксирует только HTTP. Для сырого TCP и SSH соберите Caddy с layer4-приложением caddy-l4, пробрасывайте порты или делите порт 443 между SSH и HTTPS по протоколу.
Коротко: стандартный Caddy не умеет проксировать сырой TCP или SSH, потому что его `reverse_proxy` говорит только по HTTP. Чтобы пересылать TCP, соберите Caddy с модулем caddy-l4 (github.com/mholt/caddy-l4) через xcaddy, а затем настройте его приложение `layer4` так, чтобы оно слушало порт и проксировало соединения — при желании выбирая апстрим по протоколу, чтобы SSH и HTTPS делили порт 443.
caddy-l4 — отдельный модуль, не входящий в стандартную поставку Caddy, и в его README он до сих пор описан как работа в процессе, конфиг которой может меняться между версиями. В этом руководстве используется JSON-конфиг, который напрямую соответствует задокументированной структуре модуля. Если вам нужно только проксирование HTTP или HTTPS, достаточно руководства по Caddy как reverse proxy.
Почему базовый Caddy работает только с HTTP
Встроенные приложения Caddy — `http`, `tls` и `pki`. Обработчик `reverse_proxy` живёт внутри HTTP-приложения, поэтому ему нужен разобранный HTTP-запрос, прежде чем что-то маршрутизировать. SSH-клиент, клиент PostgreSQL или игровой протокол никогда его не отправят, и Caddy ответит ошибкой TLS или HTTP.
caddy-l4 добавляет отдельное приложение `layer4`, которое работает с сырыми соединениями. Оно умеет распознавать первые байты соединения (SSH-баннер, TLS ClientHello с SNI, строку HTTP-запроса, стартовое сообщение PostgreSQL и другое), а затем проксировать, терминировать TLS или передавать поток другому обработчику. Поскольку это такое же приложение, как `http`, оба могут работать в одном процессе.
| Задача | Базовый Caddy | Caddy + caddy-l4 |
|---|---|---|
| HTTPS reverse proxy с auto TLS | Да | Да |
| Простой проброс TCP-портов | Нет | Да (обработчик `proxy`) |
| Пересылка UDP | Нет | Да (слушать на `udp/:port`) |
| TLS passthrough по SNI | Нет | Да (матчер `tls` с `sni`) |
| SSH и HTTPS на одном порту | Нет | Да (матчеры `ssh` и `tls`) |
| Поддержка Caddyfile | Полная | Частичная; эталонный формат — JSON |
Соберите Caddy с модулем layer4
xcaddy компилирует Caddy с дополнительными модулями. Как вариант, выберите `mholt/caddy-l4` на официальной странице загрузки Caddy и скачайте готовый бинарник. В любом случае, прежде чем писать конфиг, проверьте через `list-modules`, что модули `layer4` присутствуют.
# 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 layer4Безопасно замените пакетный бинарник
Если Caddy установлен из официального apt-репозитория, задокументированный способ подменить его своей сборкой — `dpkg-divert` плюс `update-alternatives`, чтобы обновления пакета не перезаписали ваш бинарник. Помните: теперь для получения обновлений 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 caddyПример 1: простой TCP reverse proxy
Этот конфиг слушает порт 5432 и пересылает каждое соединение серверу PostgreSQL на приватном адресе, например на другом конце туннеля WireGuard. Без матчера маршрут применяется к каждому соединению. Апстримы принимают несколько адресов `dial` для отказоустойчивости, а обработчик `proxy` поддерживает балансировку нагрузки и health checks — аналогично HTTP-шному `reverse_proxy`.
Бэкенд видит IP Caddy, а не клиента. Если бэкенд понимает PROXY protocol, задайте `"proxy_protocol": "v2"` в обработчике proxy, чтобы передать исходный адрес.
{
"apps": {
"layer4": {
"servers": {
"postgres": {
"listen": [":5432"],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [{"dial": ["10.8.0.2:5432"]}]
}
]
}
]
}
}
}
}
}Пример 2: SSH и HTTPS на одном порту 443
SSH-клиенты отправляют баннер версии (`SSH-2.0-...`) сразу после подключения, поэтому caddy-l4 может распознать SSH ещё до любого ответа сервера. Конфиг ниже отправляет SSH на локальный sshd, TLS для `git.example.com` — сервису, который сам терминирует TLS, а весь остальной TLS — HTTP-приложению Caddy, слушающему 8443. Поскольку порт 443 теперь занимает layer4, HTTP-приложение не должно тоже его занимать: задайте `https_port 8443` в глобальных опциях Caddyfile или эквивалентный адрес `listen` в JSON и оставьте порт 80 открытым для challenge HTTP-01. С `https_port 8443` автоматический редирект Caddy с HTTP на HTTPS будет вести на порт 8443, поэтому задайте `auto_https disable_redirects` и добавьте собственный сайт `http:// { redir https://{host}{uri} permanent }`.
Протоколы, где первым говорит сервер, например SMTP, FTP или MySQL, так распознать нельзя — клиент молча ждёт. Выделите им собственный порт.
{
"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"]}]}
]
}
]
}
}
}
}
}Запуск и проверка
- 01.Сохраните JSON как `/etc/caddy/caddy.json` и проверьте его.
- 02.Остановите сервис и запустите Caddy на переднем плане, чтобы видеть лог во время подключения.
- 03.Направьте unit systemd на JSON-файл через drop-in override, затем выполните `sudo systemctl daemon-reload` и `sudo systemctl start caddy`.
- 04.Подключитесь через `ssh -p 443` и откройте HTTPS-сайт в браузере, чтобы убедиться, что оба маршрута работают.
# 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]Когда лучше HAProxy или sslh
caddy-l4 хорошо подходит, если вы уже используете Caddy и хотите один процесс для HTTPS и нескольких TCP-маршрутов. Для конфигурации с упором на TCP зрелые альтернативы из репозиториев Debian и Ubuntu могут быть проще в эксплуатации: HAProxy в `mode tcp` маршрутизирует по SNI через `req.ssl_sni` и даёт подробную статистику и health checks, а sslh — маленький демон, созданный только для того, чтобы делить один порт между SSH, TLS, OpenVPN и другими. Оба ставятся через apt и обновляются вместе с системой, без собственной сборки, которую нужно поддерживать.
SSH на 443 помогает в сетях, где разрешены только веб-порты, но это не функция безопасности. Оставьте аутентификацию только по ключам из руководства по защите SSH. На VPS NoctisNode у вас полный root, так что любой из этих инструментов может занять нужные порты; про поведение сертификатов на стороне HTTPS см. автоматический HTTPS в Caddy, а для контейнеров — Caddy как reverse proxy для Docker.
| Инструмент | Сильные стороны | Компромиссы |
|---|---|---|
| Caddy + caddy-l4 | Один процесс для auto HTTPS и TCP, матчеры протоколов, JSON API | Своя сборка, модуль ещё развивается |
| HAProxy | Зрелый TCP/HTTP-прокси, маршрутизация по SNI, богатые health checks и статистика | Больше конфигурации, нет встроенного ACME-клиента |
| sslh | Крошечный, специально для разделения порта между SSH/TLS/VPN | Только мультиплексирует; без терминации TLS и балансировки |
FAQ
QМожет ли Caddy проксировать TCP-трафик?
AНе из коробки. Базовый Caddy проксирует только HTTP; для сырого TCP или UDP нужна сборка Caddy со сторонним модулем caddy-l4 (layer4).
QКак проксировать SSH через Caddy?
AСоберите Caddy с caddy-l4, добавьте layer4-сервер, который распознаёт `ssh` и проксирует на `127.0.0.1:22`, затем подключайтесь через `ssh -p <port>`. На порту 443 это можно совместить с маршрутом `tls`, чтобы HTTPS продолжал работать.
QМогут ли SSH и HTTPS делить порт 443?
AДа. caddy-l4, sslh и HAProxy умеют анализировать первые байты соединения и направлять SSH и TLS на разные бэкенды на одном порту.
QПоддерживает ли caddy-l4 Caddyfile?
AПоддержка Caddyfile есть через глобальную опцию `layer4`, но JSON остаётся самым полным и лучше всего задокументированным форматом. Сверьтесь с README модуля для собираемой версии.