docs(readme): document prole.org fork — identity stack, live services, SSO

Add dedicated sections covering:
- prole.org cluster topology (myrddin/gandalf/merlin, namespaces, kubectl context)
- Live services table (db.prole.org, git.prole.org, pg.prole.org)
- Full identity/SSO stack: PROLE.ORG Kerberos → knoe-auth OIDC → Supabase Studio
- knoe-auth operational notes: Subject.callAs vs doAs, JDK 21 RC4 removal,
  AES-only keytab requirement, samba-tool exportkeytab --principal empty-keytab gotcha
- git.prole.org SPNEGO: proxy challenge logic, keytab rotation, arm64 build procedure
- Supabase Studio SSO: oauth2-proxy flow, basic-auth removal from Kong dashboard route
- Chrome SPNEGO policy deployment via make workstation
- Fork-specific files table

Update status section to 2026-05-28 with Identity/SSO commits clearly grouped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chrisfu 2026-05-28 06:14:53 -07:00
parent 5077e1335d
commit 0e937eb9db

218
README.md
View File

@ -297,33 +297,192 @@ Detailed setup docs, cluster procedures, and host-specific notes should live in
---
## prole.org deployment
This repository is the **prole.org fork** of the upstream knoe-db platform. It runs a production k3s cluster on a three-node ARM64 LAN (myrddin / gandalf / merlin) with Samba AD providing Kerberos identity for single sign-on across all platform services.
### Live services
| Service | URL | Auth |
|---|---|---|
| Supabase Studio | `https://db.prole.org` | SPNEGO → OIDC → Studio |
| Gitea | `https://git.prole.org` | SPNEGO (auto) or Gitea login |
| PostgreSQL | `pg.prole.org:5432` | mTLS + CNPG cert |
| SSH (Gitea) | `git.prole.org:3022` | SSH key |
### Cluster nodes
| Host | Role | IP |
|---|---|---|
| `myrddin.prole.org` | k3s server, Samba AD DC, container registry | 10.0.0.3 |
| `gandalf.prole.org` | k3s agent | 10.0.0.5 |
| `merlin.prole.org` | k3s agent | 10.0.0.6 |
Kubectl context: `prole-service-cluster` (kubeconfig at `knoe-k3s.kubeconfig`).
### Key namespaces
| Namespace | Contents |
|---|---|
| `knoe-system` | knoe-auth, Kong API gateway, KDC, Redis, Traefik |
| `gitea` | Gitea, gitea-spnego-proxy |
| `knoe-db` | CloudNativePG cluster |
| `supabase` | Supabase Studio, Kong (supabase), oauth2-proxy |
---
## Identity & SSO
All platform services use a unified Kerberos SSO stack. The flow from browser to service is:
```
Browser (Chrome, Safari, curl --negotiate)
│ kinit-obtained TGT from PROLE.ORG KDC (myrddin.prole.org)
Traefik (TLS termination, *.prole.org wildcard cert via ACME)
Kong API gateway (knoe-system/knoe-svc-kong)
│ routes by Host header
├── db.prole.org ──► oauth2-proxy ──► knoe-auth (OIDC) ──► Supabase Studio
└── git.prole.org ──► gitea-spnego-proxy (port 4000) ──► Gitea (port 3000)
```
### Kerberos realm
- **Realm:** `PROLE.ORG`
- **KDC / AD DC:** `myrddin.prole.org` (Samba 4, `10.0.0.3`)
- **Cross-realm trust:** `PROLE.ORG ↔ KNOE.LOCAL` (in-cluster MIT KDC for knoe-auth internal use)
- **Encryption:** AES256 + AES128 only (`msDS-SupportedEncryptionTypes=24`); RC4 disabled on all service accounts
### knoe-auth (OIDC provider — `api.prole.org/auth`)
knoe-auth is a Spring Boot 3 / JDK 21 OIDC authorization server that validates Kerberos SPNEGO tokens and issues OIDC tokens for downstream services.
**Key implementation details:**
| Detail | Value |
|---|---|
| Deployment | `knoe-system/knoe-auth` |
| SPNEGO endpoint | `https://api.prole.org/auth/spnego` |
| Keytab secret | `knoe-system/knoe-auth-keytab` (`HTTP/api.prole.org@PROLE.ORG`, AES-only) |
| JDK Subject API | `Subject.callAs()`**not** `Subject.doAs()` (removed in JDK 21) |
| RC4 | Hard-removed in JDK 21 JGSS — keytab and AD account must be AES-only |
| krb5.conf | Mounted via ConfigMap; `permitted_enctypes = aes256 aes128` (cannot re-enable RC4 here) |
Keytab provisioning (`etc/init_knoe_users.sh`):
```bash
# On myrddin — set AES-only, reset password, export and rekey keytab
sudo ldbmodify -H /var/lib/samba/private/sam.ldb <<EOF
dn: CN=knoe-auth,CN=Users,DC=prole,DC=org
changetype: modify
replace: msDS-SupportedEncryptionTypes
msDS-SupportedEncryptionTypes: 24
EOF
sudo samba-tool user setpassword knoe-auth --newpassword="$(openssl rand -base64 32 | tr -d '=/+' | head -c 40)"
# Export full domain keytab; rekey to HTTP/api.prole.org@PROLE.ORG in Python
# (samba-tool exportkeytab --principal=HTTP/... returns empty — see note below)
```
**Note:** `samba-tool domain exportkeytab --principal=HTTP/hostname` always produces an empty keytab on Samba 4 — the filter only matches UPN format, not SPN format. The correct procedure is to export the full domain keytab and rekey the AES entries to the SPN principal name using the Python script embedded in `infrastructure/playbooks/gitea_spnego_keytab.yml` (same logic applies for all HTTP service principals).
### Supabase Studio SSO (`db.prole.org`)
```
Browser → Kong → oauth2-proxy → knoe-auth OIDC → oauth2-proxy (cookie set)
→ Kong (upstream to studio)
→ Supabase Studio
```
- **oauth2-proxy** handles the OIDC callback and sets a session cookie (`_oauth2_proxy`)
- **Supabase Kong** dashboard route: `cors` plugin only — `basic-auth` plugin **removed** (it blocked oauth2-proxy's proxied requests)
- The `basic-auth` removal is applied directly to the live ConfigMap and must be re-applied after any `helm upgrade` of the supabase chart
Supabase Studio access:
```bash
# Via browser (automatic SPNEGO with Chrome policy)
open https://db.prole.org
# Check oauth2-proxy is passing through correctly
curl -I https://db.prole.org/oauth2/sign_in
```
Chrome SPNEGO policy (`/Library/Managed Preferences/com.google.Chrome.plist`):
```xml
<key>AuthServerAllowlist</key>
<string>*.prole.org</string>
<key>AuthNegotiateDelegateAllowlist</key>
<string>*.prole.org</string>
```
Deploy to a Mac workstation:
```bash
make workstation
# or: ansible-playbook infrastructure/playbooks/workstation_kerberos.yml --ask-become-pass
```
### git.prole.org SPNEGO (Gitea)
```
Browser/curl → Kong → gitea-spnego-proxy (:4000) → Gitea (:3000)
├─ No Authorization header → 401 + WWW-Authenticate: Negotiate
├─ Authorization: Negotiate <token> → SPNEGO validate → X-WEBAUTH-USER → Gitea auto-login
└─ Authorization: Basic/token → pass-through → Gitea auth
```
**Component:** `gitea/spnego-proxy/` — Go binary using `gokrb5/v8`, built for `linux/arm64`.
| Detail | Value |
|---|---|
| Image | `myrddin.prole.org:5000/gitea-spnego-proxy:latest` |
| Keytab secret | `gitea/gitea-krb5-keytab` (`HTTP/git.prole.org@PROLE.ORG`, AES-only, KVNO 4) |
| AD account | `CN=gitea-http,CN=Users,DC=prole,DC=org` (`msDS-SupportedEncryptionTypes=24`) |
| Rebuild | Build on myrddin (native arm64); `docker build` then `docker push localhost:5000/...` |
Keytab rotation:
```bash
ansible-playbook infrastructure/playbooks/gitea_spnego_keytab.yml -e force_keytab_reset=true
```
Manual rebuild (if source changed):
```bash
# Transfer source and build on myrddin (all nodes are arm64)
tar -czf /tmp/src.tar.gz gitea/spnego-proxy/ && scp /tmp/src.tar.gz myrddin:/tmp/
ssh myrddin "mkdir -p /tmp/spnego-build && tar -xzf /tmp/src.tar.gz -C /tmp/spnego-build && \
sudo docker build -t localhost:5000/gitea-spnego-proxy:latest /tmp/spnego-build/gitea/spnego-proxy/ && \
sudo docker push localhost:5000/gitea-spnego-proxy:latest"
kubectl --context=prole-service-cluster -n gitea rollout restart deployment/gitea-spnego-proxy
```
Smoke test:
```bash
curl -s -o /dev/null -w "%{http_code}\n" https://git.prole.org/ # → 401 (challenge)
curl -s -o /dev/null -w "%{http_code}\n" --negotiate -u : https://git.prole.org/ # → 200 (authed)
```
---
## Status
Knoe is an actively evolving platform stack aimed at practical self-hosted, edge, and cloud operation.
**As of 2026-05-28** the following work has shipped on the prole.org fork:
**As of 2026-05-23** the following work has shipped:
- `fix(cfg)``_validate_cfg_values` prevents MagicMock reprs from leaking into `conf/*.cfg` (`ef20c8a`)
- `feat(env)``gke_` kubecontext prefix auto-detected as `prod`; `status.py` context helpers (`da0fd2c`)
- `refactor(mock_val)``prole_*` shell lib and cfg tooling renamed to `knoe_*` namespace (`11064cb`)
- `feat(mock_val)` — init scripts rewritten; 10+ new service init scripts added (`3f96f66`)
- `feat(mock_val)` — diagnostics, utilities, certs, and operational scripts added (`3943d1b`)
- `feat(scripts)` — upstream knoe-db sync script + procedure doc (`decb9a5`)
- `feat(infrastructure)` — Pi-hole DNS flush Ansible playbook (`3ada16c`)
- `docs` — shipped briefs moved to `docs/completed/`; `conf/service/knoe.cfg` added (`73dce03`)
- `feat(mock_val)``common_core_lib` mode-aware config path helper (`d806905`)
- `fix(conf)` — MagicMock contamination removed from `conf/k3d.cfg` and `conf/k3s.cfg` (`568f03b`)
- `docs(branches)` — upstream knoe-db/20260523 review doc, branch index, and Junie integration brief (`0e822ea`)
- `feat(pg-knoe-auth)` — upstream PostgreSQL JWT auth extension imported; compiled in `knoe-db` image (`57886f9`)
- `docs(branches)` — Task 1 marked complete in branch index (`e5cf9b1`)
**Identity / SSO (May 2026)**
- `feat(gitea)` — SPNEGO Kerberos SSO for `git.prole.org`; proxy issues `WWW-Authenticate: Negotiate` challenge; `gitea_spnego_keytab.yml` Ansible playbook for full provisioning lifecycle (`5077e13`)
- `fix(ansible)` — workstation install script + `make workstation` target for Chrome SPNEGO policy on personal Macs (`b245593`)
- `fix(spnego)``Subject.callAs()`, AES-only keytab, and `krb5.conf` sync for JDK 21 knoe-auth (`c1d2a91`)
- `feat(oidc)` — knoe-auth routing through Kong; Flyway schema baseline for clean OIDC DB (`9523045`)
- `feat(prole)` — knoe-auth bootstrap on k3s; tenant onboarding; cluster stabilisation (`cf33342`)
Working tree is clean. Upstream sync tooling is operational; `upstream/knoe-db/20260523` review is in progress (Task 1 ✅).
**Infrastructure (earlier)**
- `fix(cfg)``_validate_cfg_values` prevents MagicMock reprs from leaking into `conf/*.cfg`
- `feat(env)``gke_` kubecontext prefix auto-detected as `prod`; `status.py` context helpers
- `refactor(mock_val)``prole_*` shell lib and cfg tooling renamed to `knoe_*` namespace
- `feat(mock_val)` — init scripts rewritten; 10+ new service init scripts added
- `feat(scripts)` — upstream knoe-db sync script + procedure doc
- `feat(pg-knoe-auth)` — upstream PostgreSQL JWT auth extension imported; compiled in `knoe-db` image
Expect the architecture to continue being refined toward:
- cleaner bootstrapping
- better shard isolation
- smoother rejoin/reset behavior for cluster nodes
- clearer service boundaries
- improved onboarding and operations documentation
Working tree is clean. Browser SPNEGO SSO is live on `db.prole.org` and `git.prole.org`.
---
@ -347,6 +506,21 @@ Upstream changes are pulled into a dated review branch (`upstream/knoe-db/YYYYMM
See [`docs/upstream-knoe-db-sync.md`](docs/upstream-knoe-db-sync.md) for the full review-and-merge procedure.
### Fork-specific files
Files added or substantially modified in the prole.org fork (not present or not relevant upstream):
| Path | Purpose |
|---|---|
| `infrastructure/` | Ansible roles, playbooks, and inventory for the prole.org cluster |
| `infrastructure/playbooks/gitea_spnego_keytab.yml` | Gitea SPNEGO keytab provisioning |
| `infrastructure/playbooks/workstation_kerberos.yml` | Chrome SPNEGO policy + krb5.conf for macOS workstations |
| `infrastructure/playbooks/kerberos_trust_setup.yml` | PROLE.ORG ↔ KNOE.LOCAL cross-realm trust |
| `infrastructure/bin/install_workstation.sh` | Wrapper for workstation Ansible (adds `--ask-become-pass`) |
| `gitea/spnego-proxy/` | Go SPNEGO reverse proxy for `git.prole.org` |
| `deploy/opentofu/k3s/manifests/knoe/gitea-spnego-proxy.yaml` | k8s deployment for the SPNEGO proxy |
| `conf/k3s.cfg` | prole.org k3s cluster configuration |
---
## License