NoctisNode
← GuiasRedes2026-10-239 min

Reverse proxy TCP e proxy SSH no Caddy com caddy-l4

O Caddy padrão só faz proxy de HTTP. Para TCP bruto e SSH, compile o Caddy com o app layer4 do caddy-l4, encaminhe portas ou compartilhe a porta 443 entre SSH e HTTPS por protocolo.

Resposta curta: o Caddy padrão não consegue fazer proxy de TCP bruto nem de SSH, porque o `reverse_proxy` dele só fala HTTP. Para encaminhar TCP, você compila o Caddy com o módulo caddy-l4 (github.com/mholt/caddy-l4) usando o xcaddy e depois configura o app `layer4` para escutar numa porta e repassar as conexões, podendo escolher o upstream pelo protocolo — assim SSH e HTTPS podem compartilhar a porta 443.

O caddy-l4 é um módulo separado, não faz parte da distribuição padrão do Caddy, e o README dele ainda o descreve como um trabalho em andamento cuja configuração pode mudar entre versões. Este guia usa configuração em JSON, que corresponde diretamente à estrutura documentada do módulo. Se você só precisa de proxy HTTP ou HTTPS, o guia de reverse proxy com Caddy é tudo de que precisa.

Por que o Caddy padrão é só HTTP

Os apps embutidos do Caddy são `http`, `tls` e `pki`. O handler `reverse_proxy` fica dentro do app HTTP, então ele precisa de uma requisição HTTP já interpretada antes de rotear qualquer coisa. Um cliente SSH, um cliente PostgreSQL ou um protocolo de jogo nunca envia uma, e o Caddy responderia com um erro de TLS ou HTTP.

O caddy-l4 adiciona um app `layer4` separado que trabalha com conexões brutas. Ele consegue identificar os primeiros bytes de uma conexão (banner SSH, ClientHello TLS com SNI, linha de requisição HTTP, startup do PostgreSQL e mais) e então fazer proxy, terminar o TLS ou entregar o fluxo a outro handler. Como é um app igual ao `http`, os dois podem rodar no mesmo processo.

NecessidadeCaddy padrãoCaddy + caddy-l4
Reverse proxy HTTPS com TLS automáticoSimSim
Encaminhamento de porta TCP simplesNãoSim (handler `proxy`)
Encaminhamento UDPNãoSim (escutar em `udp/:port`)
TLS passthrough por SNINãoSim (matcher `tls` com `sni`)
SSH e HTTPS na mesma portaNãoSim (matchers `ssh` e `tls`)
Suporte a CaddyfileCompletoParcial; JSON é o formato de referência

Compile o Caddy com o módulo layer4

O xcaddy compila o Caddy com módulos extras. Como alternativa, selecione `mholt/caddy-l4` na página oficial de download do Caddy e baixe um binário pronto. De qualquer forma, confirme com `list-modules` que os módulos `layer4` aparecem antes de escrever a configuração.

# 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

Substitua o binário do pacote com segurança

Se o Caddy foi instalado pelo repositório apt oficial, a forma documentada de colocar um build personalizado é `dpkg-divert` mais `update-alternatives`, para que as atualizações do pacote não sobrescrevam o seu binário. Lembre-se de que, a partir de agora, você precisa recompilar para receber atualizações do 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

Exemplo 1: reverse proxy TCP simples

Esta configuração escuta na porta 5432 e encaminha cada conexão para um servidor PostgreSQL num endereço privado, como a outra ponta de um túnel WireGuard. Sem matcher, toda conexão segue a rota. Os upstreams aceitam vários endereços `dial` para failover, e o handler `proxy` oferece balanceamento de carga e health checks parecidos com os do `reverse_proxy` HTTP.

O backend vê o IP do Caddy, não o do cliente. Se o backend entende o PROXY protocol, defina `"proxy_protocol": "v2"` no handler de proxy para repassar o endereço original.

