Commit Graph

717 Commits

Author SHA1 Message Date
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
chrisfu
6f99f95f84 kdc: verify install.sh --reset converges to a working cross-realm trust
Fix _deployment_mode_hint() to correctly map cluster_env=service → k3s
mode. Previously 'service' was not handled in the normalized_env checks,
causing fallthrough to build.deploy_env='Dev' → mode='dev' → k3d
dependency required. On a k3s node (myrddin/merlin/gandalf) k3d is not
installed and the DependenciesMilestone fatally aborted the install.

Also fix get_required_dependency_ids(): k3s mode does not require k3d
(k3s is provisioned on the cluster nodes by Ansible, not by the
installer binary).

Fixes: install.sh --mode k3s --reset failing with
  'Dependencies unresolved after install attempts. Required: k3d'

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-11 02:33:38 -07:00
chrisfu
d6586cf1d9 kdc: backport init_kdc.sh trust fixes + add reset-repeatable Junie brief
Mirrors the knoe-db commit `ff7546d` patches into the prole copy of
`etc/init_kdc.sh` so a re-run of `install.sh --mode k3s --reset` from
this repo produces a working cross-realm trust without manual cluster
surgery. The k3s cluster is provisioned from this repo, so the source
fix must live here (knoe-db remains canonical for GKE).

Changes to etc/init_kdc.sh:

1. Create BOTH cross-realm krbtgts in MIT, not just the outbound one.
   The inbound `krbtgt/<REALM>@<TRUST_REALM>` (issued by Samba,
   decrypted here) was missing entirely; without it, MIT cannot
   decrypt inbound TGTs and the trust never carries traffic.

2. Pin both cross-realm krbtgts to RC4 (`arcfour-hmac:normal`). AES
   keys depend on salt, and Samba's `<remote_realm>+UPN` salt does
   not match MIT's `<local_realm>+<principal-no-realm>`; RC4 has no
   salt so both sides converge from the password alone. Matches the
   already-pinned Samba side (commit `ad1eced`).

3. Replace the broken "remote kadmin to Samba" reciprocal-trust block
   with a documented no-op pointing at
   `infrastructure/playbooks/kerberos_trust_setup.yml`. Samba AD
   does not accept additions over MIT's kadmin protocol; the block
   always failed with "Missing parameters in krb5.conf required for
   kadmin client".

4. Switch the KDC data volume from emptyDir to a PVC
   (claimName `knoe-kdc-data`, parameterized by
   `$PROLE_KDC_STORAGE_SIZE` and `$PROLE_KDC_STORAGE_CLASS`).
   State now survives pod restarts.

Adds Junie brief `docs/plans/junie/kdc-trust-reset-repeatable.md`
with four TDD acceptance criteria for an end-to-end --reset run on
the prole k3s cluster.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 01:24:34 -07:00
chrisfu
ad1ecedb5e kerberos_trust_setup: pin trust account to RC4 only (msDS-Supported... 4)
The matching knoe-db change (commit `ff7546d`) re-keys the MIT-side
cross-realm krbtgts with `-e arcfour-hmac:normal` to dodge the
AES-salt mismatch between Samba and MIT. For the trust to actually
carry traffic, the Samba side must also offer only RC4 on the
trust account — otherwise the client picks AES, the two sides
derive different keys from the same password (different salt
conventions), and TGT decryption fails.

Change `msDS-SupportedEncryptionTypes` from 28 (RC4+AES128+AES256)
to 4 (RC4 only). RC4 has no salt, so keys derive from the password
alone and both sides converge.

Updated docstring + summary debug print to reflect the new value
and the why.

Tracked alongside the Junie brief at
~/dev/knoe-db/docs/plans/junie/kdc-trust-reset-repeatable.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 00:29:01 -07:00
chrisfu
4fe8647953 init_kdc.sh: fix realm default from PROLE.LOCAL to KNOE.LOCAL
The post-rebrand realm is KNOE.LOCAL (matches knoe-db/etc/init_kdc.sh
already). The prole copy of init_kdc.sh was never updated, so the k3s
KDC pods on myrddin/merlin/gandalf got initialized with PROLE.LOCAL
realm baked into /etc/krb5.conf and /etc/krb5kdc/kdc.conf.

When chrisfu later wired up the Samba <-> in-cluster KDC cross-realm
trust playbook (kerberos_trust_setup.yml, targeting KNOE.LOCAL), the
trust was bidirectionally correct on the Samba side but the in-cluster
KDC could not decrypt the inbound krbtgt/KNOE.LOCAL@PROLE.ORG TGT
because its own realm was PROLE.LOCAL — different krbtgt key, every
TGS_REQ failed with "Decrypt integrity check failed" (manifested as
"Server not found in Kerberos database" in the client trace).

