reference-ansible/ARCHITECTURE.md

9.6 KiB

Architekturskizze — demo-gymburgdorf

Diese Skizze zeigt, wie das reference-ansible-Repo am Beispiel der Inventory demo-gymburgdorf funktioniert: welche Hosts existieren, welche Rollen darauf laufen, wo welche Variablen hingehören und wie Secrets aus OpenBao gelookupt werden.

1. Variablen-Hierarchie (Ansible Precedence)

flowchart TB
    classDef rolelayer fill:#fef3c7,stroke:#92400e,color:#000
    classDef grouplayer fill:#dbeafe,stroke:#1e40af,color:#000
    classDef hostlayer fill:#dcfce7,stroke:#166534,color:#000
    classDef vaultlayer fill:#fee2e2,stroke:#991b1b,color:#000

    R["<b>role defaults/main.yml</b><br/>(niedrigste Precedence)<br/>~180 Variablen über 12 Rollen<br/>z.B. traefik_use_ssl: false<br/>keycloak_admin_password: changeme"]:::rolelayer

    GA["<b>group_vars/all/</b><br/>docker.yml → docker_registry_mirrors<br/>vault.yml → vault_addr, vault_mount"]:::grouplayer

    GT["<b>group_vars/traefik_servers/</b><br/>traefik.yml<br/>traefik_use_ssl, traefik_cert_mode: acme<br/>traefik_acme_dns_zone<br/>traefik_acme_tsig_* (Vault-Lookup!)"]:::grouplayer

    GB["<b>group_vars/backend_servers/</b><br/>traefik.yml → traefik_mode: backend"]:::grouplayer

    HR["<b>host_vars/reverseproxy/</b><br/>traefik.yml → traefik_mode: dmz"]:::hostlayer

    HA["<b>host_vars/application/</b> (fehlt aktuell!)<br/>FQDNs, OIDC-Clients, DB-Passwords<br/>nextcloud_domain, authentik_domain, ..."]:::hostlayer

    HS["<b>host_vars/storage/</b> (fehlt aktuell!)<br/>garage_s3_domain, garage_*_token<br/>traefik_dmz_exposed_services (für DMZ)"]:::hostlayer

    V["<b>HashiCorp Vault / OpenBao</b><br/>bao.digitalboard.ch<br/>mount: demo-gymburgdorf<br/>z.B. demo-gymburgdorf/data/acme-tsig"]:::vaultlayer

    R --> GA --> GT --> GB --> HR
    GB --> HA
    GB --> HS
    GT -.Lookup zur Laufzeit.-> V
    HA -.Lookup zur Laufzeit.-> V
    HS -.Lookup zur Laufzeit.-> V

Höhere Ebene überschreibt tiefere. host_vars/reverseproxy/traefik_mode: dmz schlägt also group_vars/backend_servers/traefik_mode: backend — möglich, weil reverseproxy nicht in backend_servers ist.

2. Inventory-Topologie demo-gymburgdorf

flowchart LR
    classDef dmz fill:#fee2e2,stroke:#991b1b,color:#000
    classDef app fill:#dcfce7,stroke:#166534,color:#000
    classDef stor fill:#dbeafe,stroke:#1e40af,color:#000
    classDef turn fill:#fef9c3,stroke:#854d0e,color:#000

    subgraph ALL["group: all_servers (alle Hosts)"]
        direction LR
        subgraph DMZ["DMZ-Segment 172.16.9.0/24"]
            RP["<b>reverseproxy</b><br/>172.16.9.111<br/>traefik_mode: dmz"]:::dmz
            TURN["<b>turn</b><br/>172.16.9.112<br/>(STUN/TURN)"]:::turn
        end
        subgraph BE["Backend-Segment 172.16.19.0/24<br/>(group: backend_servers)"]
            APP["<b>application</b><br/>172.16.19.101<br/>traefik_mode: backend<br/>+ nextcloud, opencloud,<br/>collabora, drawio,<br/>authentik, authentik_outpost_ldap"]:::app
            ST["<b>storage</b><br/>172.16.19.102<br/>traefik_mode: backend<br/>+ garage (S3)"]:::stor
        end
    end

    RP -.HTTP/HTTPS reverse proxy.-> APP
    RP -.HTTP/HTTPS reverse proxy.-> ST

