Caputchin
代理门禁

用 nginx 设置代理门禁

查看 Markdown

到这篇教程结束时,门禁将运行在一个 nginx 之后的站点前面:一个未放行的访客被送去一局游戏,一局解开的游戏设置一个 Cookie,之后的请求直接通过。你会先在 预览模式 里观察整件事,这样在你确认接线之前不会拦下任何东西。这里用的是 nginx;Traefik、Caddy 和 Authelia 食谱 遵循同样的形状。

1. 打开预览模式

在仪表盘里,打开团队并打开 预览模式。它开着时,门禁授权方把挑战对直接放行的决定记录到 统计页,但绝不拦截。你会在最后把它关掉。

2. 在你的站点密钥上启用门禁

打开你的站点密钥,去 代理门禁 页。打开 状态 → 已启用,然后设置:

  • 通行 TTL:一次解题放行一个访客多久(默认 30 分钟)。更短意味着更多复查,但如果一个 Cookie 被偷,窗口更小。
  • 失败模式:当你的代理够不到 Caputchin 的授权方时它怎么做。失败即关闭(fail closed)拦截(对登录门户更安全);失败即开放(fail open)放行请求(在公开站点上避免停机)。

该页也展示一段随取随用的 nginx 片段,你的站点密钥已经填好在里面。

3. 允许你正在设门禁的源

挑战只会重定向回一个 被允许的源,就是组件所用的那份相同的源允许列表。确保它包含你正在设门禁的源,例如 https://auth.example.com。如果它还不在那里,就在站点密钥的 Cap 配置里设上它。

4. 接好授权方和挑战重定向

两个 location 块:一个内部的 授权方 子请求,和一个代理在 401 时跳去的 挑战 重定向。把 YOUR_SITE_KEY 换成你的公钥。

# Gate the protected paths.
location / {
  auth_request /_cpt_authz;             # 204 = allow, 401 = challenge
  error_page 401 = @cpt_challenge;
  # ...proxy_pass to your app...
}

location = /_cpt_authz {
  internal;
  proxy_pass https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY;
  proxy_pass_request_body off;
  proxy_set_header Content-Length "";
  proxy_set_header Cookie $http_cookie;  # forward the cpt_gate cookie
}

location @cpt_challenge {
  return 302 https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=$scheme://$host$request_uri;
}

给入口路径设门禁(门户的 HTML 和登录 POST)。不要 给授权方子请求、健康检查、或下面的回调设门禁。

挑战页在结束时会向你源上的 /__cpt/callback 自动提交一个小 POST,在 正文 里带着 cpt_gate(通行证)和 to(把访客送去哪里)。一个微小的处理器设置 Cookie 并重定向。完整契约,包括每个 Cookie 属性为什么重要,在 反向代理食谱 里。最低限度:

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);
});

6. 在预览里观察它

加载那个受门禁的 URL。因为预览开着,你直接通过,但 统计页 记录一个 challenge_served(你没有 Cookie),并且,一旦你解开题、回调跑起来,一个 pass_issued。刷新,你应该看到 passthrough 在爬升。如果挑战从不出现,那说明授权方块没被命中;如果回调返回 400,就检查 to 源。

7. 上线

当数字看起来对了,就把 预览模式 关掉。门禁现在开始执行:未放行的访客被送去游戏,解了题的访客漫游到他们的通行证过期为止。

接下来去哪

本页内容