Receitas de proxy reverso e o contrato do callback
Todo proxy reverso precisa das mesmas duas peças: uma checagem autorizador que pergunta ao Caputchin se uma requisição leva um passe válido, e um callback na sua origem que transforma um passe resolvido no cookie cpt_gate. O passo a passo do nginx mostra a forma de ponta a ponta; esta página dá os outros proxies, o contrato exato do callback, e um exemplo completo do Authelia. Substitua YOUR_SITE_KEY pela sua chave pública em toda parte.
Os endpoints são os mesmos em toda parte:
- Autorizador:
GET https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY, com o cabeçalhoCookiedo visitante encaminhado.204significa permitir,401significa desafio. - Desafio: redirecione o navegador para
https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<a URL original>.
Traefik
Use um middleware forwardAuth para o autorizador. O Traefik encaminha o cabeçalho Cookie que chega por padrão. Em um 401 do middleware, redirecione o navegador para o desafio (um middleware de página de erro apontado para uma pequena rota que emite o 302, ou trate o 401 na sua edge).
http:
middlewares:
cpt-gate:
forwardAuth:
address: "https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY"
# The 401 from forwardAuth is your signal to send the browser to:
# https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<original-url>
routers:
portal:
rule: "Host(`auth.example.com`)"
middlewares: ["cpt-gate"]
service: your-appCaddy
Use forward_auth para o autorizador e handle_errors para transformar o 401 em um redirecionamento. Isto é um ponto de partida; ajuste a sintaxe das diretivas para a sua versão do Caddy.
auth.example.com {
forward_auth https://verify.caputchin.com {
uri /v1/gate/authz?site=YOUR_SITE_KEY
copy_headers Cookie
}
handle_errors {
@challenge expression {http.error.status_code} == 401
redir @challenge https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return={scheme}://{host}{uri} 302
}
reverse_proxy your-app:8080
}O callback
A página de desafio hospedada termina auto-enviando um formulário POST para https://<sua-origem>/__cpt/callback com dois campos no corpo: cpt_gate (o passe) e to (para onde enviar o visitante). Adicione esta única rota à sua origem (ou como uma location dedicada no proxy). Ela deve:
- Ler
cpt_gateetodo corpo do POST, nunca da URL (um passe em uma URL vaza para logs e referenciadores). - Confirmar que
toestá na sua própria origem, e rejeitar qualquer outra coisa, para que o callback não possa ser transformado em um open redirect. - Definir o cookie com os atributos abaixo.
- Redirecionar (
303) parato.
// Any tiny handler on your origin works. Express shown; ~15 lines.
app.post("/__cpt/callback", express.urlencoded({ extended: false }), (req, res) => {
const pass = String(req.body.cpt_gate || "");
const to = String(req.body.to || "/");
const target = new URL(to, `https://${req.headers.host}`);
if (target.host !== req.headers.host) return res.status(400).end(); // same-origin only
res.setHeader(
"Set-Cookie",
`cpt_gate=${pass}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=1800`
);
res.redirect(303, target.pathname + target.search);
});Os atributos do cookie são todo o modelo de segurança. Como o passe não está amarrado a um dispositivo, HttpOnly (bloqueia o roubo por script), Secure (só HTTPS), SameSite=Lax, e um Max-Age curto (a casar com a sua TTL de liberação) são o que o protege. Além disso: não registre o valor cpt_gate nos seus access logs, e mantenha Referrer-Policy: no-referrer no callback para que o passe nunca viaje de carona em um cabeçalho referenciador.
Exemplo trabalhado: Authelia
O portal de login do Authelia é uma app mono-página compilada sem lugar para incrustar o widget e sem hook de plugin, então a porta é o jeito de protegê-lo. O Authelia já fica atrás de um proxy reverso (é assim que o próprio ForwardAuth dele funciona), e esse proxy é onde a porta vai. O Authelia em si fica completamente sem modificação.
Ponha a porta no vhost do portal (por exemplo auth.example.com):
- Ponha porta no HTML do portal e no endpoint de login de primeiro fator (
/api/firstfactor). - Exclua o endpoint verify do próprio ForwardAuth do Authelia (
/api/verify), que o proxy chama para toda requisição upstream protegida. Pôr porta nele quebraria toda app protegida atrás do Authelia. - Exclua os health checks e os assets estáticos de que a própria página do desafio precisa.
- Adicione a rota
/__cpt/callbackcomo acima.
A porta mantém os bots fora do portal; a Regulation embutida do Authelia (limite de retentativas e banimento) ainda limita as retentativas de credenciais de quem passa, então as duas camadas se empilham. A porta protege só o portal interativo (a UI de login e consentimento); ela não põe porta, e não deve, nos endpoints de token não interativos.
Veja também
- Configure a porta de proxy: o passo a passo do nginx e o rollout com modo-prévia-primeiro.
- Visão geral: o conceito e o cookie de liberação.
- Estatísticas: confirme a ligação observando desafio contra passagem direta.