Gruppen-Mitgliedschaften (hosts.yml):

  • traefik_serversall_serversalle 4 Hosts bekommen Traefik (DMZ-Modus für reverseproxy, Backend-Modus für application/storage).
  • backend_servers = {application, storage} → setzt traefik_mode: backend via group_vars.
  • Service-Gruppen (nextcloud_servers, garage_servers, …) sind Single-Host-Wrapper, mit denen playbooks/site.yml gezielt deploybare Rollen targetet.

3. Service-Layout & Variablen-Verortung

flowchart TB
    classDef rp fill:#fee2e2,stroke:#991b1b,color:#000
    classDef ap fill:#dcfce7,stroke:#166534,color:#000
    classDef st fill:#dbeafe,stroke:#1e40af,color:#000
    classDef ext fill:#e9d5ff,stroke:#6b21a8,color:#000

    Internet((Internet))
    DNS["DNS ns1.digitalboard.ch<br/>RFC2136 TSIG (key: acme_update_key_demo_gymb)<br/>dynamic zone: demo-gymb._acme.digitalboard.ch<br/>CNAME-bridge: _acme-challenge.*.gymb.souveredu.ch"]:::ext
    BAO["OpenBao<br/>bao.digitalboard.ch<br/>mount: demo-gymburgdorf"]:::ext

    subgraph RP["<b>reverseproxy</b> — traefik dmz"]
        TRDMZ["traefik (file provider)<br/>📍 group_vars/traefik_servers/traefik.yml<br/>  → acme, tsig, ssl<br/>📍 host_vars/reverseproxy/traefik.yml<br/>  → traefik_mode: dmz<br/>📍 host_vars/reverseproxy/...<br/>  → traefik_dmz_exposed_services"]:::rp
    end

    subgraph APP["<b>application</b> — backend"]
        TRA["traefik (docker provider)<br/>📍 group_vars/backend_servers<br/>  → traefik_mode: backend"]:::ap
        NC["nextcloud<br/>📍 host_vars/application/nextcloud.yml<br/>  domain, postgres_pw, oidc, s3, ldap"]:::ap
        OC["opencloud<br/>📍 host_vars/application/opencloud.yml<br/>  oidc_issuer, ldap, s3"]:::ap
        AK["authentik<br/>📍 host_vars/application/authentik.yml<br/>  secret_key, postgres, ldap_apps, oidc_apps"]:::ap
        AKO["authentik_outpost_ldap<br/>📍 host_vars/application/authentik_outpost_ldap.yml<br/>  host, token"]:::ap
        COL["collabora<br/>📍 host_vars/application/collabora.yml<br/>  domain, allowed_domains"]:::ap
        DRW["drawio<br/>📍 host_vars/application/drawio.yml"]:::ap
    end

    subgraph ST["<b>storage</b> — backend"]
        TRS["traefik (docker provider)"]:::st
        GAR["garage (S3)<br/>📍 host_vars/storage/garage.yml<br/>  s3_domain, rpc_secret,<br/>  admin_token, s3_keys"]:::st
    end

    Internet -->|HTTPS :443| TRDMZ
    TRDMZ -->|HTTP backend| TRA
    TRDMZ -->|HTTP backend| TRS
    TRA --> NC & OC & AK & COL & DRW & AKO
    TRS --> GAR

    NC -. S3 .-> GAR
    OC -. S3 .-> GAR
    NC -. OIDC .-> AK
    OC -. OIDC .-> AK
    NC -. WOPI .-> COL
    OC -. WOPI .-> COL
    NC -. LDAP .-> AKO
    OC -. LDAP .-> AKO
    AKO -. RPC + token .-> AK

    TRDMZ -. ACME DNS-01 TSIG .-> DNS
    TRDMZ -. lookup acme-tsig .-> BAO
    AK -. lookup secrets .-> BAO
    NC -. lookup secrets .-> BAO
    GAR -. lookup secrets .-> BAO

