docs/sso.md: the sign-in setup ipodderx.sdf1.net really runs
Authentik is Cloudflare Access's OpenID Connect identity provider, not something in the request path, and the tunnel's requests reach ipx from the content_default gateway, 192.168.16.1, not 127.0.0.1. The page is rewritten from what was measured, with checks for both the trusted and the refused path, and docs/history.md records every change made to get there with how to undo it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TAC7sLVqfKmY6rsTLXzNgk
This commit is contained in:
@@ -28,6 +28,9 @@ The long form, with what was wrong before and how it was found, is in
|
|||||||
|
|
||||||
- Show notes that the podcast's host cut off in the middle of a tag no longer open with a scrap of
|
- Show notes that the podcast's host cut off in the middle of a tag no longer open with a scrap of
|
||||||
HTML: the item's other copy of its notes is shown instead. Daily Meditation Podcast had 57.
|
HTML: the item's other copy of its notes is shown instead. Daily Meditation Podcast had 57.
|
||||||
|
- The sign-in guide, `docs/sso.md`, describes the setup ipodderx.sdf1.net really runs: Authentik as
|
||||||
|
Cloudflare Access's identity provider, and how to find the address ipx has to trust. It had never
|
||||||
|
been checked against a real setup, and pointed at the wrong address.
|
||||||
|
|
||||||
## [0.5.1] - 2026-09-12
|
## [0.5.1] - 2026-09-12
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Arcane project `content`: `/mnt/fast/arcane/projects/content/compose.yaml`. That
|
|||||||
| Database | `/mnt/user/ipodderx/state.db` | `/data/state.db` |
|
| Database | `/mnt/user/ipodderx/state.db` | `/data/state.db` |
|
||||||
| Downloads | `/mnt/user/ipodderx/downloads` | `/downloads` |
|
| Downloads | `/mnt/user/ipodderx/downloads` | `/downloads` |
|
||||||
| Web UI | `192.168.1.130:8099`, also `ipodderx.sdf1.net` via a Cloudflare tunnel | `0.0.0.0:8099` |
|
| Web UI | `192.168.1.130:8099`, also `ipodderx.sdf1.net` via a Cloudflare tunnel | `0.0.0.0:8099` |
|
||||||
|
| Sign-in via the tunnel | Cloudflare Access app `ipodderx`, with Authentik as its identity provider; see [docs/sso.md](docs/sso.md) | trusts `Cf-Access-Authenticated-User-Email` from `192.168.16.1`, the `content_default` gateway |
|
||||||
|
|
||||||
Deploying a change is: build and push the image, then pull it and recreate the container.
|
Deploying a change is: build and push the image, then pull it and recreate the container.
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,46 @@ reasoning lives. New write-ups go at the top.
|
|||||||
|
|
||||||
See [README.md](../README.md) for what the thing is.
|
See [README.md](../README.md) for what the thing is.
|
||||||
|
|
||||||
|
## 2026-09-12 — Signing in through Authentik, for real
|
||||||
|
|
||||||
|
Ray could not get Authentik's sign-in to reach ipx, following `docs/sso.md`, which had been written
|
||||||
|
without ever being tried. Looking at the Cloudflare account through its API showed that side was
|
||||||
|
already complete. Authentik is Zero Trust's OpenID Connect identity provider; the Access application
|
||||||
|
`ipodderx` allows only it and a list of five addresses; the tunnel `rays-unraid` routes
|
||||||
|
`ipodderx.sdf1.net` to `192.168.1.130:8099`; DNS is a proxied CNAME to the tunnel. Access's log
|
||||||
|
showed `rays@sdf1.net` signing in through it. Nothing on Cloudflare was changed, so no other site
|
||||||
|
was touched.
|
||||||
|
|
||||||
|
The gaps were all at ipx's end: `trusted_header` was empty, `trusted_proxies` held only loopback,
|
||||||
|
and the account was called `rays` while the header carries `rays@sdf1.net`.
|
||||||
|
|
||||||
|
Finding the address to trust took the most time. The page said `127.0.0.1`, but `cloudflared` runs in
|
||||||
|
its own container and reaches ipx through the host's published port. ipx logs no peer addresses, so
|
||||||
|
the address was read from `/proc/net/tcp` inside the ipx container: `192.168.16.1`, the gateway of
|
||||||
|
`content_default`, where Docker's masquerade puts traffic crossing from another bridge. A request
|
||||||
|
from Tower's own shell arrived as `192.168.1.130` instead, and a throwaway `busybox` on the default
|
||||||
|
bridge as `192.168.16.1`: the first was refused with the header, the second believed. LAN machines
|
||||||
|
keep their own addresses, since Docker forwards published ports with iptables (the userland proxy
|
||||||
|
only handles loopback).
|
||||||
|
|
||||||
|
Every change, in order, with how to undo it:
|
||||||
|
|
||||||
|
1. **Code**, commit `586d2c0`: `ipx user rename`, deployed. Revert the commit and redeploy to
|
||||||
|
remove it; nothing depends on it once used.
|
||||||
|
2. **Account**: `docker exec iPodderX ipx user rename rays rays@sdf1.net`. Same id, so its feeds,
|
||||||
|
read state, password and admin rights stayed. Undo: `docker exec iPodderX ipx user rename
|
||||||
|
rays@sdf1.net rays`. Signing in at `/login` now takes the new name.
|
||||||
|
3. **Config**, `/mnt/fast/appdata/ipodderx/config.toml`, `[web]`: `trusted_header` from `""` to
|
||||||
|
`"Cf-Access-Authenticated-User-Email"`, and `"192.168.16.1"` added to `trusted_proxies`. The
|
||||||
|
file as it was is `config.toml.2026-09-12-sso.bak` beside it. Undo: copy the backup back and
|
||||||
|
`docker compose -f /mnt/fast/arcane/projects/content/compose.yaml restart ipodderx`.
|
||||||
|
4. **Cloudflare, Authentik, Docker networks and other containers**: unchanged. The `busybox` test
|
||||||
|
container was removed when it exited, and its image afterwards.
|
||||||
|
|
||||||
|
What the address trusts is any container on Tower that connects through the host's port, not only
|
||||||
|
`cloudflared`. Verifying Cloudflare's signed `Cf-Access-Jwt-Assertion` would remove that, and is
|
||||||
|
the upgrade if it matters.
|
||||||
|
|
||||||
## 2026-09-12 — Trimming the state database
|
## 2026-09-12 — Trimming the state database
|
||||||
|
|
||||||
An audit of the database layer, with a read-only copy of production to check it against. The
|
An audit of the database layer, with a read-only copy of production to check it against. The
|
||||||
|
|||||||
288
docs/sso.md
288
docs/sso.md
@@ -1,9 +1,8 @@
|
|||||||
# Signing in through Cloudflare Zero Trust or Authentik
|
# Signing in through Cloudflare Access and Authentik
|
||||||
|
|
||||||
ipx can take the signed-in identity from whatever sits in front of it, instead of asking for a
|
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
|
password itself. The proxy authenticates the person and passes the result to ipx in a **header**;
|
||||||
pass the result to the origin in a **header**. ipx reads that header, finds (or creates) the
|
ipx reads it, finds (or creates) the matching account, and gets on with it.
|
||||||
matching account, and gets on with it.
|
|
||||||
|
|
||||||
Read [How this is secured](#how-this-is-secured) before exposing anything. The short version: a
|
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
|
header is worth exactly as much as the hop that set it, so ipx only believes one from an address you
|
||||||
@@ -11,195 +10,152 @@ list.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## The ipx side (both setups)
|
## How ipodderx.sdf1.net does it
|
||||||
|
|
||||||
|
Checked end to end on 2026-09-12. An earlier version of this page had never been tried against a
|
||||||
|
real setup and pointed at the wrong address.
|
||||||
|
|
||||||
|
```
|
||||||
|
browser ─► Cloudflare Access, app "ipodderx" ─── sign in ───► Authentik (OpenID Connect)
|
||||||
|
─► tunnel "rays-unraid" (the cloudflared container on Tower)
|
||||||
|
─► http://192.168.1.130:8099 ─► ipx
|
||||||
|
```
|
||||||
|
|
||||||
|
Authentik is not in the request path. It is the identity provider Cloudflare Access asks. Access
|
||||||
|
then adds `Cf-Access-Authenticated-User-Email`, the email address Authentik gave it, to every
|
||||||
|
request it forwards through the tunnel, and ipx signs that person in.
|
||||||
|
|
||||||
|
| Piece | Where | Setting |
|
||||||
|
|---|---|---|
|
||||||
|
| Identity provider | Zero Trust → Settings → Authentication | `Authentik`, OpenID Connect; scopes `openid email profile` |
|
||||||
|
| Access application | Zero Trust → Access → Applications → `ipodderx` | Domain `ipodderx.sdf1.net`; identity providers: Authentik only, with instant auth; session 730h; policy *Require Login* allows a list of email addresses |
|
||||||
|
| Tunnel route | Zero Trust → Networks → Tunnels → `rays-unraid` → Public hostnames | `ipodderx.sdf1.net` → HTTP `192.168.1.130:8099` |
|
||||||
|
| DNS | `sdf1.net` | `ipodderx` CNAME to the tunnel, proxied |
|
||||||
|
| ipx | `/mnt/fast/appdata/ipodderx/config.toml`, `[web]` | below |
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[web]
|
[web]
|
||||||
enabled = true
|
enabled = true
|
||||||
bind = "0.0.0.0:8099"
|
bind = "0.0.0.0:8099"
|
||||||
token = "…" # keep it: it is the admin, used by the healthcheck
|
trusted_header = "Cf-Access-Authenticated-User-Email"
|
||||||
|
trusted_proxies = ["127.0.0.1", "::1", "192.168.16.1"]
|
||||||
# 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
|
auto_create_users = true
|
||||||
|
|
||||||
session_days = 30
|
session_days = 30
|
||||||
```
|
```
|
||||||
|
|
||||||
Restart the daemon after editing. Accounts made this way have **no password**: they can only ever
|
Restart ipx after editing it: `docker compose -f /mnt/fast/arcane/projects/content/compose.yaml
|
||||||
arrive through the proxy. `ipx user list` marks them `proxy only`.
|
restart ipodderx`.
|
||||||
|
|
||||||
The first account created is an admin. Every later one is an ordinary user, and an ordinary user
|
### What was missing
|
||||||
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
|
Cloudflare and Authentik were already right. Three things on the ipx side were not:
|
||||||
subscriptions are per person. Adding a feed someone else already reads costs no second fetch and no
|
|
||||||
second copy on disk.
|
|
||||||
|
|
||||||
Promote someone with:
|
1. **`trusted_header` was empty**, which switches the whole proxy path off. ipx ignored the header
|
||||||
|
and asked for a password.
|
||||||
|
2. **`trusted_proxies` listed only `127.0.0.1`.** The tunnel's requests do not come from there;
|
||||||
|
see the next section.
|
||||||
|
3. **The account had the wrong name.** It was made by hand as `rays`, but the header carries
|
||||||
|
`rays@sdf1.net`. With `auto_create_users` on, the first visit would have made a second, empty
|
||||||
|
account. `ipx user rename rays rays@sdf1.net` fixed that without losing anything.
|
||||||
|
|
||||||
|
### The address to trust, and why it is 192.168.16.1
|
||||||
|
|
||||||
|
`cloudflared` runs in its own container and reaches ipx through the host's published port. Docker
|
||||||
|
(iptables firewall backend) masquerades traffic between its bridge networks, so the tunnel's
|
||||||
|
requests arrive from the **gateway of ipx's own network**, `content_default`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ipx user list
|
docker network inspect content_default -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'
|
||||||
echo -n 'a good password' | ipx user passwd <name> # 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
|
That was measured, not assumed. ipx does not log where a request came from, so the addresses were
|
||||||
when the tunnel is down. So does the shared `[web] token`, which signs in as the admin: that is
|
read from the kernel's connection table inside the container while the site was open. (`/proc/net/tcp`
|
||||||
what the Docker healthcheck uses, and the way back in if you lock yourself out. A brand new database
|
lists them in hex.)
|
||||||
starts with **admin / ipodderx** — change it.
|
|
||||||
|
If the `content` project's network is ever recreated, its gateway can change. Check it again, and
|
||||||
|
update `trusted_proxies` to match.
|
||||||
|
|
||||||
|
### Names
|
||||||
|
|
||||||
|
The username is the email address, lower-cased: `rays@sdf1.net`. To sign in at `/login` with a
|
||||||
|
password from the LAN, use that name too.
|
||||||
|
|
||||||
|
To let someone else in, add their address to the Access policy; they need an Authentik account with
|
||||||
|
that email. With `auto_create_users = true` they get an ipx account on their first visit, as an
|
||||||
|
ordinary user with no feeds. An account made before the proxy can be given the name the proxy will
|
||||||
|
send:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker exec iPodderX ipx user rename <old name> <email address>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check it
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# From Tower itself: not a trusted address, so the header is ignored.
|
||||||
|
curl -s -H 'Accept: application/json' -H 'Cf-Access-Authenticated-User-Email: rays@sdf1.net' \
|
||||||
|
http://192.168.1.130:8099/api/me # -> sign in
|
||||||
|
|
||||||
|
# From a container on a Docker bridge, as cloudflared is: believed.
|
||||||
|
docker run --rm --network bridge mirror.gcr.io/library/busybox wget -qO- \
|
||||||
|
--header 'Accept: application/json' --header 'Cf-Access-Authenticated-User-Email: rays@sdf1.net' \
|
||||||
|
http://192.168.1.130:8099/api/me # -> {"admin":true,"name":"rays@sdf1.net"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `https://ipodderx.sdf1.net` in a private window. Authentik should ask who you are, and
|
||||||
|
ipx should show `rays@sdf1.net` in the sidebar footer without asking for a password.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Cloudflare Zero Trust
|
## The ipx settings
|
||||||
|
|
||||||
This is what runs `ipodderx.sdf1.net`: a `cloudflared` tunnel to the origin, with an Access
|
| Key | What it does |
|
||||||
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` |
|
| `trusted_header` | The header the proxy sets. Empty, the default, turns the proxy path off. |
|
||||||
| Type | HTTP |
|
| `trusted_proxies` | The addresses allowed to set it. Nothing else is believed. |
|
||||||
| URL | `localhost:8099` (or the LAN address of the box) |
|
| `auto_create_users` | Make an account the first time the proxy vouches for a name ipx has not seen. |
|
||||||
|
| `session_days` | How long a password sign-in lasts without use. |
|
||||||
|
|
||||||
Use `localhost` when `cloudflared` runs on the same machine as ipx — that keeps the origin request
|
The first account ever created is an admin. Every later one is an ordinary user, who cannot change
|
||||||
coming from `127.0.0.1`, which is already in `trusted_proxies`. If `cloudflared` runs elsewhere (its
|
global settings, a feed's URL or folder, or how often feeds are scanned: the API refuses those with
|
||||||
own container, another host), put **its** address in `trusted_proxies` instead, and make sure
|
a `403`, not just the UI. Everything else about a feed is theirs alone; see [users.md](users.md).
|
||||||
nothing else can reach port 8099.
|
|
||||||
|
|
||||||
### 2. The Access application
|
Local sign-in at `/login` keeps working alongside the proxy, 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 and is the
|
||||||
**Zero Trust → Access → Applications → Add an application → Self-hosted**:
|
way back in if you lock yourself out. A brand new database starts with **admin / ipodderx**;
|
||||||
|
change it.
|
||||||
- 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 in the request path instead
|
||||||
|
|
||||||
Authentik does this with a **Proxy Provider** plus an **outpost**, which sits in the request path and
|
Not what ipodderx.sdf1.net uses, and **not verified**. Authentik can also sit in front of ipx
|
||||||
adds `X-authentik-username` (also `X-authentik-email`, `X-authentik-name`, `X-authentik-groups`).
|
itself, with a **Proxy Provider** and an **outpost** that adds `X-authentik-username`:
|
||||||
|
|
||||||
### 1. Provider
|
- Applications → Providers → Create → Proxy Provider; mode **Proxy** (the outpost talks to ipx) or
|
||||||
|
**Forward auth** (an existing reverse proxy asks the outpost).
|
||||||
**Applications → Providers → Create → Proxy Provider**:
|
- Applications → Create, bound to that provider, with a policy; add the provider to an outpost.
|
||||||
|
- In ipx: `trusted_header = "X-authentik-username"`, and the outpost's or reverse proxy's address
|
||||||
- Name: `ipx`
|
in `trusted_proxies`. Measure that address as above rather than guessing it.
|
||||||
- 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://<ip of the ipx box>: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
|
## How this is secured
|
||||||
|
|
||||||
**The header is only believed from `trusted_proxies`.** Every other source is ignored, and the
|
**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,
|
request falls through to a session cookie or the shared token. That 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;
|
With the tunnel reaching ipx through the host's port, `192.168.16.1` means **any container on Tower
|
||||||
the container's IP when it does not.
|
that connects to `192.168.1.130:8099`**, not only `cloudflared`. Machines on the LAN, and Tower
|
||||||
- Never list a LAN subnet. Anyone on your network could then send
|
itself, arrive under their own addresses and cannot set the header; the checks above show both
|
||||||
`Cf-Access-Authenticated-User-Email: admin@…` and be your admin.
|
sides. Never list a LAN address or range: anyone there could then send
|
||||||
- Make sure the origin port is not reachable *around* the proxy by anyone you would not admit
|
`Cf-Access-Authenticated-User-Email: rays@sdf1.net` and be you.
|
||||||
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
|
**What ipx does not do:** it does not verify Cloudflare's signed `Cf-Access-Jwt-Assertion`. It
|
||||||
header from your machine gets a `401`:
|
trusts the hop. Verifying the signature would make the containers on Tower irrelevant to the
|
||||||
|
boundary, and is the upgrade if that ever matters.
|
||||||
|
|
||||||
```sh
|
**Turning it off:** clear `trusted_header` and restart. Proxy-made accounts stay, but nobody can sign
|
||||||
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 <name>`).
|
in with them until they are given a password (`ipx user passwd <name>`).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -207,22 +163,20 @@ in with them until they are given a password (`ipx user passwd <name>`).
|
|||||||
## Everyday administration
|
## Everyday administration
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ipx user list # who exists, and how each one signs in
|
ipx user list # who exists, how each signs in, and when
|
||||||
echo -n 'secret123' | ipx user add sam # local account, password on stdin
|
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
|
ipx user add sam@example.com --no-password # proxy-only account, made ahead of time
|
||||||
|
ipx user rename sam sam@example.com # give an account the name the proxy sends
|
||||||
echo -n 'newsecret' | ipx user passwd sam # change a password
|
echo -n 'newsecret' | ipx user passwd sam # change a password
|
||||||
ipx user rm sam # remove the account
|
ipx user rm sam # remove the account
|
||||||
```
|
```
|
||||||
|
|
||||||
Set `auto_create_users = false` once everyone who should have an account has one. After that the
|
In the container, put `docker exec iPodderX` in front, and `docker exec -i iPodderX` for the ones
|
||||||
proxy vouching for an unknown name is logged and refused, rather than quietly making an account.
|
that read a password.
|
||||||
Pre-create people instead with `ipx user add <name> --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
|
Set `auto_create_users = false` once everyone who should have an account has one. After that the
|
||||||
**admin-only**: the Settings button is hidden for everyone else, and the API refuses the change even
|
proxy vouching for an unknown name is logged and refused. Make people ahead of time instead, with
|
||||||
if the request is made by hand. Everyone controls their own keywords, auto-download, explicit
|
the exact name the header will carry.
|
||||||
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)
|
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.
|
for every `[web]` key, and [cli.md](cli.md) for the `ipx user` commands.
|
||||||
|
|||||||
Reference in New Issue
Block a user