docs/architecture/topology.md
Simon Bärlocher 345cf4b319
docs: add architecture section and overhaul top-level README
- Move Simon's architecture documentation into architecture/
  (setup, variables, topology, dns, deploy, security, operations
  plus index and glossary). All cross-repo references point at
  https://git.digitalboard.ch/Digitalboard/{reference-ansible,dns-zones}
  via absolute URLs so the docs remain navigable from any context.
- Rewrite README.md as a documentation hub: introduction, platform
  Mermaid overview, comparison of the three repos
  (docs / digitalboard.core / reference-ansible) and a full table of
  contents covering architecture, contributing, infrastructure,
  keycloak, ms-entra and troubleshooting.

Addresses the open items from the WKS PoC review (2026-05-26):
docs README begrüssungstext + Übersichtsgrafik + Verlinkung der
beiden anderen Repos, sowie das Verschieben der Architektur-Doku.
2026-05-28 14:25:27 +02:00

5.1 KiB

Topology — inventory and services

← Back to Architecture index

4. Inventory topology (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"]
        direction LR
        subgraph DMZ["DMZ 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/>(no role in site.yml yet)"]:::turn
        end
        subgraph BE["Backend 172.16.19.0/24<br/>group: backend_servers"]
            APP["<b>application</b><br/>172.16.19.101<br/>traefik_mode: backend<br/>+ authentik, authentik_outpost_ldap,<br/>  nextcloud, collabora, drawio"]:::app
            ST["<b>storage</b><br/>172.16.19.102<br/>traefik_mode: backend<br/>+ garage (S3)"]:::stor
        end
    end

    RP -.HTTPS in, HTTP out.-> APP
    RP -.HTTPS in, HTTP out.-> ST

Group memberships (from hosts.yml):

Group Members Purpose
all_servers reverseproxy, application, storage, turn Base role for all hosts
traefik_servers children: all_servers (= all 4 hosts) Traefik everywhere; DMZ/backend via traefik_mode
backend_servers application, storage Sets traefik_mode: backend via group var
garage_servers storage Single-host wrapper for the Garage role
nextcloud_servers, collabora_servers, drawio_servers, authentik_servers, authentik_outpost_ldap_servers application only Single-host wrappers

Difference vs. the vagrant inventory: vagrant structures Traefik differently — via the children groups traefik_servers_dmz and traefik_servers_backend instead of backend_servers + host_vars override. The two topologies are structurally incompatible; a 1:1 mapping is not possible. See operations.md for the recommended template.

5. Service layout and variable placement

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<br/>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/>📍 host_vars/reverseproxy/traefik.yml<br/>  → traefik_mode: dmz<br/>  → traefik_dmz_exposed_services"]:::rp
    end

    subgraph APP["<b>application</b> — traefik backend"]
        TRA["traefik (docker provider)<br/>📍 group_vars/backend_servers/traefik.yml"]:::ap
        AK["authentik (OIDC + LDAP outpost backend)<br/>📍 host_vars/application/authentik.yml"]:::ap
        AKO["authentik_outpost_ldap<br/>📍 host_vars/application/authentik_outpost_ldap.yml"]:::ap
        NC["nextcloud<br/>📍 host_vars/application/nextcloud.yml"]:::ap
        COL["collabora<br/>📍 host_vars/application/collabora.yml"]:::ap
        DRW["drawio<br/>📍 host_vars/application/drawio.yml"]:::ap
    end

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

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

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

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

Note: opencloud, send, opnform, homarr, and bookstack are defined as plays in playbooks/site.yml but currently have no matching group in hosts.yml for demo-gymburgdorf — those plays therefore run as no-ops. If a tenant needs these services, add the corresponding <service>_servers group in hosts.yml and a host_vars/application/<service>.yml (mind the spelling — the forms role is opnform, the LDAP role is 389ds).

The turn host is in all_servers (and therefore in traefik_servers) but has no service group of its own — currently only the base and traefik roles run on it.