Skip to content

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=room entries under ou=People.
  • OpenVPN — sssd on the jump host (mbptillo) authenticates VPN users against OpenLDAP via PAM, with cache_credentials=true for 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.