Files
home-network-k3s-impl/platform/crafty/service-l4.yaml
T
jakman85andClaude Opus 4.8 0437871807 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]>
2026-08-28 15:39:51 -05:00

73 lines
3.7 KiB
YAML

---
# ============================================================================================
# GAME-PORT L4 EXPOSURE — why the shared HTTPS Gateway is NOT enough (same shape as apps/vms/unifi).
# ============================================================================================
# The Minecraft servers Crafty creates listen on RAW TCP (Java) and UDP (Bedrock) game ports inside
# the crafty pod's network namespace. The Gateway API `shared` Gateway is an L7 HTTPS terminator and
# CANNOT express raw TCP on arbitrary ports or UDP at all. So the game ports are exposed as
# type=LoadBalancer Services whose VIP is pulled from the CiliumLoadBalancerIPPool `default-pool`
# (192.168.1.53-.60) and ARP-announced on the flat LAN by the existing CiliumL2AnnouncementPolicy
# `default-l2` (platform-config/cilium/*) — the SAME machinery the Gateway VIP and unifi already use.
# No new Cilium config is needed; these Services just consume the pool.
#
# THIS PATH IS NOT AUTH-GATED — and that is correct: Minecraft's Java (Mojang/Microsoft auth) and
# Bedrock (Xbox Live) protocols authenticate the PLAYER themselves. Only the Crafty WEB UI (admin
# control plane) is Keycloak-gated (via the oauth2-proxy sidecar); the game ports carry player traffic
# and must reach the server subprocesses directly.
#
# VIP: 192.168.1.55 — the next free address in default-pool (.53 = shared Gateway, .54 = unifi;
# verified free live 2026-08-28). PINNED via lbipam.cilium.io/ips so players have a stable target.
# ONE VIP, TWO SERVICES: a LoadBalancer Service is single-protocol-friendly, so TCP and UDP split into
# two Services sharing the IP via lbipam.cilium.io/sharing-key (same pattern as unifi).
#
# STARTER PORT SET (the operator has not created any servers yet): TCP 25565-25567 (three Java slots)
# + UDP 19132-19134 (three Bedrock slots). To add capacity: create the server in Crafty bound to a NEW
# port, then add that port to the matching Service below (and a containerPort in deployment.yaml for
# documentation) and to the game-port ingress CiliumNetworkPolicy (networkpolicy.yaml). K8s Services
# enumerate ports individually (no ranges), so add one `{ name, port, targetPort, protocol }` line each.
#
# externalTrafficPolicy: Local preserves the LAN client source IP and pins the VIP to the node running
# the crafty pod (also what L2 announcement does). external-dns (source: service) publishes the A
# record from the -tcp Service.
apiVersion: v1
kind: Service
metadata:
name: crafty-l4-tcp
namespace: crafty
labels:
app.kubernetes.io/name: crafty
annotations:
lbipam.cilium.io/sharing-key: "crafty"
lbipam.cilium.io/ips: "192.168.1.55"
# external-dns publishes A crafty-mc.home.keithjackson.dev -> 192.168.1.55 (the Java server target).
external-dns.alpha.kubernetes.io/hostname: "crafty-mc.home.keithjackson.dev"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: crafty
ports:
- { name: mc-java-0, port: 25565, targetPort: 25565, protocol: TCP }
- { name: mc-java-1, port: 25566, targetPort: 25566, protocol: TCP }
- { name: mc-java-2, port: 25567, targetPort: 25567, protocol: TCP }
---
apiVersion: v1
kind: Service
metadata:
name: crafty-l4-udp
namespace: crafty
labels:
app.kubernetes.io/name: crafty
annotations:
lbipam.cilium.io/sharing-key: "crafty" # same key + same IP => shares the VIP with -tcp
lbipam.cilium.io/ips: "192.168.1.55"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: crafty
ports:
- { name: mc-bedr-0, port: 19132, targetPort: 19132, protocol: UDP }
- { name: mc-bedr-1, port: 19133, targetPort: 19133, protocol: UDP }
- { name: mc-bedr-2, port: 19134, targetPort: 19134, protocol: UDP }