Caputchin
代理页面门禁

用 nginx 设置代理页面门禁

查看 Markdown

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

1. 打开预览模式

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

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

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

  • 挑战类型:玩一个游戏(默认)或 勾选复选框确认。一个要求游戏的站点密钥总是展示游戏,页面上也会这么说。选游戏需要至少一个已安装、带可重放产物的游戏。
  • 通行 TTL:一次解题放行一个访客多久(默认 30 分钟)。更短意味着更多复查,但如果一个 Cookie 被偷,窗口更小。
  • 失败模式:当你的代理够不到 Caputchin 的授权方时它怎么做。失败即关闭(fail closed)拦截(对登录门户更安全);失败即开放(fail open)放行请求(在公开站点上避免停机)。

把 语言 和 主题 留在 跟随访客:验证就会说访客的语言,跟随他的明暗偏好,并自动带上这个站点密钥的品牌。在 高级 下,你可以给门禁 Cookie 改名、挪动回调路径、并把门禁限定到特定路径;本篇演练用的是默认值。见 自定义挑战。

该页也展示一段随取随用的 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_intercept_errors on;
  error_page 500 502 503 504 = @cpt_unreachable;
  # ...proxy_pass to your app...
}

location = /_cpt_authz {
  internal;
  auth_request off;
  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
  proxy_connect_timeout 2s;
  proxy_read_timeout 2s;
}

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

# This is your fail mode. `return 403` blocks when Caputchin is unreachable
# (fail closed); `return 204` lets the request through (fail open).
location @cpt_unreachable {
  return 403;
}

授权方只会回 204 或 401,所以其他任何东西,包括超时,都意味着 Caputchin 不可达,由你的失败模式来决定。从仪表盘复制那段片段,它已经和你选的失败模式对上了。

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

验证页在结束时会向你源上的 /__cpt/callback(或你设定的那个回调路径)自动提交一个小 POST,在 正文 里带着 cpt_gate(通行证)和 to(把访客送去哪里)。即使你给 Cookie 改了名,这个字段仍叫 cpt_gate。一个微小的处理器设置 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. 上线

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

接下来去哪

本页内容