4. Deploy-Flow

sequenceDiagram
    participant U as User (make)
    participant M as Makefile
    participant A as ansible-playbook
    participant V as OpenBao
    participant H as Hosts

    U->>M: make bao
    M->>V: bao login (OIDC)
    V-->>M: VAULT_TOKEN
    U->>M: make deploy_site_demo_gymburgdorf
    M->>A: ansible-playbook site.yml -i inventories/demo-gymburgdorf/hosts.yml
    A->>A: lade group_vars/all → group_vars/traefik_servers → group_vars/backend_servers → host_vars/*
    A->>V: community.hashi_vault Lookups (acme-tsig, secrets)
    V-->>A: secret values
    A->>H: Play "base" → all_servers
    A->>H: Play "traefik" → traefik_servers (dmz auf reverseproxy, backend auf application/storage)
    A->>H: Play "garage" → storage
    A->>H: Play "authentik / nextcloud / collabora / ..." → application

5. Variablen-Cheatsheet — wo gehört was hin?

Variable Wohin in demo-gymburgdorf/ Warum
vault_addr, vault_mount group_vars/all/vault.yml Vault-Endpoint gilt site-weit
docker_registry_mirrors group_vars/all/docker.yml Pulls aus Mirror auf allen Hosts
traefik_acme_*, traefik_use_ssl, traefik_cert_mode group_vars/traefik_servers/traefik.yml Gilt für alle Traefik-Instanzen (dmz + backend)
traefik_mode: backend group_vars/backend_servers/traefik.yml Default für app + storage
traefik_mode: dmz host_vars/reverseproxy/traefik.yml Host-spezifischer Override
traefik_dmz_exposed_services host_vars/reverseproxy/ Liste, welche Backend-Services die DMZ proxyt (nur dort sinnvoll)
nextcloud_*, authentik_*, opencloud_*, collabora_*, drawio_* host_vars/application/<service>.yml Service läuft genau auf application
garage_* host_vars/storage/garage.yml Service läuft genau auf storage
Secrets (Passwords, Tokens, Keys) Inline-Variable mit lookup('community.hashi_vault.hashi_vault', vault_mount + '/data/<path>', url=vault_addr) Single source of truth; Pattern wie bei _acme_tsig

6. Traefik-Modi (zentral für die Architektur)

traefik_mode: dmz (Public-facing Reverse Proxy auf reverseproxy):

  • Aggregiert Services von Backend-Servern via traefik_dmz_exposed_services (Host-Variablen).
  • Nutzt file provider mit services.yml für statisches Routing.
  • Kein Docker-Socket gemountet — keine lokalen Container.
  • Routet zu backend_host auf anderen Maschinen.
  • Selektive Backend-Auswahl via traefik_backend_servers_to_proxy.

traefik_mode: backend (Application/Storage Server):

  • Mountet Docker-Socket (/var/run/docker.sock).
  • Nutzt docker provider für Auto-Discovery lokaler Container.
  • Services mit Label traefik.enable=true werden automatisch exponiert.
  • Beide Modi unterstützen ACME (RFC2136 DNS Challenge) oder Self-Signed.

7. Was im aktuellen demo-gymburgdorf noch fehlt

Im Vergleich zur vagrant-Inventory (Referenz) fehlen für eine vollständige Deployment:

  • host_vars/application/main.yml — Backbone-Vars für den Host (FQDN-Pattern, gemeinsame Defaults).
  • host_vars/application/{nextcloud,opencloud,authentik,authentik_outpost_ldap,collabora,drawio}.yml — die service-spezifischen Konfigurationen.
  • host_vars/storage/{main.yml,garage.yml} — Garage-Cluster-Setup.
  • host_vars/reverseproxy/<…>.yml mit traefik_dmz_exposed_services — sonst routet die DMZ nichts.

Die vagrant-Inventory ist das Template: dieselbe Struktur auf application/storage mappen, FQDNs auf *.gymb.souveredu.ch umstellen, Secrets durch Bao-Lookups ersetzen.