NoctisNode
← ГайдыСети2026-10-239 мин

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`, оба могут работать в одном процессе.

ЗадачаБазовый CaddyCaddy + 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"]}]}
              ]
            }
          ]
        }
      }
    }
  }
}

Запуск и проверка

  1. 01.Сохраните JSON как `/etc/caddy/caddy.json` и проверьте его.
  2. 02.Остановите сервис и запустите Caddy на переднем плане, чтобы видеть лог во время подключения.
  3. 03.Направьте unit systemd на JSON-файл через drop-in override, затем выполните `sudo systemctl daemon-reload` и `sudo systemctl start caddy`.
  4. 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 модуля для собираемой версии.

Caddy как TCP reverse proxy и SSH-прокси с caddy-l4 · NoctisNode