Commit Graph
3 Commits
Author SHA1 Message Date
jakman85andClaude Opus 4.8 41d696e9fe fix(apps): semaphore SEMAPHORE_PORT — shadow kubelet service-link injection
With the config PVC now writable (fsGroup), semaphore got past config write and
pinged its PostgreSQL successfully, then crashed on:
  panic: value of field 'Port' is not valid: tcp://<svcIP>:3000

Because the Service is named `semaphore`, the kubelet injects the Docker-link-style
SEMAPHORE_PORT=tcp://<clusterIP>:3000, which collides with Semaphore's OWN
SEMAPHORE_PORT (its HTTP listen port). Add an explicit SEMAPHORE_PORT=":3000" env
entry — user-defined env takes precedence over service-link vars — pinning the
listen port back to :3000 (matches containerPort/Service). Uses the existing env
field; only apps/semaphore.yaml changes, all others byte-identical.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-26 02:09:22 -05:00
jakman85andClaude Opus 4.8 28498f1099 fix(apps): semaphore fsGroup — schema 1.3.0 podSecurityContext for non-root PVC write
The semaphoreui/semaphore image runs as non-root UID 1001 / GID 0 and writes its
generated config.json into the config dir on boot. A mounted Longhorn PVC is
root:root, so UID 1001 got "permission denied" and the pod crash-looped.

First-class the fix into the render contract (same mechanism the retired
gitea-bootstrap-patch was replaced by): schema 1.3.0 adds an optional pod-level
`podSecurityContext`, emitted verbatim onto spec.template.spec.securityContext by
render.py. Absent => every other app renders byte-identical (verified). semaphore
declares fsGroup=1001 (kubelet group-owns the PVC + adds the supplemental group so
UID 1001 can write) plus runAsUser/runAsGroup/runAsNonRoot pinning the image identity.

render.py selftest extended (verbatim render + absence). L0 (schema+kyverno 17/17),
L1 3/3, lint all green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-26 02:00:45 -05:00
jakman85andClaude Opus 4.8 77496f9a8e feat(apps): onboard semaphore (Phase 6) — second CNPG-DB + OIDC-target app
Semaphore (Ansible UI, docs/13 row 10): internal exposure, internet egress,
config on Longhorn + external PostgreSQL, auth oidc. Reuses the CNPG-DB pattern
nextcloud established (apps/nextcloud-db.yaml, commit 4be6752).

- catalog/semaphore.yaml: ServiceDefinition (schema >=1.2.0 env) wiring the
  SEMAPHORE_DB_* connection to semaphore-db-rw.semaphore-db:5432, break-glass
  SEMAPHORE_ADMIN* local admin, and the FIXED base64 SEMAPHORE_ACCESS_KEY_ENCRYPTION.
- apps/semaphore.yaml: rendered (render.py --no-image-automation) — Deployment,
  config PVC, Service, default-deny + egress/gateway/internet CNPs, HTTPRoute
  semaphore.home.keithjackson.dev:3000, ServiceMonitor, SLI alert.
- apps/semaphore-db.yaml: CNPG Cluster in dedicated ns semaphore-db, initdb from
  the owner secret, inheritedMetadata app.kubernetes.io/name=semaphore-db so the
  dep-edge egress CNP matches.
- apps/semaphore-db-app.sops.yaml + apps/semaphore-bootstrap-secret.sops.yaml:
  SOPS(age) owner creds (basic-auth) + mirrored db-password, break-glass admin,
  and the persistent access-key encryption key.

OIDC (Keycloak realm client) is DEFERRED like gitea/nextcloud — comes up on the
break-glass local admin; realm-client wiring is the batched follow-up.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-26 01:47:22 -05:00