# Signing in through Cloudflare Zero Trust or Authentik ipx can take the signed-in identity from whatever sits in front of it, instead of asking for a password itself. Both products below do the same thing in the end: they authenticate the person and pass the result to the origin in a **header**. ipx reads that header, finds (or creates) the matching account, and gets on with it. Read [How this is secured](#how-this-is-secured) before exposing anything. The short version: a header is worth exactly as much as the hop that set it, so ipx only believes one from an address you list. --- ## The ipx side (both setups) ```toml [web] enabled = true bind = "0.0.0.0:8099" token = "…" # keep it: it is the admin, used by the healthcheck # The header your proxy sets. Empty (the default) disables this whole path. trusted_header = "Cf-Access-Authenticated-User-Email" # Authentik: "X-authentik-username" # Addresses allowed to assert that header -- the proxy, and nothing else. trusted_proxies = ["127.0.0.1", "::1"] # Create an account the first time the proxy vouches for a name ipx has not seen. auto_create_users = true session_days = 30 ``` Restart the daemon after editing. Accounts made this way have **no password**: they can only ever arrive through the proxy. `ipx user list` marks them `proxy only`. The first account created is an admin. Every later one is an ordinary user, and an ordinary user cannot change global settings, a feed's URL or folder, or how often feeds are scanned: the API refuses those with a `403`, not just the UI. Everything else about a feed (which items they want, whether to fetch them, how many at a time) is theirs alone; see [users.md](users.md). Somebody arriving through the proxy for the first time starts with **no feeds**, because subscriptions are per person. Adding a feed someone else already reads costs no second fetch and no second copy on disk. Promote someone with: ```sh ipx user list echo -n 'a good password' | ipx user passwd # optional: also lets them sign in directly ``` Local sign-in at `/login` keeps working alongside all of this, which is how you get in from the LAN when the tunnel is down. So does the shared `[web] token`, which signs in as the admin: that is what the Docker healthcheck uses, and the way back in if you lock yourself out. A brand new database starts with **admin / ipodderx** — change it. --- ## Cloudflare Zero Trust This is what runs `ipodderx.sdf1.net`: a `cloudflared` tunnel to the origin, with an Access application in front of it. Cloudflare authenticates the visitor and adds `Cf-Access-Authenticated-User-Email` to every request it forwards. ### 1. The tunnel In **Zero Trust → Networks → Tunnels**, either use the existing tunnel or create one, then add a public hostname: | Field | Value | |---|---| | Subdomain / domain | `ipodderx` / `sdf1.net` | | Type | HTTP | | URL | `localhost:8099` (or the LAN address of the box) | Use `localhost` when `cloudflared` runs on the same machine as ipx — that keeps the origin request coming from `127.0.0.1`, which is already in `trusted_proxies`. If `cloudflared` runs elsewhere (its own container, another host), put **its** address in `trusted_proxies` instead, and make sure nothing else can reach port 8099. ### 2. The Access application **Zero Trust → Access → Applications → Add an application → Self-hosted**: - Application domain: `ipodderx.sdf1.net` - Session duration: whatever suits; ipx keeps its own 30-day session on top. - Add a policy — *Allow*, with a rule such as `Emails` → your address, or `Emails ending in` → your domain. Anyone this policy admits gets an ipx account when `auto_create_users` is on, so keep the policy as narrow as the people you actually want reading your feeds. ### 3. Point ipx at the header ```toml trusted_header = "Cf-Access-Authenticated-User-Email" trusted_proxies = ["127.0.0.1", "::1"] ``` The username becomes the email address, lower-cased (`ray@example.com`). That is what shows in the sidebar and what `ipx user list` prints. ### 4. Check it ```sh # From the box itself: no header, no session -> the sign-in page. curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' -H 'Accept: text/html' http://127.0.0.1:8099/ # Pretending to be the tunnel (only works because 127.0.0.1 is trusted): curl -s -H 'Cf-Access-Authenticated-User-Email: you@example.com' http://127.0.0.1:8099/api/me ``` Then load `https://ipodderx.sdf1.net` in a browser: Cloudflare should ask who you are, and ipx should show your address in the sidebar footer without ever asking for a password. --- ## Authentik Authentik does this with a **Proxy Provider** plus an **outpost**, which sits in the request path and adds `X-authentik-username` (also `X-authentik-email`, `X-authentik-name`, `X-authentik-groups`). ### 1. Provider **Applications → Providers → Create → Proxy Provider**: - Name: `ipx` - Authorization flow: your usual (`default-provider-authorization-implicit-consent`) - Mode: **Forward auth (single application)** if an existing reverse proxy fronts ipx, or **Proxy** to let the outpost talk to ipx directly. - External host: `https://ipodderx.example.net` - Internal host (Proxy mode): `http://:8099` ### 2. Application and outpost **Applications → Create**, bind it to that provider, and give it a policy so only the people you mean are let through. Then add the provider to an outpost (**Applications → Outposts**, the embedded one is fine). ### 3. Forward auth, if you use nginx/SWAG in front In the server block for ipx: ```nginx location /outpost.goauthentik.io { proxy_pass http://authentik-server:9000/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; } location / { auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; # This is the line that matters to ipx. auth_request_set $authentik_username $upstream_http_x_authentik_username; proxy_set_header X-authentik-username $authentik_username; proxy_pass http://ipx:8099; } ``` ### 4. Point ipx at the header ```toml trusted_header = "X-authentik-username" trusted_proxies = ["172.18.0.5"] # the outpost or nginx container, NOT a whole subnet ``` Usernames arrive as Authentik knows them (`ray`), lower-cased. --- ## How this is secured **The header is only believed from `trusted_proxies`.** Every other source is ignored, and the request falls through to a session cookie or the shared token. This is the whole security boundary, so: - List the **proxy's own address**, not a range. `["127.0.0.1"]` when the tunnel runs beside ipx; the container's IP when it does not. - Never list a LAN subnet. Anyone on your network could then send `Cf-Access-Authenticated-User-Email: admin@…` and be your admin. - Make sure the origin port is not reachable *around* the proxy by anyone you would not admit through it. If it is, bind ipx to `127.0.0.1` and let only the proxy reach it. Verify the refusal, don't assume it — set `trusted_proxies = ["10.9.9.9"]` briefly and confirm a header from your machine gets a `401`: ```sh curl -s -o /dev/null -w '%{http_code}\n' \ -H 'Cf-Access-Authenticated-User-Email: someone@example.com' http://127.0.0.1:8099/api/me ``` **What ipx does not do:** it does not verify Cloudflare's `Cf-Access-Jwt-Assertion` signature or Authentik's session. It trusts the hop. That is a deliberate trade — it keeps the configuration to three lines — and it is sound exactly as long as the point above holds. **Turning it off:** clear `trusted_header`. Existing proxy-only accounts stay, but nobody can sign in with them until they are given a password (`ipx user passwd `). --- ## Everyday administration ```sh ipx user list # who exists, and how each one signs in echo -n 'secret123' | ipx user add sam # local account, password on stdin ipx user add sam --no-password # proxy-only account, created ahead of time echo -n 'newsecret' | ipx user passwd sam # change a password ipx user rm sam # remove the account ``` Set `auto_create_users = false` once everyone who should have an account has one. After that the proxy vouching for an unknown name is logged and refused, rather than quietly making an account. Pre-create people instead with `ipx user add --no-password`, using exactly the name the header will carry (Cloudflare sends the email address, lower-cased). Scanning intervals, the disk quota, retention, the download folder and a feed's URL are **admin-only**: the Settings button is hidden for everyone else, and the API refuses the change even if the request is made by hand. Everyone controls their own keywords, auto-download, explicit setting and per-scan cap, along with their own read state and which feeds they see. See also [users.md](users.md) for what several people share, [configuration.md](configuration.md) for every `[web]` key, and [cli.md](cli.md) for the `ipx user` commands.