# 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 or how often feeds are scanned. 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. 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. Scanning intervals, the disk quota, retention and the download folder 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. Ordinary users still control their own folders, keywords and downloads per feed.