Caputchin
Proxy-Gate

Reverse-Proxy-Rezepte und der Callback-Vertrag

Als Markdown ansehen

Jeder Reverse-Proxy braucht dieselben zwei Teile: eine Autorisierer-Prüfung, die Caputchin fragt, ob eine Anfrage einen gültigen Pass trägt, und einen Callback auf deinem Origin, der einen gelösten Pass in das cpt_gate-Cookie verwandelt. Die nginx-Durchgehung zeigt die Form von Ende zu Ende; diese Seite gibt die anderen Proxys, den genauen Callback-Vertrag, und ein volles Authelia-Beispiel. Ersetze YOUR_SITE_KEY durchgehend durch deinen öffentlichen Key.

Die Endpunkte sind überall dieselben:

  • Autorisierer: GET https://verify.caputchin.com/v1/gate/authz?site=YOUR_SITE_KEY, mit weitergeleitetem Cookie-Header des Besuchers. 204 heißt erlauben, 401 heißt Challenge.
  • Challenge: leite den Browser zu https://verify.caputchin.com/v1/gate/challenge?site=YOUR_SITE_KEY&return=<die ursprüngliche URL> um.

Traefik

Nutze eine forwardAuth-Middleware für den Autorisierer. Traefik leitet den eingehenden Cookie-Header standardmäßig weiter. Bei einem 401 von der Middleware leite den Browser zur Challenge um (eine Error-Page-Middleware, die auf eine kleine Route zeigt, die den 302 ausgibt, oder behandle den 401 an deinem 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-app

Caddy

Nutze forward_auth für den Autorisierer und handle_errors, um den 401 in eine Umleitung zu verwandeln. Das ist ein Ausgangspunkt; pass die Direktiven-Syntax an deine Caddy-Version an.

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
}

Der Callback

Die gehostete Challenge-Seite schließt ab, indem sie ein POST-Formular an https://<dein-origin>/__cpt/callback mit zwei Feldern im Body auto-absendet: cpt_gate (dem Pass) und to (wohin der Besucher geschickt wird). Füge diese eine Route zu deinem Origin hinzu (oder als eigene Location auf dem Proxy). Sie muss:

  1. cpt_gate und to aus dem POST-Body lesen, nie der URL (ein Pass in einer URL leckt in Logs und Referrer).
  2. Bestätigen, dass to auf deinem eigenen Origin liegt, und alles andere ablehnen, sodass der Callback nicht in einen Open Redirect verwandelt werden kann.
  3. Das Cookie mit den Attributen unten setzen.
  4. (303) zu to umleiten.
// 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);
});

Die Cookie-Attribute sind das ganze Sicherheitsmodell. Weil der Pass nicht an ein Gerät gebunden ist, sind HttpOnly (blockt Skript-Diebstahl), Secure (nur HTTPS), SameSite=Lax, und eine kurze Max-Age (passend zu deiner Freigabe-TTL) das, was ihn schützt. Außerdem: logge den cpt_gate-Wert nicht in deinen Access-Logs, und behalte Referrer-Policy: no-referrer auf dem Callback, sodass der Pass nie in einem Referrer-Header mitreist.

Durchgearbeitetes Beispiel: Authelia

Authelias Login-Portal ist eine kompilierte Single-Page-App ohne Platz, das Widget einzubetten, und ohne Plugin-Hook, also ist das Gate der Weg, es zu schützen. Authelia sitzt bereits hinter einem Reverse-Proxy (so funktioniert sein eigenes ForwardAuth), und dieser Proxy ist der Ort für das Gate. Authelia selbst bleibt völlig unverändert.

Setze das Gate auf den vhost des Portals (zum Beispiel auth.example.com):

  • Sichere das Portal-HTML und den First-Factor-Login-Endpunkt (/api/firstfactor) ab.
  • Schließe Authelias eigenen ForwardAuth-verify-Endpunkt (/api/verify) aus, den der Proxy bei jeder geschützten Upstream-Anfrage aufruft. Ihn abzusichern würde jede geschützte App hinter Authelia brechen.
  • Schließe Health-Checks und die statischen Assets aus, die die Challenge-Seite selbst braucht.
  • Füge die /__cpt/callback-Route wie oben hinzu.

Das Gate hält Bots vom Portal fern; Authelias eingebaute Regulation (Retry-Limit und Bann) deckelt weiterhin die Credential-Retries für jeden, der durchkommt, sodass die zwei Schichten sich stapeln. Das Gate schützt nur das interaktive Portal (die Login- und Consent-UI); es sichert die nicht-interaktiven Token-Endpunkte nicht ab, und sollte es auch nicht.

Siehe auch

  • Das Proxy-Gate einrichten: die nginx-Durchgehung und der Vorschaumodus-zuerst-Rollout.
  • Überblick: das Konzept und das Freigabe-Cookie.
  • Statistik: bestätige die Verdrahtung, indem du Challenge gegen Durchgang beobachtest.

Auf dieser Seite