docs: rework security/hardening batch with diagrams and callouts
Mermaid diagrams up front, GFM alert callouts for key decisions and gotchas, tables instead of bullet walls - replaces plain prose with the visual template established for security-monitoring.md.
This commit is contained in:
6 files changed
+247
-114
No files matched your search
+35
-11
@@ -1,24 +1,48 @@
|
||||
# SSO & Identity
|
||||
# 🪪 SSO & Identity
|
||||
|
||||
## Dual-zone SSO registration
|
||||
> [!NOTE]
|
||||
> Every app that needs single sign-on registers against the identity provider once per reverse-proxy zone it's exposed on — and a real platform limitation shapes how superuser accounts authenticate.
|
||||
|
||||
Every app that needs single sign-on gets a provider + application pair registered *per reverse-proxy zone it's exposed on* — up to two registrations for the same app, one for the browser-facing zone and one for the internal service-to-service zone. About a dozen apps also use native OIDC/SAML instead of (or alongside) proxy-based forward-auth, where the upstream app supports it directly.
|
||||
## Login flow — same shape, every app
|
||||
|
||||
The login flow is the same shape regardless of which app or which zone it's normally accessed through:
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant App as App (browser-facing zone)
|
||||
participant IdP as Identity Provider (internal zone)
|
||||
|
||||
1. User opens the app on the browser-facing zone.
|
||||
2. The app redirects the browser to the identity provider for the actual OAuth authorize/token exchange — and that exchange *always* happens against the internal zone's identity-provider address, even though the app itself is normally reached via the browser-facing zone.
|
||||
3. On success, the browser lands back on the app's browser-facing address.
|
||||
U->>App: Opens app
|
||||
App->>U: Redirect to identity provider
|
||||
U->>IdP: OAuth authorize/token exchange
|
||||
Note over IdP: Always happens on the internal zone,<br/>even though the app itself lives<br/>on the browser-facing zone
|
||||
IdP->>U: Success
|
||||
U->>App: Lands back on the app
|
||||
```
|
||||
|
||||
Every app-specific setting beyond this shape (token lifetime, scopes, flow type) is deliberately *not* generalized across apps — each app's own OIDC documentation gets read fresh rather than copying another app's provider config, since the details genuinely differ per vendor.
|
||||
Every app-specific setting beyond this shape (token lifetime, scopes, flow type) is deliberately **not** generalized across apps — each app's own OIDC documentation gets read fresh rather than copying another app's provider config, since the details genuinely differ per vendor.
|
||||
|
||||
## A real platform limitation, documented rather than hidden
|
||||
| | |
|
||||
|---|---|
|
||||
| **Registration pattern** | Provider + application pair per zone (up to 2 per app) |
|
||||
| **Native OIDC/SAML apps** | ~12 apps, where the upstream supports it directly |
|
||||
| **Everything else** | Proxy-based forward-auth |
|
||||
|
||||
LDAP-based login through the identity provider's own outpost isn't viable for the primary superuser account: the outpost hides superuser accounts from LDAP directory search, so any app that requires a search-then-bind flow can never authenticate that account through LDAP. The two ways around it — creating a second non-admin account, or demoting the primary account from superuser — were both rejected as worse than the problem. Header-based forward-auth is used instead for apps in that situation, which sidesteps the LDAP search-then-bind requirement entirely.
|
||||
## A real limitation, documented rather than hidden
|
||||
|
||||
> [!WARNING]
|
||||
> LDAP-based login through the identity provider's own outpost **isn't viable for the primary superuser account** — the outpost hides superuser accounts from LDAP directory search, so any app requiring a search-then-bind flow can never authenticate that account through LDAP. The two workarounds — a second non-admin account, or demoting the primary account from superuser — were both rejected as worse than the problem. Header-based forward-auth is used instead for apps in that situation.
|
||||
|
||||
## Git access: least privilege, not the admin account
|
||||
|
||||
CI and automation pushes to the internal Git server run through a dedicated, non-admin bot account with scoped write access — not through an administrator identity. Other hosts that only need to *read* (pulling code, not pushing) get separate read-only deploy keys instead of any shared credential. This keeps a compromised automation credential from ever being an admin credential.
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CI["CI / Automation"] -->|scoped write| Bot["Dedicated bot account<br/>non-admin"]
|
||||
Bot --> Repo["Git server"]
|
||||
Other["Other hosts"] -->|read-only| Deploy["Deploy keys<br/>per host"]
|
||||
Deploy --> Repo
|
||||
```
|
||||
|
||||
CI and automation pushes run through a dedicated, non-admin bot account with scoped write access — never through an administrator identity. Hosts that only need to *read* get separate read-only deploy keys instead of any shared credential. A compromised automation credential is never an admin credential.
|
||||
|
||||
---
|
||||
Related stacks: [`stacks/identity-and-security/authentik/`](../stacks/identity-and-security/authentik/), [`stacks/platform/gitea/`](../stacks/platform/gitea/)
|
||||
Reference in new issue
Block a user