auth¶
how people sign in to each app, on the lan and from outside, and what MFA each way in has. my accounts and their MFA are in entra ID, and entra is the MFA for any name outside that has none of its own.
three access paths¶
- direct: the app's own address and port, on the lan only
- traefik on the lan:
https://<name>.mydomain.com, on 443 at the swarm's VIP - traefik outside: the same name from the internet. the router forwards its 443 to traefik's 1444, see traefik
the rules¶
- outside, every name has MFA or is a public site. an app with MFA of its own keeps it, and every other name goes behind oauth2-proxy, so users sign in with entra first
- on the lan, oauth2-proxy is also in front of the names that give control of something with no login, or only a password: the zigbee and z-wave UIs, dozzle's container logs, apprise's notification tokens, the unifi API browser's stored login, traefik's dashboard, seerr and the arr apps
- an app with native oauth (entra), signing its users in with entra itself, needs no oauth2-proxy in front: proxmox, PBS, portainer and grafana
- oauth2-proxy protects the name only. on the lan an app's own port still answers, with the app's own login, so that login still matters
- jellyfin has no entra sign-in. its TV and phone apps can't pass one
two kinds of entra sign-in¶
entra is the provider for both here. oauth2-proxy and each of these apps also work with other OpenID Connect providers.
| oauth2-proxy (entra) | native oauth (entra) | |
|---|---|---|
| who asks | traefik, through oauth2-proxy, before the app sees the request | the app, on its login page |
| after it | the app's own login, if it has one | nothing: the app maps the entra user to a user of its own |
| app registration | one, shared by every name behind oauth2-proxy | one per app |
| works on | the name, through traefik | any address the app's redirect URIs list |
entra ID has each app registration and where it's set up.
every app¶
the table is written by the same script as traefik's routes, from the same registry, so it changes when a route does. it lists only the names traefik serves, and its columns are grouped by the three access paths.
- each row is a name,
<name>.mydomain.com, unless it shows another in brackets - the direct columns are the app's own login and MFA, which apply whichever way you reach it
- sign-in and MFA on each traefik path are what you meet there: the app's own,
with
oauth2-proxy (entra), thenin front where traefik asks oauth2-proxy first - entra's MFA is whatever my conditional access policy requires
not recordedmeans the registry doesn't say yet
| name | direct | traefik on the lan | traefik outside | ||||
|---|---|---|---|---|---|---|---|
| address | its own login | MFA | sign-in | MFA | sign-in | MFA | |
| adguard1 | 192.168.1.5:80 | password | none | password | none | not served | |
| adguard2 | 192. | password | none | password | none | not served | |
| apex (mydomain.com) | 192. | WordPress password login, at a custom address (wp-login.php answers 404); the network admin is here | not recorded | none to read it; WordPress password login, at a custom address (wp-login.php answers 404); the network admin is here | not recorded | none: every path redirects to https: | |
| apprise | 192. | none | none | oauth2-proxy (entra) | entra | not served | |
| auth (the sign-in) | 192. | oauth2-proxy (entra): it is the sign-in | entra | oauth2-proxy (entra): it is the sign-in | entra | oauth2-proxy (entra): it is the sign-in | entra |
| bazarr | 192. | password (form) | none | oauth2-proxy (entra), then password (form) | entra | not served | |
| bentopdf | 192. | none | none | none | none | not served | |
| blog | 192. | WordPress password login (wp-login.php) | not recorded | none to read it; WordPress password login (wp-login.php) | not recorded | none to read it; WordPress password login (wp-login.php) | not recorded |
| dozzle | 192. | none | none | oauth2-proxy (entra) | entra | not served | |
| gatus | 192. | none | none | none | none | not served | |
| glances-docker01 | 192. | none | none | none | none | not served | |
| glances-docker02 | 192. | none | none | none | none | not served | |
| glances-docker03 | 192. | none | none | none | none | not served | |
| glances-pi-zwave01 | 192. | none | none | none | none | not served | |
| glances-pve1 | 192. | none | none | none | none | not served | |
| glances-pve2 | 192. | none | none | none | none | not served | |
| glances-pve3 | 192. | none | none | none | none | not served | |
| glances-syn02 | 192. | none | none | none | none | not served | |
| glances-truenas1 | 192. | none | none | none | none | not served | |
| grafana | 192. | native oauth (entra), grafana's Azure AD setting; password form | entra; the password form has none | native oauth (entra), grafana's Azure AD setting; password form | entra; the password form has none | not served | |
| ha | 192. | password | authenticator-app module: not yet confirmed for every user | not served | password | authenticator-app module: not yet confirmed for every user | |
| homepage | 192. | none | none | none | none | oauth2-proxy (entra) | entra |
| infinitude | 192. | none | none | none | none | not served | |
| jellyfin | 192. | password | none | password | none | not served | |
| npm | 192. | password (NPM's admin) | none | password (NPM's admin) | none | not served | |
| nvr | 192. | password | none | password | none | not served | |
| omni-tools | 192. | none | none | none | none | not served | |
| open-webui | 192. | password (sign-up off) | none | password (sign-up off) | none | not served | |
| pbs | pbs1. | native oauth (entra), realm 'EntraID'; password realms pam and pbs | entra; the password realms: not recorded (per-user TOTP) | native oauth (entra), realm 'EntraID'; password realms pam and pbs | entra; the password realms: not recorded (per-user TOTP) | not served | |
| pbs-restore | 192. | Proxmox's sign-in, per user: native oauth (entra) through the realm 'Azure AAD'; the pam, pve and AD password realms also appear | entra, its only MFA: the portal can't do Proxmox's TOTP. The password realms: none | Proxmox's sign-in, per user: native oauth (entra) through the realm 'Azure AAD'; the pam, pve and AD password realms also appear | entra, its only MFA: the portal can't do Proxmox's TOTP. The password realms: none | not served | |
| portainer | 192. | native oauth (entra); the initial admin's password (always on); API keys | entra; the password and API keys have none | native oauth (entra); the initial admin's password (always on); API keys | entra; the password and API keys have none | native oauth (entra); the initial admin's password (always on); API keys; oauth2-proxy (entra) first on /api/auth, X-API-Key requests | entra; the password and API keys have none; entra first on those |
| profilarr | 192. | password (native oauth supported, not set up) | none | oauth2-proxy (entra), then password (native oauth supported, not set up) | entra | not served | |
| prometheus | 192. | none | none | none | none | not served | |
| prowlarr | 192. | password (forms, required for all) | none | oauth2-proxy (entra), then password (forms, required for all) | entra | not served | |
| proxmox | pve1-3. | native oauth (entra), realm 'Azure AAD'; password realms pam, pve and AD | entra; root@pam: TOTP everywhere, and a WebAuthn passkey on proxmox. | native oauth (entra), realm 'Azure AAD'; password realms pam, pve and AD | entra; root@pam: TOTP everywhere, and a WebAuthn passkey on proxmox. | not served | |
| qbittorrent | 192. | password (subnet whitelist off) | none | oauth2-proxy (entra), then password (subnet whitelist off) | entra | not served | |
| radarr | 192. | password (forms, required for all) | none | oauth2-proxy (entra), then password (forms, required for all) | entra | not served | |
| sabnzbd | 192. | password | none | oauth2-proxy (entra), then password | entra | not served | |
| mydomain1 (mydomain1. | 192. | WordPress password login, at a custom address (wp-login.php answers 404) | not recorded | not served | none to read it; WordPress password login, at a custom address (wp-login.php answers 404) | not recorded | |
| searxng | 192. | none | none | none | none | not served | |
| seerr | 192. | password (Jellyfin or local accounts) | none | oauth2-proxy (entra), then password (Jellyfin or local accounts) | entra | oauth2-proxy (entra), then password (Jellyfin or local accounts) | entra |
| sonarr | 192. | password (forms, required for all) | none | oauth2-proxy (entra), then password (forms, required for all) | entra | not served | |
| syn01 | 192. | password | one-time codes | not served | password | one-time codes | |
| syn02 | 192. | password | one-time codes | not served | password | one-time codes | |
| traefik | no port of its own; the API is also on :8080, for traefik-api only | none: its read-only API has no login | none | oauth2-proxy (entra) | entra | not served | |
| unifi | 192. | UniFi OS login | MFA on the account | not served | UniFi OS login | MFA on the account | |
| unifiapibrowser | 192. | none: its login is turned off (NOAPIBROWSERAUTH=1), and it holds a UniFi login | none | oauth2-proxy (entra) | entra | not served | |
| versity | truenas1. | S3 access key and secret | none | S3 access key and secret | none | not served | |
| watch-your-lan | 192. | none | none | none | none | not served | |
| www | 192. | WordPress password login (wp-login.php) | not recorded | none to read it; WordPress password login (wp-login.php) | not recorded | none to read it; WordPress password login (wp-login.php) | not recorded |
| zigbee2mqtt | 192. | not recorded | none | oauth2-proxy (entra), then the app's own login (not recorded) | entra | not served | |
| zwave-js-ui | 192. | password | none | oauth2-proxy (entra), then password | entra | not served | |
secrets, ssh and active directory¶
the passwords and keys the apps use, and how they reach a container, are on secrets.
to be written
ssh and keys, and active directory.