Commit Graph

729 Commits

Author SHA1 Message Date
chrisfu
abc458260c ansible: lock in docker_build_host role for the arm64 build lane (gandalf)
Provisions the docker engine on [build_hosts] (gandalf) for the native arm64 daily
knoe-db image build → registry.knoe.dev, run via a remote buildx builder. gandalf is
also a k3s_agent, so the role holds iptables FORWARD=ACCEPT to protect k3s pod
networking (docker's containerd coexists with k3s's on separate sockets). Verified
idempotent: --check against gandalf = ok=5, changed=0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:11:08 -07:00
chrisfu
30df7d1c12 preserve: prole-specific OpenBao K8s-auth + Kong secret-bridge + phase2-3 canary runbook
Captured for the knoe-db convergence (Phase 2): configure_kubernetes_auth() (OpenBao
K8s auth → scoped knoe-jobs tokens vs root), the Kong knoe-secret route (secure-dropbox
bridge /secret/* → knoe-jobs:8081), and the prole.org canary-deploy runbook. None of
this is in canonical knoe-db — it is prole-staging-specific, not stale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:42:27 -07:00
chrisfu
96fba3c9bb chore(gitignore): ignore .venv-ansible-winrm/ 2026-06-12 14:25:47 -07:00
chrisfu
9b0005aa4c fix(samba_ad_dc): bind to LAN IP only so the DC stops registering junk DNS
The AD DC's smb.conf set no `interfaces` / `bind interfaces only`, so Samba
bound to — and samba_dnsupdate registered into DNS — every interface on the
host. On myrddin that meant the Docker bridge (172.17.0.1) and k3s/flannel
CNI addresses (10.42.0.0, 10.42.0.1) were published as A records for both the
prole.org apex and `myrddin`, alongside the real 10.0.0.3. Clients then
round-robined onto unroutable addresses, producing the long-standing
"resolves, then doesn't" internal DNS flakiness.

Confine Samba to loopback + the LAN service IP:

  interfaces = lo 10.0.0.3
  bind interfaces only = yes

This scopes both service binding and DNS self-registration to the real
address, so the junk records stop being (re)created on restart.

  - smb.conf.j2: emit the two directives, gated on bind-interfaces-only being
    enabled AND a non-loopback IP being present (empty -> directives omitted,
    never binds loopback-only by accident).
  - defaults: samba_ad_dc_lan_ip ("" by default), samba_ad_dc_bind_interfaces_only
    (true), samba_ad_dc_interfaces (lo + lan_ip), all documented.
  - tasks: assert samba_ad_dc_lan_ip is non-empty before deploying smb.conf
    when bind-interfaces-only is on, so a missing value fails fast instead of
    rendering the DC unreachable.
  - group_vars/ad_dc: set samba_ad_dc_lan_ip=10.0.0.3 (myrddin's LAN address).

Deploying notifies the existing Restart samba-ad-dc handler. Pre-existing junk
records must be deleted once by hand; they will not be re-registered after the
restart. Template rendering verified for both the set and empty-IP cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:43:28 -07:00
chrisfu
0ae0ac4a8a fix(samba_reverse_dns): inline reverse-zone list so it survives tag filtering
`Create reverse DNS zones if missing` looped over `samba_reverse_zones`, a fact
built by a separate `set_fact` task that carried no tags. Under a tag-filtered
run (e.g. --tags samba_reverse_dns) that set_fact was skipped, so the variable
was undefined and the play failed:

    TASK [samba_reverse_dns : Create reverse DNS zones if missing]
    'samba_reverse_zones' is undefined

A set_fact must carry the same tags as every task that consumes it. Rather than
re-add tags (which breaks again under any other tag combination), compute the
zone list inline in the loop and drop the now-dead set_fact. The role is now
correct under any tag selection. lan_reverse_zone and k3s_reverse_zones are
defined in group_vars/all/vars.yml, so they are always available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:35:29 -07:00
chrisfu
c0c43bcf6a fix(samba_dns): strip zone suffix correctly so internal A/CNAME/PTR records are created
Forward A/CNAME and reverse PTR records reported changed=0 / ok but never
resolved by short name on the Samba AD DC (myrddin.prole.org). Root cause was
regex escaping inside YAML folded block scalars (">-"): unlike double-quoted
scalars, block scalars do NOT process backslash escapes, so a pattern written
as '\\.' reached Jinja as a literal backslash + any-char and never matched.

Consequences:
  - regex_replace() never stripped the zone suffix, so records were created
    under their full FQDN (e.g. a record literally named "sg2428lp.prole.org"
    inside zone "prole.org"). `samba-tool dns query ... prole.org sg2428lp A`
    then returns WERR_DNS_ERROR_NAME_DOES_NOT_EXIST.
  - regex_findall() of existing values always returned [], so stale records
    were never pruned and idempotency only survived via RECORD_ALREADY_EXISTS.

The leading/trailing whitespace from the folded scalar was a red herring: the
command module's shlex tokenization collapses it, so a single-token name
survived intact. The escaping was the actual defect.

Fix:
  - Convert the templated set_fact expressions from ">-" block scalars to
    double-quoted single-line scalars (one consistent escaping convention,
    matching the already-working samba_target_zone line; also removes the
    stray whitespace).
  - Add an assertion that the computed record name is a clean, non-empty,
    whitespace-free token.
  - Add a post-apply verification re-query + assert that the expected
    value(s) are actually present, turning a silent no-op into a hard failure.

Applies to roles/samba_dns (ensure_a.yml, ensure_cname.yml) and
roles/samba_reverse_dns (ensure_ptr.yml).

Verified end-to-end through ansible-playbook against a stateful fake
samba-tool: correct short names (sg2428lp, git, @, registry), stale-value
removal, internal.prole.org zone routing, idempotent re-runs (changed=0),
and the verify-assert failing loudly when an add silently does not persist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:32:00 -07:00
chrisfu
c9591a5da2 docs(runbook): stage db.prole.org GSSAPI auth for the airgap lane
Complete, review-ready runbook to light up Kerberos/GSSAPI on db.prole.org so
`kn db --service prole.org` authenticates with a local PROLE.ORG ticket — the
airgap data lane (no password, no knoe-auth, no internet).

Nothing applied. Delivers:
- Samba SPN + keytab export steps (myrddin): postgres/db.prole.org@PROLE.ORG
- k8s keytab secret (knoe-db-gss-keytab-secret.example.yaml template)
- exact knoe-db.yaml diff: krb_server_keyfile, hostgssenc pg_hba (include_realm=0
  → role), projectedVolumeTemplate keytab mount, chrisfu/ron managed roles
- apply + end-to-end verify (kinit → kn db --service prole.org)
- failure-mode triage, rollback, follow-ups

Hooks into the manifest's existing placeholders (knoe-db.yaml lines 38 + 45,
which already note 'Kerberos disabled on k3s'). Image is already --with-gssapi.

Flags the PROLE.ORG (Kerberos) vs PROLE.LOCAL (knoe.user default) discrepancy
to reconcile. Grants intentionally deferred — connect succeeds, reads gated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 02:59:07 -07:00
chrisfu
76522eace2 chore(vault): add WinRM passwords for morgoth (chrisfu) and fairyland (minecraft)
vault_winrm_password_morgoth and vault_winrm_password_fairyland stored for
Ansible WinRM auth to Windows GPU workstations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 21:28:15 -07:00
chrisfu
26790b8ced fix(pihole): use Pi-hole 6 dns.hosts for knoe.dev records
Pi-hole 6 uses pihole-FTL --config dns.hosts as the authoritative local DNS
store. dnsmasq address= directives still work for new domains (e.g. mcp.0.knoe.dev)
but Pi-hole may serve stale upstream cache over them for previously-resolved
domains (e.g. auth.0.knoe.dev was cached from old DNS before the fix).

dns.hosts entries always win, idempotent approach: strip old *.0.knoe.dev entries,
append new ones, update via pihole-FTL --config + pihole reloaddns.

auth.0.knoe.dev: 34.120.221.5 (wrong) → 34.36.71.141 (knoe-auth GCE LB) ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:00:40 -07:00
chrisfu
3877cbfd38 feat(pihole): add knoe.dev GKE service records for LAN split-horizon DNS
Adds dnsmasq address= directives for all active knoe.dev GKE endpoints
so LAN clients (morgana, Pi nodes) resolve them without hitting external DNS.

Records added:
  mcp.0.knoe.dev   → 8.232.93.14    (knoe-mcp GCE LB)
  auth.0.knoe.dev  → 34.36.71.141   (knoe-auth GCE LB)
  api.0.knoe.dev   → 34.120.221.5   (supabase-kong GCE LB)
  db.0.knoe.dev    → 34.120.221.5   (supabase-kong GCE LB)
  app.0.knoe.dev   → 34.120.221.5   (supabase-kong GCE LB)

auth.0.knoe.dev was previously resolving to 34.120.221.5 (wrong — supabase LB)
causing the knoe-auth managed cert to be stuck Provisioning for 20 days.
Correct IP is 34.36.71.141. Also update auth.0.knoe.dev in external DNS
(name.com) to unblock the GCP managed cert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:39:47 -07:00
chrisfu
11ba0c6f57 feat(winrm): wire Ansible WinRM for morgoth+fairyland Windows GPU hosts
- ansible.sh: export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES (fixes macOS
  fork safety abort when pywinrm loads ObjC frameworks before fork())
- hosts.ini: add winrm scheme=http port=5985, transport=ntlm for Windows hosts;
  remove hardcoded ansible_user (now per-host)
- group_vars/workstations_windows/vars.yml: created; WinRM connection vars
  (ansible_user/password resolved per-host)
- host_vars/morgoth.prole.org.yml: ansible_user=chrisfu + vault ref
- host_vars/fairyland.prole.org.yml: ansible_user=minecraft + vault ref
- windows_ollama_models.yml: fix PowerShell ollama show check — wrap in
  try/catch so ErrorActionPreference=Stop does not abort on model-not-found

Result: mxbai-embed-large-2k (num_ctx=2048) created on both hosts.
Idempotent — re-running only recreates if Modelfile hash changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 02:16:05 -07:00
chrisfu
f0d3a85fbc Avoid fact gathering for Windows Ollama playbook 2026-06-01 23:24:11 -07:00
chrisfu
e5fe9dc2a1 Disable Unix become for Windows inventory hosts 2026-06-01 23:08:40 -07:00
chrisfu
3b1014ee4d Disable sudo become for Windows Ollama playbook 2026-06-01 23:03:49 -07:00
chrisfu
5f60ede5d7 Add Windows Ollama model configuration playbook 2026-06-01 22:04:11 -07:00
chrisfu
5aacee46d8 feat(migrations): 001 — prole ekosystem vector schema
- Prole registered as tenant_id=1, uuid=000necda5b3a6tc2 (DNS anchor)
- Project embedding store uuid=000nectf23m7865j (tenant_id=1 in bits)
- Schema p_000nectf23m7865j: chunks, embeddings, embeddings_merged (vector 1024)
- HNSW index on embeddings_merged for cosine similarity search
- Cross-grant issued to tenant 0 (knoe.dev MCP): grant id=000neddhjsr8eadg
- Establishes knoe-db/schema/migrations/ directory (sqitch wiring: tomorrow)

Applied live to pg.prole.org at 2026-05-30 ~02:25 UTC-7.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-30 02:25:17 -07:00
chrisfu
23cf4a8585 fix(ekosystem): register_tenant — RETURN QUERY + smallint cast
INSERT ... RETURNING needs RETURN QUERY in PL/pgSQL RETURNS TABLE functions.
tenant_id column is smallint in knoe.tenants; cast to integer to match
the function's declared return type.

Reproduced on pg.prole.org at 2026-05-30 during Phase 2 canary deploy.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-30 02:18:09 -07:00
chrisfu
d114801758 feat: wire ekosystem UUID system into CNPG manifests (Tasks 1, 3, 4)
Task 1 — ConfigMap + CNPG wiring:
- Add k8s/knoe/knoe-ekosystem-sql.yaml: ConfigMap embedding ekosystem.sql
  and ekosystem_objects.sql for CNPG postInitApplicationSQLRefs
- Add scripts/gen-ekosystem-configmap.py: generation script to keep the
  ConfigMap in sync with knoe-db/schema/ekosystem*.sql source files
- Add Makefile target: make k8s/knoe/knoe-ekosystem-sql.yaml
- Wire postInitApplicationSQLRefs into all three CNPG cluster manifests:
    k8s/knoe/knoe-db.yaml (k3s / prole-service-context production)
    deploy/gcp/gke/knoe-db.yaml (GKE)
    deploy/opentofu/k3s/manifests/knoe/knoe-db.yaml (OpenTofu k3s)
- Add knoe-ekosystem-sql.yaml to k8s/knoe/kustomization.yaml

Task 3 — Python counterpart utility:
- Add knoe/ekosystem.py: thread-safe EkosystemID generator matching the
  PostgreSQL bit layout [49:ts_ms|12:tenant|10:shard|11:seq], with
  decode() and can_access() helpers
- Add tests/test_ekosystem.py: 23 tests covering base36 encoding,
  round-trips, thread safety, can_access, and the spec round-trip assertion

Task 4 — knoe.user ekosystem_uuid column:
- Add ALTER TABLE knoe.user ADD COLUMN IF NOT EXISTS ekosystem_uuid text UNIQUE
  to postInitSQL in all three CNPG manifests

Task 2 (register prole tenant) requires a live DB connection — manual step.
Task 5 (LDAP/AD reconciler) is design-only per spec.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-30 00:26:48 -07:00
chrisfu
9c5c56beb5 docs(readme): Prole. banner and header
Replace the knoe ASCII art banner with a Prole. banner linking to all
four live services (svc/db/git/api.prole.org). Add # Prole. as the
top-level header with a one-paragraph description of the homelab
deployment and its relationship to upstream knoe-db. Demote the Knoe
platform overview to ## Knoe platform.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 08:18:38 -07:00
chrisfu
998c1bc94d chore: gitignore infrastructure/logs; add ekosystem UUID CNPG plan
- Add infrastructure/logs/ to .gitignore so ansible run logs
  (infrastructure/logs/ansible/*.log) are never accidentally committed
- Track docs/plans/junie/ekosystem-uuid-cnpg-wire.md — Junie brief for
  wiring the ekosystem UUID schema to the CNPG cluster (queue item #13)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 08:10:39 -07:00
chrisfu
dc099b99f9 fix(knoe-auth): wire Google Workspace login for prole.org
- Add KNOE_AUTH_BASE_URL=https://api.prole.org/auth to knoe-auth
  deployment; LoginController.java defaulted to api.0.knoe.dev causing
  redirect_uri mismatch on every Google login attempt

- Update oauth2-proxy-deployment-prole.yaml to reflect live config:
  --provider=oidc --oidc-issuer-url=https://api.prole.org/auth (was
  stale --provider=google; live deployment already switched to OIDC)

- Fix redirect URIs in knoe-auth-google-prole-secret.example.yaml:
  remove spurious double /auth/auth/ prefix (Kong strip_path=false so
  Spring @RequestMapping(/auth) gives /auth/callback/google not
  /auth/auth/callback/google)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 07:11:13 -07:00
chrisfu
0e937eb9db 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>
2026-05-28 06:14:53 -07:00
chrisfu
5077e1335d feat(gitea): SPNEGO Kerberos SSO for git.prole.org
Three-part fix to make browser and curl SPNEGO auth work end-to-end:

1. spnego-proxy: issue WWW-Authenticate: Negotiate challenge when no
   Authorization header is present so Chrome (with AuthServerAllowlist)
   and curl --negotiate automatically present Kerberos tokens. Previously
   the proxy only validated tokens if the client proactively sent them.
   Pass-through preserved for non-Negotiate schemes (Basic/token) so
   git CLI users with PATs continue to work via Gitea own auth.

2. gitea_spnego_keytab.yml: new Ansible playbook that provisions the
   gitea-http AD account (AES-only, msDS-SupportedEncryptionTypes=24),
   registers SPN HTTP/git.prole.org, resets the password to derive fresh
   AES keys, exports the domain keytab, and rekeys it to principal name
   HTTP/git.prole.org@PROLE.ORG that gokrb5 needs for keytab lookup.
   Key lesson: samba-tool exportkeytab --principal=HTTP/... returns empty;
   must export full domain keytab and rekey in Python.

3. init_gitea.sh: add setup_gitea_spnego() calling the Ansible playbook
   in k3s mode as part of the standard deploy flow, with inline notes
   on every non-obvious constraint discovered during this work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 05:19:59 -07:00
chrisfu
b245593b0c fix(ansible): workstation install script + Makefile target for local Macs
ansible_become_ask_pass in group_vars does not reliably trigger an
interactive sudo prompt for ansible_connection=local — Ansible cannot
prompt per-host mid-play. The fix is a wrapper script that passes
-K / --ask-become-pass before any play execution.

- infrastructure/bin/install_workstation.sh: auto-detects FQDN, adds
  --ask-become-pass, passes extra args through. Run from repo root:
    ./infrastructure/bin/install_workstation.sh
    make workstation
- Makefile: add 'workstation' target pointing to the script
- group_vars/workstations.yml: drop ansible_become_ask_pass (does not
  work), keep ansible_pipelining=false (needed for SSH workstations
  with sudo passwords — pipelining replaces stdin, breaking sudo -S)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:15:49 -07:00
chrisfu
d945d88b5d fix(ansible): workstations group vars — become_ask_pass + no pipelining
Personal Macs require a sudo password; the global become_ask_pass=False
and pipelining=True combination silently swallows the prompt and fails.

- ansible_become_ask_pass: true  → Ansible prompts for sudo password
- ansible_pipelining: false      → sudo can read stdin for the password;
                                   pipelining replaces stdin with a pipe
                                   which sudo treats as non-interactive and
                                   refuses to ask for a password at all.

Applies to [workstations] group (mac + linux). Has no effect on
[workstations_windows] (WinRM auth is separate from become).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:10:26 -07:00
chrisfu
9571f42568 fix(ansible): wire vault_password_file to repo-root .vault_pass
Both ansible.cfg files lacked vault_password_file, causing
'Attempting to decrypt but no vault secrets found' when running any
playbook that auto-loads the encrypted group_vars/all/ files.

- ansible.cfg (root): vault_password_file = .vault_pass
- infrastructure/ansible.cfg: vault_password_file = ../.vault_pass

.vault_pass is gitignored. Each machine needs its own copy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 21:05:49 -07:00
chrisfu
c1d2a91991 fix(spnego): Subject.callAs, AES keytab, and krb5.conf sync for JDK 21
- KerberosSpnegoService: replace Subject.doAs with Subject.callAs (JDK 18+)
  so Subject.current() is set correctly for JGSS credential lookup in JDK 21.
  Pass null GSSCredential to createContext so the mechanism auto-selects
  rather than failing with "No credential found for 1.3.6.1.5.5.2".
- knoe-auth-deployment: fix PROLE_KERBEROS_KEYTAB_PATH to /etc/knoe-auth/http.keytab
  (matches the knoe-auth-http-keytab secret volume mount).
- knoe-kdc-configmap: full rewrite from stale KNOE.DEV to KNOE.LOCAL;
  add PROLE.ORG realm (Samba AD at 10.0.0.3) and [capaths] for future
  cross-realm trust. Drop arcfour-hmac/des3 from permitted_enctypes —
  RC4 hard-removed in JDK 21 and service account now AES-only
  (msDS-SupportedEncryptionTypes=24). Fix hardcoded secret values in
  entrypoint.sh to use ${PROLE_KDC_MASTER_PASSWORD} variable references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 20:06:36 -07:00
chrisfu
9523045add feat(oidc): fix knoe-auth routing for Kong strip_path=false; add Flyway baseline
Add @RequestMapping(/auth) to all OIDC and login controllers so paths
match the Kong route (strip_path: false keeps /auth in the forwarded path).

Changes:
- authority/web: add @RequestMapping(/auth) to LoginController,
  OidcAuthorizeController, OidcTokenController, OidcUserInfoController
- authority/resources: add V1__oidc_authorization_codes Flyway migration
- application-prole.yml: add OIDC clientId/clientSecret env bindings
- knoe-auth-deployment.yaml: add Flyway baseline env vars and
  KNOE_AUTH_OIDC_CLIENT_ID/SECRET from knoe-auth-oidc-client secret

Result: db.prole.org login flow redirects correctly through
https://api.prole.org/auth/login

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 02:13:33 -07:00
chrisfu
cf33342500 feat(prole): bootstrap knoe-auth on k3s; tenant onboarding; cluster stabilisation
knoe-auth (prole.org k3s):
- Fix CNPG manifest drift: remove spec.backup.pluginConfiguration (CNPG 1.28 only),
  switch spec.certificates from serverTLSSecret to serverAltDNSNames
- Apply knoe-auth Round 1 schema + GRANTs manually (postInitSQL had never run on live cluster)
- Fix OIDC signing key generator: base64(DER) not base64(PEM) — OidcTokenService
  does Base64.decode() → PKCS8EncodedKeySpec which requires raw DER bytes
- Add OIDC controllers: authorize, token, userinfo, jwks, discovery
- Add prole Spring profile: cookieDomain, emailDomain, Kerberos config
- Add secret example templates: knoe-db-user, knoe-auth-oidc-signing, knoe-auth-google-prole
- Kong configmap: scope knoe-auth route to /auth prefix only

Tenant onboarding:
- Add etc/onboard_tenant.sh: provision/apply/rotate/status workflow backed by 1Password
  vaults; types: 'enterprise' (own Kerberos + domain) and 'tenant' (hosted, initContainer KDC)
- Provision 'Knoe Tenant - prole.org' vault; apply all 7 k8s secrets to knoe-system
- init_knoe_auth.sh: add explicit GRANT + ALTER DEFAULT PRIVILEGES for knoe role

Cluster stabilisation:
- gitea: roll back 14-day stuck rollout (RWO PVC + maxSurge=100% deadlock);
  patch deployment strategy to Recreate
- supabase: create supabase_admin role, _supabase db, _analytics schema, _realtime schema
  in CNPG — analytics and realtime had never connected since Helm install day 1
- knoe-db barman ObjectStore: add GCS-backed objectstore manifest + scheduled backup

Infrastructure:
- gandalf host_vars: k3s registry config
- pi host_vars: clean up stale entries
- knoe-db schemas: ekosystem.sql, ekosystem_objects.sql
- init_prole_app.sql: prole app DB initialisation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 00:50:37 -07:00
chrisfu
03d1c3d9e8 docs: update README and branch/plan indexes for pg-knoe-auth import (Task 1 complete) 2026-05-23 21:54:35 -07:00
chrisfu
e5cf9b1bb6 docs(branches): mark upstream-knoe-db-20260523 Task 1 complete (pg-knoe-auth imported) 2026-05-23 21:52:15 -07:00
chrisfu
57886f9268 feat(pg-knoe-auth): import upstream PostgreSQL JWT auth extension; compile in knoe-db image
- Copy pg-knoe-auth/ wholesale from upstream/knoe-db/20260523 (Task 1 of
  docs/plans/junie/upstream-knoe-db-20260523-integration.md).
- Extension: PG18 OAUTHBEARER JWT validator using libcurl + OpenSSL RS256.
- knoe-db/Dockerfile: add libcurl4-openssl-dev to dev deps; COPY src/ and
  build with make USE_PGXS=1 install after tds_fdw.
- NOT enabled in the default database build (absent from 20_create_extensions.sh).
  To enable: CREATE EXTENSION pg_knoe_auth; (requires pg_hba.conf oauth_issuer).

Closes Task 1 of upstream-knoe-db-20260523-integration.md.
2026-05-23 21:52:06 -07:00
chrisfu
0e822ea976 docs(branches): upstream knoe-db/20260523 review — branch doc, index, and Junie integration brief
- docs/branches/README.md: index of upstream review branches
- docs/branches/upstream-knoe-db-20260523.md: full analysis of 403-file diff
  (no shared history; conflict risk by area; recommended actions)
- docs/plans/junie/upstream-knoe-db-20260523-integration.md: 6-task Junie brief
  ordered by conflict risk (pg-knoe-auth → k3d manifests → briefs → authority
  OIDC fixes → knoe/core installer → etc/ init scripts)
- docs/plans/junie/README.md: integration brief added to active table
2026-05-23 21:44:13 -07:00
chrisfu
5babe04516 docs: update status — cfg files clean, no pending working-tree changes 2026-05-23 21:38:25 -07:00
chrisfu
568f03ba42 fix(conf): remove MagicMock contamination from k3d.cfg and k3s.cfg
Replace all <MagicMock ...> values with empty strings or sensible defaults:
- BooleanVar mocks → false
- StringVar mocks for known fields (cluster_name, db_host_port, db_namespace,
  db_username, cluster_env) → correct defaults
- All other StringVar mocks → empty string

Files are now safe to parse and commit. Protected going forward by
_validate_cfg_values() added in ef20c8a.
2026-05-23 21:38:08 -07:00
chrisfu
d806905b2e feat(mock_val): common_core_lib — mode-aware default config path helper
Add common_core_default_config_path() which resolves the correct knoe.cfg
path (k3d/k3s/gke) from KNOE_MODE/DEPLOYMENT_MODE/CLUSTER_ENV env vars,
falling back to the first cfg file found under conf/. Replaces the
hardcoded knoe.cfg reference in the config-loading comment.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:32:23 -07:00
chrisfu
73dce037f3 docs: reorganise briefs into docs/completed/; add conf/service/knoe.cfg
docs/completed/ — new directory; 9 shipped Junie briefs moved from
docs/plans/junie/ (02-k3s-prole-rename, 03-image-rename, 06-patch-garage,
07-init-cnpg-gke-sa-wiring, 13-podmonitor, 15-remove-dead-dashboard,
k3d-knoe-auth-dev-loop, phase2-oidc-gke-deploy, todo-1-cfg-save-path-bug).
docs/completed/README.md summarises all shipped work with dates/refs.

docs/plans/junie/README.md — updated to 2026-05-23; active/pending tables
reflect current state; shipped section now points to docs/completed/.

conf/service/knoe.cfg — new unattended deploy config for the service/k3s
environment (used by ./deploy.sh).

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:32:01 -07:00
chrisfu
3ada16c875 feat(infrastructure): add Pi-hole DNS flush playbook and logs dir
infrastructure/playbooks/pihole_flush_dns.yml — Ansible playbook to
flush Pi-hole DNS cache across inventory hosts.

infrastructure/logs/ — log directory for Ansible run output.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:52 -07:00
chrisfu
decb9a5ad0 feat(scripts): upstream knoe-db sync tooling and docs
Add scripts/sync_upstream_knoe_db.sh — idempotent script that registers
the upstream-knoe-db remote (git@git.knoe.dev:knoe-dev/knoe-db.git),
fetches upstream main, and creates a dated review branch
upstream/knoe-db/YYYYMMDD. Supports --dry-run; returns to previous
branch automatically.

Add docs/upstream-knoe-db-sync.md — step-by-step procedure covering
quick start, clean-tree requirement, diff/review, merge-to-main, PR
workflow, branch cleanup, and how to change the upstream branch name.

Update README.md: add 'Upstream fork — knoe-db' section with quick-start
commands and link to the sync procedure doc.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:48 -07:00
chrisfu
3943d1b298 feat(mock_val): add diagnostics, utilities, and misc operational scripts
Diagnostics:
  diag_gitlab_boot.sh, diag_gitlab_webservice_oom.sh, diag_gke_storage.sh

Utilities:
  ensure_default_storage_class.sh — set/verify default StorageClass
  preflight_kubecontext.sh        — validate kubecontext before ops
  onboard_engineer.sh             — new engineer onboarding script
  gen_oidc_signing_key.sh         — generate OIDC signing key
  fetch_prole_secrets.sh          — pull secrets from vault
  set-k3s-token-1password.sh      — store k3s token in 1Password
  sync_cnpg_grafana_dashboard.py  — sync CNPG dashboard to Grafana

Config/certs:
  krb5.local.conf, knoe-db-ca.crt

Updated: build-a-bao.sh, hostprobe-*.yaml, hosts.txt, knoe-db-passwwd.sh,
         repair_pipeline.sh, status.sh, status_common_services.sh
Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:40 -07:00
chrisfu
3f96f66a78 feat(mock_val): rewrite init scripts; add new service init scripts
Rewrites (updated for knoe namespace, GKE support, and current service configs):
  init_gitlab.sh, init_kong.sh, init_cnpg_backup.sh, init_monitoring.sh,
  init_garage_store.sh, init_gitea.sh, init_kdc.sh, init_openbao.sh,
  init_argocd.sh, init_certmgr.sh, init_common_services.sh, init_db_manager.sh,
  init_forgejo.sh, init_k3s_registry.sh, init_kerberos.sh, init_nginx_ingress.sh,
  init_port_forwards.sh, init_registry.sh, init_service_layer.sh

Deleted: init_cloudnative_pg.sh (superseded by init_cnpg_gke.sh)

New scripts:
  init_cnpg_gke.sh      — CNPG setup for GKE with Workload Identity
  init_knoe_auth.sh     — knoe-auth OIDC service init
  init_knoe_users.sh    — user provisioning
  init_redis.sh         — Redis init
  init_oauth2_proxy.sh / init_oauth2_proxy_prole.sh — OAuth2 proxy setup
  init_grafana_oauth.sh / init_grafana_oauth_prole.sh — Grafana OAuth wiring
  init_1password.sh     — 1Password Connect init
  init_min.sh           — minimal bootstrap

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:27 -07:00
chrisfu
11064cbd5b refactor(mock_val): rename prole_* shell lib and cfg tooling to knoe_* namespace
Shell library (mock_val/lib/shell/):
  - Delete prole_cmd.sh, prole_env.sh, prole_guardrails.sh,
    prole_secrets.sh, prole_string.sh, prole_yaml.sh
  - Add knoe_* equivalents with same functionality

Config tooling:
  - Delete mock_val/prole_cfg.sh, mock_val/sync-prole-cfg.py
  - Add mock_val/knoe_cfg.sh, mock_val/sync-knoe-cfg.py

Mirrors the broader prole → knoe project rename.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:14 -07:00
chrisfu
da0fd2c545 feat(env): auto-detect gke_ kubecontext prefix as prod; status.py context helpers
knoe/knoe_conf.py: normalize_environment() now recognises the gke_ prefix
(e.g. gke_plenary-truck-485623-p7_us-west3_knoe-dev-0) as the 'prod'
environment, matching real GKE kubecontext naming.

status.py: add _current_kubecontext() and _cfg_path_from_kubecontext()
so status.py auto-selects the correct cfg file based on the active
kubectl context without manual KNOE_CONF overrides.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:07 -07:00
chrisfu
ef20c8a598 fix(cfg): refuse to serialize non-string widget values into knoe.cfg
Add _validate_cfg_values() to knoe/ui/screens/cfg.py — called before
_filter_cfg_values_for_persistence so MagicMock (or any non-str) widget
values raise TypeError instead of being silently str()-coerced into
conf/<mode>.cfg.

Also re-raise TypeError/ValueError from _save_knoe_cfg so the error
escapes the outer broad except-Exception handler.

New test: tests/installer/test_cfg_save_refuses_mock_values.py
  - test_save_knoe_cfg_refuses_non_string_widget_values: MagicMock vars → TypeError
  - test_save_knoe_cfg_real_strings_produce_clean_cfg: real _Var stubs → clean cfg

Fixes TODO-1 / tracked in docs/completed/todo-1-cfg-save-path-bug.md.
conf/k3d.cfg and conf/k3s.cfg still contain stale MagicMock values from
before this fix and must be regenerated before committing.

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:02 -07:00
chrisfu
b5f17ffa72 fix(samba_dns): update vault with correct AD Administrator password
vault_samba_dns_admin_pass was stale — samba-tool commands were failing
with NT_STATUS_LOGON_FAILURE. Updated to match current AD Administrator
credential so the samba_dns role can manage records end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:58:09 -07:00
chrisfu
a2a82bc495 fix(samba_dns): use admin credentials instead of machine Kerberos (-P)
All samba-tool dns commands were using -P (machine account Kerberos) which
silently failed with no error — tasks reported changed=0 but records were
never written. Switch to -U Administrator --password={{ samba_dns_admin_pass }}
which uses the vault-protected admin credentials that were already defined
but never wired up.

Also fix regex patterns in record parsers: samba-tool output uses `A: IP`
and `PTR: fqdn` format, not `A IP` / `PTR fqdn` (space-separated), so
updated regex_findall patterns to match `TYPE:\s+value`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:15:13 -07:00
chrisfu
e014fd5b71 feat(k3s): add pg.prole.org as CNPG postgres endpoint with split-horizon DNS
- knoe-db.yaml: switch to CNPG-managed TLS cert with serverAltDNSNames
  (pg.prole.org + knoe-db-rw cluster service) — removes static serverTLSSecret/serverCASecret
- dns.yml: add pg.prole.org A record to prole_k3s_dns_records (10.0.0.3, 10.0.0.6)
  for Ansible-managed split-horizon DNS via Samba AD DC
- k3s.cfg: align KNOE_HOME paths to ~/dev/prole, add PROLE_KDC_* vars, remove
  hardcoded KUBECTL_CONTEXT (kubeconfig current-context is authoritative)
- prod.cfg: add PROLE_KDC_STORAGE_CLASS = prole-iscsi
- onepassword.py: skip vault check gracefully when no 1Password session active (non-TTY)
- CLAUDE.md: document production postgres connection string and DNS/CA cert ops

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 22:46:46 -07:00
chrisfu
f34d10908a docs(knoe-auth): use [placeholder] instead of <placeholder> in mermaid
Mermaid 10.7.0 chokes on bare <placeholder> in message text, parsing
it as HTML. Swap to square brackets in the cross-realm §4.3 sequence
diagram and the Gitea SPNEGO §5 diagram:

  <svc-host>, <SPNEGO>          (§4.3)
  <blob>                        (§5)

Matches the same fix landed in knoe-db's docs/knoe-auth.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:31:22 -07:00
chrisfu
f1450605c2 docs: knoe-auth — prole.org deployment overlay
Companion to the canonical architecture doc at
~/dev/knoe-db/docs/knoe-auth.md. Documents what's specific to the
prole.org homelab deployment:

  - Two upstream identity layers (Samba AD on myrddin for PROLE.ORG
    Kerberos, Google Workspace knoey.com for OIDC)
  - Three deploy targets (k3s on myrddin/merlin/gandalf, k3d for
    engineer dev loop, GKE for production API endpoint)
  - Hostname map (myrddin/morgana/zinfandel/db.prole.org/
    git.prole.org/api.knoe.dev/db.knoe.dev/etc.)
  - Cross-realm Kerberos trust setup (KNOE.LOCAL ↔ PROLE.ORG) via
    infrastructure/playbooks/kerberos_trust_setup.yml — RC4 pin
    explanation, kvno sync notes
  - Gitea SPNEGO at git.prole.org with Mermaid flow diagram
  - Supabase Studio status (db.prole.org Google OAuth working;
    db.knoe.dev knoe-auth OIDC blocked on pg_oauth image)
  - Engineer onboarding checklist (prole-specific steps)
  - Known fragility / current incidents table

Mermaid sequence diagram covers the cross-realm flow end-to-end
(workstation → Samba KDC → MIT KDC → service ticket), distinct from
the canonical doc's generic version.

References the matching Junie briefs in knoe-db for the open
fragility items (pg_oauth-image-install, init-user-schemas-heredoc-fix,
deploy-pipeline-restore).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:19:14 -07:00
chrisfu
3f34fa8b32 fix(installer): k3s --reset path hardening (kdc deploy, no-TTY 1password, context overrides)
Five fixes Junie surfaced while running the kdc-trust-reset-repeatable
Junie brief end-to-end (companion to commit 6f99f95). All hit during
the unattended `install.sh --mode k3s --reset` pipeline.

  - knoe/core/milestones.py (KerberosMilestone):
    For k3s and k3d modes, deploy the KDC pod via `init_kdc.sh start`
    before running init_kerberos.sh. init_kerberos.sh only chains into
    init_kdc.sh when PROLE_KDC_STANDALONE=1; without this hook the
    cluster came up with no KDC pod and the cross-realm trust principals
    had nowhere to land.

  - knoe/milestone.py (Milestone._get_script_env):
    Clear KUBECTL_CONTEXT in addition to KUBECONTEXT so stale entries
    from a different machine's cfg don't override the kubeconfig's
    own current-context.

  - etc/knoe_cfg.sh (_knoe_read_cfg):
    Skip KUBECTL_CONTEXT / KUBE_CONTEXT_NAME / KUBECONTEXT entries when
    reading cfg in k3s mode. Same theme: kubeconfig current-context is
    authoritative.

  - etc/init_1password.sh + knoe/core/onepassword.py:
    When running non-interactively (no TTY on stdin) and no `op`
    session exists, skip rather than hang on `op signin`. Lets the
    unattended pipeline proceed for k3s/k3d where in-cluster secrets
    are managed separately from 1Password.

Co-authored-by: Junie <junie@jetbrains.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 13:34:10 -07:00