|
| 1 | +# Authentik plugin |
| 2 | + |
| 3 | +This [plugin](https://www.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=github) |
| 4 | +adds [Authentik](https://goauthentik.io/) forward authentication to a |
| 5 | +BunkerWeb site. It works on top of any existing service configuration — |
| 6 | +reverse proxy, served files, custom location blocks — without replacing them. |
| 7 | + |
| 8 | +The auth check runs from Lua during BunkerWeb's access phase, so all of |
| 9 | +BunkerWeb's built-in checks (rate limit, bad behavior, antibot, DNSBL, |
| 10 | +whitelist / blacklist, ...) run *before* the Authentik subrequest fires. |
| 11 | +Bots and rate-limited clients get denied without ever touching Authentik. |
| 12 | + |
| 13 | +# Table of contents |
| 14 | + |
| 15 | +- [Authentik plugin](#authentik-plugin) |
| 16 | +- [Table of contents](#table-of-contents) |
| 17 | +- [Request flow](#request-flow) |
| 18 | +- [Setup](#setup) |
| 19 | + - [Docker / Swarm](#docker--swarm) |
| 20 | + - [Authentik configuration](#authentik-configuration) |
| 21 | + - [Verifying it works](#verifying-it-works) |
| 22 | +- [Settings](#settings) |
| 23 | +- [Troubleshooting](#troubleshooting) |
| 24 | +- [Notes](#notes) |
| 25 | + |
| 26 | +# Request flow |
| 27 | + |
| 28 | +For a request to `https://app.example.com/something`: |
| 29 | + |
| 30 | +1. BunkerWeb's access-phase checks run (rate limit, bad behavior, antibot, |
| 31 | + DNSBL, blacklist, ...). If any of them deny, the request stops here. |
| 32 | +2. `authentik.lua` runs. If the URI is under `AUTHENTIK_OUTPOST_PATH` |
| 33 | + (default `/outpost.goauthentik.io`), it passes through untouched — that's |
| 34 | + the SSO flow itself, served by the outpost. |
| 35 | +3. Otherwise the handler does an HTTP `GET` against |
| 36 | + `<AUTHENTIK_URL>/outpost.goauthentik.io/auth/nginx`, forwarding the |
| 37 | + browser's cookies and `X-Original-URL`. |
| 38 | + - **200** — request continues to its normal destination (reverse proxy, |
| 39 | + file serving, custom location). Any `Set-Cookie` from Authentik is |
| 40 | + relayed to the client so the session refreshes correctly. |
| 41 | + - **401 / 403** — `302` to `<outpost_path>/start?rd=<original_url>`, which |
| 42 | + kicks off the SSO login. |
| 43 | +4. The server-level snippet (`confs/server-http/authentik.conf`) raises |
| 44 | + `proxy_buffers` / `proxy_buffer_size`, sets `port_in_redirect off`, and |
| 45 | + declares the `location <outpost_path>` block that proxies the SSO |
| 46 | + endpoints (`/auth`, `/start`, `/callback`, `/sign_out`, ...) back to the |
| 47 | + Authentik outpost. Keeping these on the protected site's own domain is |
| 48 | + what lets the proxy provider's session cookie be scoped correctly. |
| 49 | + |
| 50 | +# Setup |
| 51 | + |
| 52 | +See the [plugins section](https://docs.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=github) |
| 53 | +of the BunkerWeb documentation for the generic plugin installation procedure |
| 54 | +(the short version: drop the `authentik/` directory into the scheduler's |
| 55 | +`/data/plugins/` and restart). |
| 56 | + |
| 57 | +## Docker / Swarm |
| 58 | + |
| 59 | +`AUTHENTIK_URL` is the URL **BunkerWeb itself** uses to call Authentik — |
| 60 | +typically an internal Docker network address. Users still complete the |
| 61 | +login on Authentik's own public URL (configured separately in Authentik, |
| 62 | +not here). Both BunkerWeb and the user's browser need to be able to reach |
| 63 | +that public URL; otherwise login redirects from `/outpost.../start` go |
| 64 | +nowhere. |
| 65 | + |
| 66 | +```yaml |
| 67 | +services: |
| 68 | + |
| 69 | + bunkerweb: |
| 70 | + image: bunkerity/bunkerweb:1.6.0 |
| 71 | + ... |
| 72 | + networks: |
| 73 | + - bw-services |
| 74 | + - bw-authentik |
| 75 | + ... |
| 76 | + |
| 77 | + bw-scheduler: |
| 78 | + image: bunkerity/bunkerweb-scheduler:1.6.0 |
| 79 | + ... |
| 80 | + environment: |
| 81 | + SERVER_NAME: "app.example.com" |
| 82 | + USE_REVERSE_PROXY: "yes" |
| 83 | + REVERSE_PROXY_HOST: "http://app:3000" |
| 84 | + REVERSE_PROXY_URL: "/" |
| 85 | + |
| 86 | + USE_AUTHENTIK: "yes" |
| 87 | + # Internal URL — what BunkerWeb uses to call Authentik: |
| 88 | + AUTHENTIK_URL: "http://authentik-server:9000" |
| 89 | + |
| 90 | + authentik-server: |
| 91 | + # Must also be reachable on a public URL (e.g. https://authentik.example.com) |
| 92 | + # so users can complete the login flow. |
| 93 | + image: ghcr.io/goauthentik/server:latest |
| 94 | + ... |
| 95 | + networks: |
| 96 | + - bw-authentik |
| 97 | + |
| 98 | +networks: |
| 99 | + bw-services: |
| 100 | + name: bw-services |
| 101 | + bw-authentik: |
| 102 | + name: bw-authentik |
| 103 | +``` |
| 104 | +
|
| 105 | +## Authentik configuration |
| 106 | +
|
| 107 | +In the Authentik admin UI: |
| 108 | +
|
| 109 | +1. Create a **Proxy Provider** for the protected site in **Forward Auth |
| 110 | + (single application)** mode. *External host* should be the public URL of |
| 111 | + the protected site (e.g. `https://app.example.com`). |
| 112 | +2. Create or assign an **Application** that uses the provider. |
| 113 | +3. Attach the application to an **Outpost**. The built-in *authentik Embedded |
| 114 | + Outpost* is the simplest choice — `AUTHENTIK_URL` then points at the |
| 115 | + Authentik server itself (`http://authentik-server:9000` in the example |
| 116 | + above). For a standalone outpost, point `AUTHENTIK_URL` at that outpost's |
| 117 | + address instead. |
| 118 | +4. Make sure the Authentik server itself has a public URL (defined in |
| 119 | + *System → Brands* or via the `AUTHENTIK_HOST` env var). Browsers are |
| 120 | + redirected there to enter credentials. |
| 121 | + |
| 122 | +## Verifying it works |
| 123 | + |
| 124 | +1. Reload the scheduler. The Authentik plugin should appear in BunkerWeb's |
| 125 | + plugins list (web UI or scheduler logs). |
| 126 | +2. Visit a protected URL in a private window. You should land on the |
| 127 | + Authentik login page (note the URL — it's served by Authentik, not by |
| 128 | + BunkerWeb). |
| 129 | +3. After logging in, you should be redirected back to the protected URL and |
| 130 | + see the upstream service's response. |
| 131 | +4. In the Authentik server logs you should see one `/outpost.goauthentik.io/auth/nginx` |
| 132 | + call per protected request. If you see far more (e.g. one per static |
| 133 | + asset), the outpost-path skip isn't matching — double-check |
| 134 | + `AUTHENTIK_OUTPOST_PATH`. |
| 135 | + |
| 136 | +# Settings |
| 137 | + |
| 138 | +| Setting | Default | Context | Multiple | Description | |
| 139 | +| ----------------------------- | ------------------------ | --------- | -------- | -------------------------------------------------------------------------------------------------------- | |
| 140 | +| `USE_AUTHENTIK` | `no` | multisite | no | Activate Authentik forward authentication for this site. | |
| 141 | +| `AUTHENTIK_URL` | `` | multisite | no | Internal base URL BunkerWeb uses to reach the Authentik outpost. The plugin appends `/outpost.goauthentik.io/auth/nginx` for the subrequest and uses the same base for the outpost proxy. **Required when `USE_AUTHENTIK=yes`.** | |
| 142 | +| `AUTHENTIK_OUTPOST_PATH` | `/outpost.goauthentik.io`| multisite | no | Local URL path under which the outpost endpoints (`/auth`, `/start`, `/callback`, ...) are exposed on the protected site. Must start with `/`. Changing it does not change the upstream path. | |
| 143 | +| `AUTHENTIK_SSL_VERIFY` | `yes` | multisite | no | Verify the Authentik outpost TLS certificate. | |
| 144 | +| `AUTHENTIK_TIMEOUT` | `5000` | global | no | Timeout (ms) for the Lua auth subrequest. | |
| 145 | +| `AUTHENTIK_PROXY_BUFFER_SIZE` | `32k` | multisite | no | `proxy_buffer_size` for this server. Raise if Authentik headers overflow. | |
| 146 | +| `AUTHENTIK_PROXY_BUFFERS` | `8 16k` | multisite | no | `proxy_buffers` for this server. | |
| 147 | +| `AUTHENTIK_PASS_IDENTITY_HEADERS` | `no` | multisite | no | Forward Authentik's identity headers (`X-authentik-username`, `-groups`, `-email`, ...) to the upstream. The client-supplied copy of each listed header is stripped first to prevent spoofing. Enable only for trusted-header backends. | |
| 148 | +| `AUTHENTIK_IDENTITY_HEADERS` | `X-authentik-username X-authentik-groups X-authentik-email X-authentik-name X-authentik-uid` | multisite | no | Space/comma-separated list of headers forwarded (and stripped from the request) when the above is `yes`. Include every `X-authentik-*` header your backend trusts. | |
| 149 | + |
| 150 | +# Troubleshooting |
| 151 | + |
| 152 | +- **HTTP 500 on every protected URL, scheduler log says "AUTHENTIK_URL not |
| 153 | + configured".** `USE_AUTHENTIK=yes` is set but `AUTHENTIK_URL` is empty. |
| 154 | +- **`upstream sent too big header while reading response header from upstream`.** |
| 155 | + Raise `AUTHENTIK_PROXY_BUFFER_SIZE` (try `64k`) and/or `AUTHENTIK_PROXY_BUFFERS`. |
| 156 | +- **Login loop — browser cycles between the protected URL and the Authentik |
| 157 | + login page.** Almost always a cookie-domain mismatch. Confirm that |
| 158 | + `AUTHENTIK_OUTPOST_PATH` resolves on the *same domain* as the protected |
| 159 | + app, and that the Authentik proxy provider's *External host* matches that |
| 160 | + domain exactly (scheme included). |
| 161 | +- **`502` from the outpost path.** BunkerWeb can't reach `AUTHENTIK_URL` — |
| 162 | + check the Docker network membership and that the Authentik service is up. |
| 163 | +- **Auth subrequests time out.** Increase `AUTHENTIK_TIMEOUT`, or move the |
| 164 | + Authentik outpost closer to BunkerWeb (ideally same Docker network). |
| 165 | +- **Bots are still hitting Authentik.** They shouldn't be — `bad_behavior` |
| 166 | + and friends run before the Authentik subrequest. If you're seeing |
| 167 | + unauthenticated traffic at the outpost, it's likely the SSO redirect |
| 168 | + fanout from real users; check the Authentik logs by user agent. |
| 169 | + |
| 170 | +# Notes |
| 171 | + |
| 172 | +- **Identity headers downstream (opt-in).** By default this plugin only gates |
| 173 | + access and forwards nothing about the user to the upstream. If your backend |
| 174 | + uses trusted-header authentication (Nextcloud, Grafana header-auth, |
| 175 | + Bookstack, ...), set `AUTHENTIK_PASS_IDENTITY_HEADERS=yes` to relay |
| 176 | + Authentik's `X-authentik-*` headers from the auth response to the upstream. |
| 177 | + Customize the set via `AUTHENTIK_IDENTITY_HEADERS`. |
| 178 | + |
| 179 | + **Security:** every header in `AUTHENTIK_IDENTITY_HEADERS` is stripped from |
| 180 | + the incoming request *before* the Authentik values are applied, so a client |
| 181 | + cannot spoof an identity by sending its own `X-authentik-username` (etc.). |
| 182 | + Only headers Authentik actually returns are set; missing ones are left |
| 183 | + absent rather than carrying a client-supplied value. Make sure the list |
| 184 | + covers **every** identity header your backend trusts — anything not listed |
| 185 | + is neither forwarded nor stripped. |
| 186 | +- **Per-request cost.** Every gated request makes one HTTP call to the |
| 187 | + Authentik outpost's `/auth/nginx`. The outpost caches session lookups, so |
| 188 | + this is cheap — but keep `AUTHENTIK_URL` pointing at something nearby |
| 189 | + (same Docker network is ideal). |
| 190 | +- **Domain-level vs single-application mode.** This plugin assumes the |
| 191 | + *Forward Auth (single application)* provider mode. Domain-level mode |
| 192 | + (shared SSO cookie across `*.example.com`) needs additional Authentik |
| 193 | + configuration but works with the same plugin settings as long as |
| 194 | + `AUTHENTIK_OUTPOST_PATH` resolves on the protected domain. |
0 commit comments