This commit fixes the source. To pick it up in-cluster, the active
KDC pod must be redeployed (its DB sits on an emptyDir volume so a
restart re-initializes against this default).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 23:37:31 -07:00
chrisfu
19d1f136de ansible: fix klist principal form in trust-setup summary print
klist shows the Kerberos principal (slash form
krbtgt/KNOE.LOCAL@PROLE.ORG), not the sAMAccountName underscore form
(krbtgt_KNOE.LOCAL). Update the summary debug task accordingly and
split the kvno hint into two stages (the cross-realm TGT first, then
a service ticket) so the user can isolate which step fails when
chasing salt/enctype issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:37:54 -07:00
chrisfu
78eef6fbd9 ansible: hoist ldb-tools install to top of play
The apt install task was positioned after the user-create step, so a
failure in create (or anywhere upstream) meant ldb-tools never got
installed. Move the apt task to the very top of the play, before the
cluster-secret lookups, so it always runs first on every invocation.

Drop the duplicate apt task that previously sat just before the
ldbmodify locator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:35:18 -07:00
chrisfu
4be9fbcafe ansible: idempotency check now uses underscore account name
The _trust_exists probe was still grepping samba-tool user list for
"krbtgt/KNOE.LOCAL" (slash form), but the actual sAMAccountName is
"krbtgt_KNOE.LOCAL" (underscore form, since slash is invalid in
sAMAccountName). _trust_exists was therefore always false, the create
step retried against the existing account on every re-run, and Samba
rejected it as a name collision.

Match against samba_account_name directly so re-runs route through
the setpassword branch instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:32:49 -07:00
chrisfu
52ab023ae2 ansible: install ldb-tools package (provides ldbmodify)
myrddin doesn't have ldbmodify installed — it's in the ldb-tools
Debian package, separate from samba-tool. Add an apt task ahead of
the locator so the package is in place before we try to use it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:20:26 -07:00
chrisfu
c45671283b ansible: locate ldbmodify binary explicitly (not on sudo PATH)
ldbmodify is part of the ldb-tools Debian package but is not in
root's default PATH under sudo on the Samba DC, so the user-attribute
modify step failed with "ldbmodify: command not found".

Add a locator task that probes the conventional install paths
(/usr/bin, /usr/sbin, /usr/local/bin, /opt/samba/bin) and falls back
to a depth-limited find under /usr and /opt. The downstream
ldbmodify invocation now uses the absolute path resolved here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:19:38 -07:00
chrisfu
3485046dbd ansible: temp-disable Samba password complexity for trust account write
samba-tool user create rejected the cluster-generated trust_shared_password
with "0000052D: Constraint violation - check_password_restrictions: the
password does not meet the complexity criteria". The trust password is
high-entropy random bytes from init_kdc.sh and may not satisfy AD's
"3 of 4 character categories" rule.

Wrap the create + setpassword steps in a block that:
  1. reads the current `samba-tool domain passwordsettings show` state
  2. flips complexity off if it was on
  3. does the user write
  4. always: restores complexity to its prior on/off state

The restore is in an `always:` clause so a failure inside the write
block does not leave the domain policy weakened.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:09:41 -07:00
chrisfu
aa541af3f5 ansible: use krbtgt_KNOE.LOCAL sAMAccountName + UPN for trust account
samba-tool user create rejected the sAMAccountName "krbtgt/KNOE.LOCAL"
with "samldb: sAMAccountName contains invalid '/' character". AD/Samba
disallow "/" in sAMAccountName even though Kerberos principal names
require it (krbtgt/REMOTE@LOCAL).

Switch storage name to "krbtgt_KNOE.LOCAL" and add an ldbmodify pass
that sets the canonical Kerberos identity on the same object:

  userPrincipalName     = krbtgt/KNOE.LOCAL@PROLE.ORG
  servicePrincipalName  = krbtgt/KNOE.LOCAL
  msDS-SupportedEncryptionTypes = 28  (RC4+AES128+AES256)

Samba KDC resolves principals by UPN/SPN, so a TGS-REQ for
krbtgt/KNOE.LOCAL@PROLE.ORG will hit this account.

Note: key-salt parity with the MIT side is NOT guaranteed yet. Samba's
default salt for AES keys is REALM+UPN; MIT's default for cross-realm
krbtgt is REALM+"krbtgt"+REMOTE. If kvno fails with "decrypt integrity
check failed" we'll add a keytab-export/import step in a follow-up
rather than try to coerce Samba's salt at creation time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 22:02:55 -07:00
chrisfu
6740e3dcc5 ansible: guard krbtgt smoke probe against --check mode failure
In --check mode the user-create step is skipped (it's mutating), so
the subsequent `samba-tool user show krbtgt/KNOE.LOCAL` probe fails
with rc=1 on a first dry-run because the user doesn't yet exist. Add
the same guarded failed_when used on the trust validate/show tasks
earlier: only fail when NOT (check_mode AND trust didn't pre-exist).

Real runs still fail hard on rc!=0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 21:57:51 -07:00