Onboard Crafty Controller (Minecraft panel) with Keycloak-gated web UI

Add Crafty Controller 4 (registry.gitlab.com/crafty-controller/crafty-4:4.10.8)
as a platform component: a web panel that runs Java + Bedrock Minecraft servers as
subprocesses in one pod + a 20Gi Longhorn PVC (mounted 5x by subPath).

Auth = Option B (per-app oauth2-proxy reverse-proxy sidecar), since Cilium 1.16.5
cannot do ext_authz on the shared Gateway:
- oauth2-proxy v7.8.1 sidecar (:4180) reverse-proxies to Crafty's self-signed HTTPS
  UI (127.0.0.1:8443), gated to Keycloak homelab realm group platform-admins.
- Confidential `crafty` client provisioned live via kcadm + captured in the realm
  import; client secret + cookie secret via SOPS/age (mirrored in realm-clients).

- Web UI: HTTPRoute crafty.home.keithjackson.dev -> Service :80 -> sidecar :4180.
- Game ports: Cilium L2 LoadBalancer on VIP 192.168.1.55 (next free after .53/.54),
  starter set TCP 25565-25567 (Java) + UDP 19132-19134 (Bedrock), NOT auth-gated.
- fsGroup:0 / runAsGroup:0 / runAsUser:1000 for the image's crafty:root identity on
  the root:root Longhorn PVC (the seerr fsGroup lesson).
- Own non-gating Flux tier clusters/base/crafty.yaml (dependsOn platform-config,
  wait:false) + sops-decryption patch in clusters/dev — a leaf UI must never gate
  the platform.
- Explicit default-deny NetworkPolicy + CiliumNetworkPolicies (platform namespace,
  not app-namespace, so Kyverno does not auto-generate the baseline).

make -C ci gate green; kustomize build of platform/crafty, clusters/base,
clusters/dev clean.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 4.8 committed 2026-08-28 15:39:51 -05:00
1 parent a207103c7b
commit 0437871807
17 files changed
+779 -2

No files matched your search

