Рецепты обратного прокси и контракт callback
Каждому обратному прокси нужны те же две части: проверка авторизатора, которая спрашивает Caputchin, несёт ли запрос валидный пропуск, и callback на твоём источнике, который превращает решённый пропуск в cookie cpt_gate. Прохождение с nginx показывает форму от края до края; эта страница даёт другие прокси, точный контракт callback, и полный пример Authelia. Замени YOUR_SITE_KEY своим публичным ключом повсюду.
Эндпоинты везде одни и те же:
- Авторизатор:
GET https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY, с пересланным заголовкомCookieпосетителя.204значит разрешить,401значит испытание. - Испытание: перенаправь браузер на
https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<исходный URL>.
Traefik
Используй middleware forwardAuth для авторизатора. Traefik пересылает входящий заголовок Cookie по умолчанию. При 401 от middleware перенаправь браузер на испытание (middleware страницы ошибки, указывающий на маленький маршрут, который выдаёт 302, или обработай 401 на своём краю).
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
Используй forward_auth для авторизатора и handle_errors, чтобы превратить 401 в редирект. Это отправная точка; подстрой синтаксис директив под свою версию 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
}Callback
Размещённая страница испытания завершается автоотправкой формы POST на https://<твой-источник>/__cpt/callback с двумя полями в теле: cpt_gate (пропуском) и to (куда отправить посетителя). Добавь этот один маршрут на свой источник (или как отдельный location на прокси). Он должен:
- Читать
cpt_gateиtoиз тела POST, никогда из URL (пропуск в URL утекает в логи и рефереры). - Подтвердить, что
toна твоём собственном источнике, и отклонить всё остальное, чтобы callback нельзя было превратить в open redirect. - Поставить cookie с атрибутами ниже.
- Перенаправить (
303) наto.
// 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);
});Атрибуты cookie это вся модель безопасности. Поскольку пропуск не привязан к устройству, HttpOnly (блокирует кражу скриптом), Secure (только HTTPS), SameSite=Lax, и короткий Max-Age (в тон твоему TTL пропуска) это то, что его защищает. Ещё: не логируй значение cpt_gate в своих access-логах, и держи Referrer-Policy: no-referrer на callback, чтобы пропуск никогда не ехал попутчиком в заголовке реферера.
Разобранный пример: Authelia
Портал входа Authelia это скомпилированное одностраничное приложение без места, чтобы встроить виджет, и без хука плагина, так что барьер это способ его защитить. Authelia уже сидит за обратным прокси (так работает её собственный ForwardAuth), и этот прокси и есть место для барьера. Саму Authelia оставляют полностью без изменений.
Поставь барьер на vhost портала (например auth.example.com):
- Закрой HTML портала и эндпоинт входа первого фактора (
/api/firstfactor). - Исключи собственный verify-эндпоинт ForwardAuth Authelia (
/api/verify), который прокси вызывает на каждый защищённый upstream-запрос. Закрыть его сломало бы каждое защищённое приложение за Authelia. - Исключи health-check и статические ассеты, которые нужны самой странице испытания.
- Добавь маршрут
/__cpt/callbackкак выше.
Барьер держит ботов подальше от портала; встроенная Regulation Authelia (лимит повторов и бан) по-прежнему ограничивает повторы учётных данных для того, кто прошёл, так что два слоя складываются. Барьер защищает только интерактивный портал (UI входа и согласия); он не закрывает, и не должен, неинтерактивные эндпоинты токенов.
См. также
- Настрой прокси-барьер: прохождение с nginx и раскатка с предпросмотром-сначала.
- Обзор: концепция и cookie пропуска.
- Статистика: подтверди подключение, наблюдая испытание против прямого пропуска.