ITSM β GLPI (ITIL v4)
:::note Status
π’ S001 + S002 live on dev (2026-10-04). GLPI is deployed, reachable internally at
https://itsm.10.0.0.200.nip.io (Tailscale) and publicly at https://itsm.devandre.sbs (Cloudflare
tunnel), gated by native Authentik OIDC (the glpi-singlesignon plugin β not forward-auth).
Detailed design (PRD, solution architecture, NFR register, threat model, ADRs, sprint plan) lives in the
ktayl-itsm repo (docs/); this page is the org-site map.
:::
The ktayl-solution IS runs its IT service management on GLPI (ITIL v4 β CMDB, incident/request
management, SLAs, helpdesk), board #16 (IS Foundations). It is the service-management system of
record; it is not project management (Plane) or metrics (Prometheus/Grafana). Delivered via BMAD
(Path B β adopt + integrate): the planning set + readiness gate (PASS) are in the ktayl-itsm repo.
As-built β what is live (S001)β
| Aspect | As deployed |
|---|---|
| Tool | GLPI 10.0.28 β a custom image built from ktayl-itsm (FROM glpi/glpi + minicloud CA, apache on 8080, non-root), pushed to Harbor (dev) + ghcr (prod, cosign-signed + SBOM) |
| Deploy | GAP wrapper chart minicloud-gitops/services/ktayl-itsm/helm/ + ArgoCD app apps/workloads/ktayl-itsm-dev.yaml, namespace itsm. GLPI + a dedicated MariaDB are both stateful workloads in the chart's templates/ (each with its own PVC) β the shared stateless minicloud-app-deployment library does not fit a PVC-backed app |
| URL | internal https://itsm.10.0.0.200.nip.io (Tailscale) + public https://itsm.devandre.sbs (Cloudflare tunnel) |
| Database | dedicated MariaDB 11.4 StatefulSet (GLPI requires MySQL/MariaDB β the one deviation from the CNPG/Postgres standard); GLPI auto-installs its schema on first boot |
| Auth | native Authentik OIDC via the glpi-singlesignon plugin (baked into the image) β GLPI's own login page offers Log in with Authentik; users auto-provision on first sign-in, keyed by email |
| Secrets | Vault secret/platform/ktayl-itsm (mariadb-root-password, db-password, oidc-client-id, oidc-client-secret) β ESO / provisioning script |
| Security | non-root, allowPrivilegeEscalation:false, drop:[ALL], seccomp RuntimeDefault, no SA-token automount; default-deny netpol (GLPI β MariaDB only); Trivy flags image CVEs to the GitHub Security tab (SARIF, nothing suppressed) |
As-built β SSO + public route (S002)β
| Aspect | As deployed |
|---|---|
| SSO model | GLPI's native OIDC (glpi-singlesignon v1.4.0, baked into the custom image) β Authentik. Not forward-auth: GLPI's own login page is the gate, so the public ingress carries no auth annotations |
| Authentik | provider+app glpi (confidential, authorization_code+refresh_token), scopes openid email profile (+ groups); redirect is a REGEX β¦/callback.php.* (the plugin appends a /provider/<id> path segment) |
| Provisioning | the provider row (endpoints + client creds) is upserted by the reusable minicloud-ops/scripts/glpi/configure-sso-provider.sh (creds read from Vault, never echoed) β GLPI stores SSO providers in its DB, not config |
| User mapping | use_email_for_login=1 β GLPI account keyed off the OIDC email; first sign-in auto-provisions the user. The owner is pre-provisioned Super-Admin |
| Public route | itsm.devandre.sbs via the k8s Cloudflare tunnel (manifests/cloudflare-tunnel/02-configmap.yaml, originServerName: itsm.10.0.0.200.nip.io) + ingress rule/TLS SAN for both hosts |
| Back-channel trust | GLPIβAuthentik token/userinfo over split-horizon DNS (auth.devandre.sbsβinternal ingress), trusted via the S001 runtime CA-trust initContainer (minicloud CA appended to the bundle) |
:::note group β GLPI profile
The glpi-singlesignon v1.4.0 plugin does not map the OIDC groups claim to GLPI profiles β it
auto-provisions every new user with GLPI's default profile. Profile elevation / role mapping is done
in GLPI itself (manual profile assignment, or GLPI authorization rules by email domain), not from
the claim. Claim-driven groupβprofile is a later story (plugin v2.x / a rules layer). The owner's account
is pre-provisioned Super-Admin so the ITSM is administrable over SSO from first login.
:::
Operate / verifyβ
# internal health (Tailscale + minicloud CA):
/usr/bin/curl --cacert ~/minicloud-ca.crt -sI https://itsm.10.0.0.200.nip.io/ # -> 200, "Authentication - GLPI"
# public edge (Cloudflare) + SSO redirect chain:
/usr/bin/curl -sI https://itsm.devandre.sbs/ # -> 200 (valid public TLS)
# the GLPI login page carries a "Log in with Authentik" link β
# /plugins/singlesignon/front/callback.php/provider/1 302s to auth.devandre.sbs/application/o/authorize/
# β Authentik renders "Log in to continue to GLPI ITSM"
ssh controller "kubectl get pods -n itsm" # glpi + glpi-mariadb
ssh controller "kubectl get application ktayl-itsm-dev -n argocd" # Synced / Healthy
# (re)provision the SSO provider row idempotently:
ssh controller "cd ~/minicloud-ops && bash scripts/glpi/configure-sso-provider.sh itsm Authentik https://auth.devandre.sbs platform/ktayl-itsm"
Deploy gotchas (captured β reusable for any apache/php vendor image on this hardened cluster)β
- File-capability binaries fail to exec under
no_new_privs. apache2 carriescap_net_bind_service; withallowPrivilegeEscalation:falsethe kernel refuses to exec it (EPERM, exit 126). Fix:setcap -rthe binary in the custom image and bind an unprivileged port (8080). - GLPI data lives at
/var/glpi/{config,files}, not/var/www/glpi/*β mount the PVCs there (wrong paths β config unpersisted β re-install crashloop). First-boot install needs a startupProbe. - SSO (S002): the
glpi-singlesignonv1.4.0 release asset isglpi-singlesignon-v1.4.0.tar.bz2(bzip2; the shortsinglesignon.tgzname is v2.x-only, which needs GLPI 11) and extracts toglpi-singlesignon/β rename tosinglesignon/(GLPI only discovers a plugin whose dir = its key). grant_typesdefaults to[]on a new Authentik 2026.x provider β the authorize view rejects the browser flow ("Invalid grant_type for provider"). A web OIDC app needsauthorization_code(+refresh_token); the sharedoidc-provider.pynow sets it (it previously left bookstack/glpi/plane login-broken β all three backfilled).- GLPI's
url_basedefaults tohttp://localhost/glpiand the plugin buildsredirect_urifrom it β seturl_baseto the public host (https://itsm.devandre.sbs) or OIDC redirects point at localhost. - The plugin appends
/provider/<id>to the callback β the Authentik redirect must be REGEX (β¦/callback.php.*), not STRICT, or Authentik returns invalid_request.
Full detail + rationale: ktayl-itsm repo docs/ (deployment-architecture, ADRs) and the platform
memory project_ktayl_itsm_glpi.
Roadmap (board #16)β
| Story | Status |
|---|---|
| S001 β Deploy GLPI (wrapper chart + custom image) | β Done (dev) |
| S002 β Authentik OIDC SSO + public route | β Done (dev) β native OIDC, auto-provision, public route; claimβprofile deferred |
| S003 β Incident + request + SLA | β¬ |
| S004 β Minimal CMDB (BYOD-scoped) + seed | β¬ |
| S005 β Alertmanager β auto-ticket | β¬ |
| S006 β KPI dashboard (GLPI β Grafana) | β¬ |
| S007 β Self-service portal + KB | β¬ |
Compliance mappingβ
ITIL v4 service management Β· DORA (ICT incident management + change governance β the ITSM record is evidence) Β· ACPR / Solvency II operational-risk + incident logging Β· GDPR (ticket-PII retention, handled in S003). Certification: BC03 (dΓ©ployer & sΓ©curiser).