@@ -11,6 +11,7 @@ stringData:
nextcloud-client-secret: ENC[AES256_GCM,data:Uh3+EqIr88CNNt3KfDYP8lbNIhcB9iQrxJvbaxek40hD+JYAJmUP31WQypyDY3BiSvkjE6urr9PJmYoDgz7tkw==,iv:Tg4WAgyydIUBB1mqdOZPt0HsCkWSGNPvbI4WtFKj9V8=,tag:1LgZYdDPVEM+9UROanBllw==,type:str]
semaphore-client-secret: ENC[AES256_GCM,data:BX5lXhFNX6vQh67G2+XM2vt9wymhL8Psx+NqQJneFup7gdZRjhk8paEU5dOfugZNwooieara7UFyAFm5+1BJFg==,iv:kmmz0ZiCZgsSowns3hjDTQzoD2lPCr9Oz1iS5WgbhEE=,tag:BbesQX6lxAhaGiqPqMQ9vA==,type:str]
headlamp-client-secret: ENC[AES256_GCM,data:hlQ5m6FeqwOqX9HTT8jC6NZmhQH0kMgUveMZ2Cfen1zLLxWfYXXC1QMBWSqrchY2EpyIr7vNPbFjbqffwiSDHQ==,iv:WS1F3gkWn18STl4c38AlCe7TU6jvZpl/eULf2TnjIZ4=,tag:n2BnQYrm4gZbLeemDBykjA==,type:str]
crafty-client-secret: ENC[AES256_GCM,data:j9HAqIVlr+y8xZ6RvOvtQrVBv/mTLK89KYu1oQMkeSMsjyZdwZlLwW+Q7oiO1hqpyEG7ht5z3x6dJegN4LaxQQ==,iv:mnG5boz0M2A6hDdLKBaneUPH75Ih1RhM2R2d77VG7Vs=,tag:TGQKb7+4lnmhS3DZm6Wz1A==,type:str]
sops:
kms: []
gcp_kms: []
@@ -26,8 +27,8 @@ sops:
YXJGUUJiTnFndFhDZDU1bmxXTENCbmsKuhb3RQ3LZmuTPAXEck8b6iuZ5vxLb9fV
+fQELmU4kOTbZrPSxy+kpttLG5Cy5d6X2ieZXBf6opzpLTUhlBOkHA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-28T16:39:25Z"
mac: ENC[AES256_GCM,data:HRQ1SISIr9DrfCz96vEDGd/vP0o6pkVy5iMAUS+310oWAWhTxeXFvROJAhfdL1Pq8GzgFnA8s/Z6mSwr2E/D3Sjp679dYrZJeuKqt7A/TFiIsTt2yka+xpH9uCtoX8XpQDXFgq2bpAeOYoUVIahHCm9/P6mRT+D0tOkUNCcgOPE=,iv:WNR1+aVE/Ru3UmNiTRwDVN3KshIJvCdlvo3TIJJQiLA=,tag:J9r9x2yeRhiTFCraW0XH9g==,type:str]
lastmodified: "2026-08-28T20:36:54Z"
mac: ENC[AES256_GCM,data:hXqmcUYihXpprhm0Sn9CxbxuJR12WZtMKRPi+320KjU3ZEOa2Bz0wqzMQFhM2fLa4GttBG49YbdDHhqk1z/hqtHgxJoeiIi9qQOrpKgRC5oI42x6CTS7uhyIeh0y8h1UlQWaBbJ6Z+ig4OQ/1W9PCFkxL9klt6tbnQlWGiIbSB4=,iv:rY+1gerz/mXgvXPH4HpRRd0Nus0kHV8xC6G4WZDEXyA=,tag:gofnyk3rx+RFmpRxoiF8GA==,type:str]
pgp: []
encrypted_regex: ^(data|stringData)$
version: 3.9.4
@@ -38,3 +38,5 @@ stringData:
grafana-client-secret: REPLACE-ME
oauth2-proxy-client-secret: REPLACE-ME
headlamp-client-secret: REPLACE-ME
# Mirror into platform/crafty/crafty-oauth2-proxy-credentials.sops.yaml (key: client-secret).
crafty-client-secret: REPLACE-ME
@@ -39,6 +39,10 @@ spec:
secret:
name: keycloak-realm-clients
key: headlamp-client-secret
CRAFTY_CLIENT_SECRET:
secret:
name: keycloak-realm-clients
key: crafty-client-secret
realm:
realm: homelab
enabled: true
@@ -321,6 +325,41 @@ spec:
"included.client.audience": kubernetes
"id.token.claim": "true"
"access.token.claim": "true"
# crafty — the Minecraft management panel (platform/crafty). Crafty's web UI has NO native OIDC,
# so it is gated by a PER-APP oauth2-proxy REVERSE-PROXY sidecar (Option B; platform/crafty +
# platform/oauth2-proxy/README.md) — this is that sidecar's confidential auth-code client, modeled
# on the grafana/oauth2-proxy clients. Redirect URI is the oauth2-proxy callback on Crafty's host.
# The `groups` claim (bare names) lets the sidecar enforce --allowed-groups=platform-admins
# (R-IDP-06). Secret injected from the SOPS keycloak-realm-clients Secret via CRAFTY_CLIENT_SECRET
# (R-IDP-04); the SAME value is mirrored in platform/crafty/crafty-oauth2-proxy-credentials.sops.yaml
# (cross-namespace mirror, like grafana/gitea/headlamp). Provisioned live via kcadm at authoring
# time (2026-08-28) and captured here as the durable GitOps source of truth.
- clientId: crafty
name: Crafty Controller (oauth2-proxy)
protocol: openid-connect
enabled: true
publicClient: false
standardFlowEnabled: true
directAccessGrantsEnabled: false
secret: "$(env:CRAFTY_CLIENT_SECRET)"
redirectUris:
- https://crafty.home.keithjackson.dev/oauth2/callback
webOrigins:
- https://crafty.home.keithjackson.dev
defaultClientScopes:
- email
- profile
- roles
protocolMappers:
- name: groups
protocol: openid-connect
protocolMapper: oidc-group-membership-mapper
config:
"full.path": "false"
"id.token.claim": "true"
"access.token.claim": "true"
"userinfo.token.claim": "true"
"claim.name": groups
# --- MFA for platform-admins (R-IDP-04): a browser flow whose OTP step is CONDITIONAL on the
# platform-admin role. Non-admins log in without OTP; platform-admins must enroll/present TOTP.
# Set as the realm browserFlow below. VERIFY at bring-up (see runbook): a malformed flow fails