{
  "apps": {
    "layer4": {
      "servers": {
        "postgres": {
          "listen": [":5432"],
          "routes": [
            {
              "handle": [
                {
                  "handler": "proxy",
                  "upstreams": [{"dial": ["10.8.0.2:5432"]}]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Exemplo 2: SSH e HTTPS multiplexados na porta 443

Clientes SSH enviam o banner de versão (`SSH-2.0-...`) assim que se conectam, então o caddy-l4 consegue reconhecer o SSH antes de qualquer resposta do servidor. A configuração abaixo envia o SSH para o sshd local, o TLS de `git.example.com` para um serviço que termina o próprio TLS e todo o resto do TLS para o app HTTP do Caddy escutando na 8443. Como agora o layer4 ocupa a porta 443, o app HTTP não pode se vincular a ela também: defina `https_port 8443` nas opções globais do Caddyfile, ou o endereço `listen` equivalente em JSON, e mantenha a porta 80 aberta para os desafios HTTP-01. Com `https_port 8443`, o redirecionamento automático de HTTP para HTTPS do Caddy apontaria para a porta 8443, então defina `auto_https disable_redirects` e adicione seu próprio site `http:// { redir https://{host}{uri} permanent }`.

Protocolos em que o servidor fala primeiro, como SMTP, FTP ou MySQL, não podem ser detectados assim, porque o cliente espera em silêncio. Dê a eles uma porta própria.

{
  "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"]}]}
              ]
            }
          ]
        }
      }
    }
  }
}

Execute e teste

  1. 01.Salve o JSON como `/etc/caddy/caddy.json` e valide-o.
  2. 02.Pare o serviço e rode o Caddy em primeiro plano para acompanhar o log enquanto se conecta.
  3. 03.Aponte a unit do systemd para o arquivo JSON com um override drop-in e depois rode `sudo systemctl daemon-reload` e `sudo systemctl start caddy`.
  4. 04.Conecte com `ssh -p 443` e abra o site HTTPS num navegador para confirmar que as duas rotas funcionam.
# 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 usar HAProxy ou sslh em vez disso

O caddy-l4 é uma boa escolha quando você já roda o Caddy e quer um único processo para HTTPS e algumas rotas TCP. Para um setup centrado em TCP, alternativas maduras dos repositórios do Debian e do Ubuntu podem ser mais simples de operar: o HAProxy em `mode tcp` roteia por SNI com `req.ssl_sni` e tem estatísticas e health checks detalhados, enquanto o sslh é um daemon pequeno feito só para compartilhar uma porta entre SSH, TLS, OpenVPN e outros. Ambos são instalados com apt e atualizados junto com o resto do sistema, sem build personalizado para manter.

Expor o SSH na 443 ajuda em redes que só liberam portas web, mas não é um recurso de segurança. Mantenha a autenticação só por chave do guia de hardening de SSH. Num VPS NoctisNode você tem root completo, então qualquer uma dessas ferramentas pode se vincular às portas de que você precisa; para o comportamento dos certificados no lado HTTPS, veja HTTPS automático no Caddy e, para containers, Caddy como reverse proxy no Docker.

FerramentaPontos fortesContras
Caddy + caddy-l4Um processo para HTTPS automático e TCP, matchers de protocolo, API JSONBuild personalizado, módulo ainda em evolução
HAProxyProxy TCP/HTTP maduro, roteamento por SNI, health checks e estatísticas ricosMais configuração, sem cliente ACME embutido
sslhMinúsculo, feito sob medida para compartilhar porta entre SSH/TLS/VPNSó multiplexa; sem terminação TLS nem balanceamento de carga

Perguntas frequentes

QO Caddy consegue fazer proxy de tráfego TCP?

ANão de fábrica. O Caddy padrão só faz proxy de HTTP; para TCP ou UDP bruto você precisa de um build do Caddy que inclua o módulo de terceiros caddy-l4 (layer4).

QComo faço proxy de SSH pelo Caddy?

ACompile o Caddy com o caddy-l4, adicione um servidor layer4 que identifique `ssh` e faça proxy para `127.0.0.1:22` e conecte com `ssh -p <port>`. Na porta 443 você pode combiná-lo com uma rota `tls` para que o HTTPS continue funcionando.

QSSH e HTTPS podem compartilhar a porta 443?

ASim. O caddy-l4, o sslh e o HAProxy conseguem inspecionar os primeiros bytes de uma conexão e rotear SSH e TLS para backends diferentes na mesma porta.

QO caddy-l4 suporta Caddyfile?

AEle tem suporte a Caddyfile por meio de uma opção global `layer4`, mas o JSON continua sendo o formato mais completo e mais bem documentado. Consulte o README do módulo para a versão que você compilar.

Reverse proxy TCP e proxy SSH no Caddy com caddy-l4 · NoctisNode