Authentication & Identity Flow¶
Identity Stack Overview¶
flowchart TD
subgraph ldap_stack["OpenLDAP — 192.168.1.52"]
ldap["OpenLDAP\ndc=mdapi,dc=ch"]
lam["LAM admin UI\nlam.mdapi.ch"]
end
subgraph keycloak_stack["Keycloak — login.envuassu.ch"]
kc["Keycloak\nrealm: tilloch"]
kc_pg["Postgres 5Gi"]
end
subgraph ldap_consumers["LDAP Consumers"]
mail_auth["docker-mailserver\nPostfix + Dovecot\nLDAP auth"]
sssd_vpn["sssd on mbptillo\nOpenVPN PAM auth"]
end
subgraph oidc_consumers["OIDC / OAuth2 Consumers"]
oauth2_proxy["oauth2-proxy\nauth.mdapi.ch"]
apps["Protected apps\n(auth_request)"]
end
ldap --> mail_auth & sssd_vpn
lam --> ldap
kc --> kc_pg
kc -->|"OIDC / OAuth2"| oauth2_proxy --> apps
Keycloak¶
Keycloak is the OIDC/SSO provider for user-facing web applications. It runs in the keycloak namespace with a dedicated Postgres backend.
Realms:
| Realm | Purpose |
|---|---|
master |
Admin console |
tilloch |
Main user realm (tillo.ch domain) |
The tilloch realm uses default-roles-tilloch to assign baseline permissions to every user automatically, including view-applications (required for the account console Applications tab to appear).
Custom image tracking: A CI pipeline polls quay.io/keycloak/keycloak for new 26.x releases and updates the Dockerfile automatically, triggering a rebuild + Keel rolling deploy.
OpenLDAP¶
OpenLDAP is the authoritative directory for dc=mdapi,dc=ch. It serves two primary consumers:
- Mail stack — Postfix (relay auth) and Dovecot (IMAP auth) both bind to OpenLDAP. Users are stored as
objectClass=roomentries underou=People. - OpenVPN — sssd on the jump host (
mbptillo) authenticates VPN users against OpenLDAP via PAM, withcache_credentials=truefor resilience when the cluster is unreachable.
LAM (LDAP Account Manager) provides a web UI for directory management at lam.mdapi.ch.
Secret Management¶
Secrets never touch git. The chain from Akeyless SaaS to a running pod:
flowchart LR
akeyless["Akeyless SaaS"]
cm_vm["CipherTrust Manager\ncm.home.tillo.ch\ncustomer fragment\n(on-premise)"]
eso["External Secrets Operator\nClusterSecretStore: cm-akeyless"]
k8s_sec["K8s Secret\n(per namespace)"]
pod["Pod\n(env var / volume)"]
akeyless <-->|"customer fragment\nnever leaves LAN"| cm_vm
eso -->|"fetch /mdapi/* secrets"| cm_vm
eso --> k8s_sec --> pod
All paths under /mdapi/ use with_customer_fragment: true. The customer fragment is stored exclusively on the CipherTrust Manager VM — Akeyless SaaS cannot decrypt these secrets without it. This provides a hardware-backed guarantee that secrets cannot be exfiltrated through a breach of the Akeyless SaaS layer alone.
Single point of failure
If the CipherTrust Manager VM is unreachable, ExternalSecret refreshes fail cluster-wide. The VM is backed by a Longhorn volume and can be restored from snapshot in minutes using the /harvester-vm-restore runbook.
TLS — cert-manager + Let's Encrypt¶
flowchart LR
annotation["Ingress annotation\ncert-manager.io/issuer:\nletsencrypt-prod"]
cm_op["cert-manager"]
bind9["BIND9 nameserver\n192.168.1.53\n(RFC 2136 TSIG)"]
le["Let's Encrypt ACME v2"]
tls_secret["TLS Secret"]
annotation --> cm_op
cm_op -->|"nsupdate TSIG-signed\n_acme-challenge TXT"| bind9
bind9 --> le -->|"certificate"| cm_op --> tls_secret
DNS-01 challenges are used for all domains, enabling wildcard certificate support and working even for services not directly reachable from the internet. The TSIG key never leaves the cluster — it lives in a K8s Secret referenced by cert-manager.