Commit Graph

34 Commits

Author SHA1 Message Date
chrisfu
d5f6e8f6a9 fix(gitea): set recovery password after admin promotion, not before
Gitea's AdminEditUser PATCH with source_id:0 resets the password field
as a side effect when applied to a SPNEGO-registered user (the auth
record re-initialisation clears the local password). Setting the
password before the promotion PATCH meant it was immediately wiped.

Move the 1Password recovery password step to run after the admin
promotion PATCH so the final Gitea state matches 1Password.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:15:50 -07:00
chrisfu
5d545d238c fix(gitea): include email in admin API set-password PATCH call
Gitea's AdminEditUser endpoint requires email alongside login_name
and source_id. Without it the server returns 200 but silently
ignores the password field — causing login failures despite the
script reporting success.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:05:29 -07:00
chrisfu
cad75996b5 fix: set recovery password unconditionally after any admin token path
The 1Password password-setting block was nested inside path 2 of
promote_gitea_admin(). When path 1 found a cached gitea-admin-token
k8s secret, path 2 was skipped entirely and the recovery password
was never set in Gitea, leaving the user unable to log in.

Move the password-setting step to run unconditionally after all four
token paths complete. Uses _op_ensure_auth() so it degrades gracefully
in CI/headless environments where 1Password is unavailable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:33:15 -07:00
chrisfu
7b391ab5ed feat(onboarding): scalable Kerberos SSO onboarding — service account auth, Ansible workstation policy
Problem: 'defaults write' is not an onboarding path, it's a support
incident waiting to happen. Browser SPNEGO must be deployed via managed
policy at machine provisioning time, not per-user.

Changes:
  etc/init_knoe_users.sh
  - _op_ensure_auth(): tries OP_SERVICE_ACCOUNT_TOKEN env, k8s secret
    'op-service-account-token', existing interactive session, and op-run
    inheritance — in that order. Gives actionable instructions when none
    work, including the correct 'op run --' invocation documented in k3s.cfg.
  - gitea_ensure_password(): calls _op_ensure_auth() at entry; eliminates
    the silent 'op not authenticated' failure path.
  - Script header: documents 'op run -- bash etc/init_knoe_users.sh' as
    the intended invocation for admin's laptop.
  - 'Next steps' output: replaces 'defaults write' with reference to
    workstation_kerberos.yml Ansible playbook.

  infrastructure/playbooks/workstation_kerberos.yml (new)
  - Deploys /etc/krb5.conf (PROLE.ORG realm, myrddin KDC) to all managed
    endpoints.
  - Deploys Chrome + Edge managed policy (AuthServerAllowlist) on macOS
    and Linux — no per-user browser configuration ever required.
  - Idempotent; run during laptop provisioning or re-run at any time.
  - Targets 'workstations' Ansible group.

  infrastructure/inventory/hosts.ini
  - Adds [workstations] group with example entries and onboarding notes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:10:32 -07:00
chrisfu
acfdee1fdc fix(gitea): replace UUID placeholder email with configured address on admin promotion
SPNEGO auto-registration assigns a UUID@localhost placeholder email.
promote_gitea_admin() now detects the UUID pattern and replaces it with
the user's real configured email (KNOE_ADMIN_EMAIL / GITEA_ADMIN_EMAIL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:40:54 -07:00
chrisfu
0877fe5190 fix(gitea): downgrade 1Password failures to WARN; add signin hint
1Password credentials are optional (primary auth is Kerberos SPNEGO).
When op is not signed in on a headless server, show a WARN with a
helpful hint instead of an ERROR that suggests a hard failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:37:00 -07:00
chrisfu
cddd9c8889 fix(gitea): redirect log() calls to stderr in value-returning functions
log() writes to stdout, so log calls inside functions that return
values via printf/stdout contaminate the captured output.

gitea_helm_admin_token and gitea_ensure_password now redirect all
log() calls to stderr with >&2, keeping stdout clean for the
returned token/password string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:34:58 -07:00
chrisfu
23e87f074c fix(gitea): include email in PATCH body; fix HTTP status parsing; add diagnostics
Gitea admin edit-user API requires email in PATCH body.
Fix HTTP status parser (tr -d vs broken cut -d_).
Add logging to user-existence check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:31:34 -07:00
chrisfu
a2aaea8bda fix(gitea): add required scopes to API token creation (Gitea ≥1.22)
Gitea 1.22 requires explicit scopes in the token creation POST body.
Without them the API returns: "access token must have a scope".

Now requests: read/write:admin, read/write:user, read/write:issue,
read/write:repository — sufficient for admin user management.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:28:58 -07:00
chrisfu
f8c948b2ab fix(gitea): read admin password from running pod's RS, add API diagnostic logs
When a Deployment has been updated but the new pod keeps crashing before
configure-gitea runs (e.g. after a failed Helm upgrade), the running pod
still carries the old RS's password — which differs from the current
Deployment spec.

gitea_helm_admin_token() now walks: running-pod-RS → current-Deployment-spec,
trying each password until one produces a valid API token.

Also adds HTTP status + response body to gitea_api_token() failures so the
cause is visible without exposing credentials.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:27:11 -07:00
chrisfu
6de7218ec7 fix(gitea): read Helm admin credentials from Deployment env vars, not secret
The Gitea Helm chart (gitea-12.5.3) stores GITEA_ADMIN_USERNAME and
GITEA_ADMIN_PASSWORD as plain values in the configure-gitea init container
spec — not in a k8s Secret with key 'admin-password'.  The previous code
looked for a non-existent secretKeyRef and returned empty, causing the
REST API bootstrap path to fail.

Now reads credentials via:
  kubectl get deployment gitea ... env[?(@.name=="GITEA_ADMIN_PASSWORD")].value

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:14:21 -07:00
chrisfu
5fba20b651 fix(gitea): correct Helm admin secret name to 'gitea' (not 'gitea-gitea')
The Gitea Helm chart names the admin credentials secret after the release
name alone, not '{release}-gitea'. For release name 'gitea' the secret is
just 'gitea'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:06:26 -07:00
chrisfu
dbe249ce9f fix(gitea): REST API bootstrap path bypasses broken admin CLI; fix DB namespace resolver
deploy.sh: resolve_knoe_db_namespace() now checks DATABASE_NAMESPACE from
config (k3s.cfg: knoe-db) before probing namespaces. Previously it fell back
to 'default' because only 'knoe' was probed, baking a wrong DB hostname into
app.ini. The running server was unaffected (uses GITEA__database__HOST env var)
but the gitea admin CLI, which reads app.ini directly, could not connect.

init_knoe_users.sh: add gitea_helm_admin_token() which reads the Helm
bootstrap admin password from the gitea-gitea k8s secret and exchanges it for
a REST API token — entirely avoiding the broken CLI. Add gitea_api_set_password()
which uses PATCH /api/v1/admin/users/{user} to set the password via the running
web server (which has the correct DB connection) instead of kubectl exec.

promote_gitea_admin() now prefers the REST-API-only path:
  1. existing token (env / k8s secret)
  2. Helm admin k8s secret → REST token; set KNOE_ADMIN_PRINCIPAL password via API
  3. 1Password → CLI set-password → basic-auth token (fallback)
  4. kubectl exec generate-access-token (last resort)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 18:53:55 -07:00
chrisfu
abb38fc1e9 fix(cfg): pre-scan --mode before sourcing knoe_cfg.sh so k3s.cfg is loaded
knoe_cfg.sh selects the config file based on KNOE_MODE at source time.
Without pre-scanning argv, KNOE_MODE is empty and k3d.cfg wins the
fallback loop even when --mode k3s is passed.  This caused k3s.cfg values
(e.g. KNOE_ADMIN_PRINCIPAL=chrisfu) to be silently ignored, defaulting
to 'admin' and causing gitea_set_password to fail against a non-existent user.

Also surface gitea_set_password errors (without logging the password) so
failures are diagnosable rather than silent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 18:40:04 -07:00
chrisfu
58496a31e4 feat(gitea): 1Password-backed credential management; never log plaintext passwords
Replace the predictable temp-password pattern with a secure credential flow:

- gitea_ensure_password(): creates a 1Password item with --generate-password
  (1P generator, 32-char letters+digits) if one does not exist; retrieves it
  on re-runs (idempotent). Password never appears in logs or env vars.

- gitea_set_password(): sets the Gitea account password via kubectl exec
  (gitea admin CLI, no prior Gitea auth required).

- gitea_api_token(): exchanges basic-auth credentials for a REST API token,
  deleting any stale token of the same name first. Token only; password
  cleared from memory immediately after use.

- promote_gitea_admin(): tries credential sources in order:
  1. GITEA_ADMIN_TOKEN env / gitea-admin-token k8s secret
  2. 1Password (generates if missing) → set password → REST token
  3. kubectl exec generate-access-token fallback (Helm bootstrap admin)
  Tokens persisted in k8s secret (revocable); passwords only in 1Password.

The Gitea password is a vault credential for emergency recovery only.
Normal auth is always Kerberos SPNEGO — users never need to type a password.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 14:07:47 -07:00
chrisfu
abb060d614 fix(users): remove fictitious ron@prole.org email; show Kerberos identity in provisioning log
Ron has no Google Workspace prole.org account. Email is now NULL in the DB
(NULLIF('')). The provision_user log line now always shows the Kerberos
principal explicitly and labels the email field separately so it is clear
which identity type is in use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 13:03:59 -07:00
chrisfu
57cd93ae00 fix(init_kdc): prevent stale k3d LOCAL_REGISTRY_INTERNAL from leaking into init_kdc.sh
In k3s mode, LOCAL_REGISTRY_INTERNAL set to a k3d value in the shell environment
leaked into init_kdc.sh subprocesses, causing pods to use the wrong image address
while the push went to myrddin.prole.org:5000.

Add _resolve_kdc_registry() helper that derives correct registry values from
KNOE_IMAGE_REGISTRY and PROLE_K3S_SERVER. Both init_kdc.sh call sites now pass
explicit overrides to prevent any inherited k3d value from leaking through.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 12:53:57 -07:00
chrisfu
385f74b4f7 fix(init_knoe_users): replace multiline die with err+exit to fix pipe syntax error
The 'die' call with a multiline string containing '|' (for base64 pipeline
in the help text) was parsed by bash as a pipe operator. Replace with
individual err() calls followed by explicit exit 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 12:33:58 -07:00
chrisfu
991c04d936 feat(init_knoe_users): 1Password fallback for KDC passwords; try prole-kdc-secrets
Password resolution order for PROLE_KDC_MASTER_PASSWORD and KNOE_GUEST_PASSWORD:
  1. Env var (existing)
  2. knoe-kdc-secrets k8s Secret (existing)
  3. prole-kdc-secrets k8s Secret (new — used by live prole deployment)
  4. 1Password via op read (new — OP_KDC_MASTER_PASSWORD_REF / OP_KDC_GUEST_PASSWORD_REF)
  5. PROLE_LOCAL_ADMIN_PASSWORD (existing)
  6. die with actionable message

Add try_op_read() helper: wraps op CLI, returns 1 if op not available or ref empty.

conf/k3s.cfg: add KNOE_ADMIN_PRINCIPAL=chrisfu, OP_KDC_MASTER_PASSWORD_REF,
  OP_KDC_GUEST_PASSWORD_REF placeholders (empty — user fills in op:// URIs).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 12:15:02 -07:00
chrisfu
2f8b88a97a fix(init_knoe_users): allow 'default' kubectl context in k3s/k8s mode
The k3s-generated kubeconfig on the cluster server node uses 'default'
as its context name. The existing guard rejected this unconditionally,
causing init_knoe_users.sh to always fail when run from myrddin.

Tighten the guard: only reject 'default' when KNOE_MODE is k3d (where
it would indicate the k3d context was not found). In k3s/k8s mode,
'default' is legitimate and the script proceeds normally.

Usage on myrddin: bash etc/init_knoe_users.sh --mode k3s initialize

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:44:02 -07:00
chrisfu
3d92781ae6 feat(init_knoe_users): add Gitea SPNEGO keytab step; fix PROLE.LOCAL→KNOE.LOCAL
- Add ensure_gitea_spnego_keytab(): provisions HTTP/git.prole.org@PROLE.ORG
  service principal, keytab, and gitea-krb5-keytab Secret automatically when
  samba-tool is available (i.e. running on myrddin); otherwise prints manual
  steps. Restarts gitea-spnego-proxy after keytab is created.
- Add GITEA_SPNEGO_HOST, GITEA_KRB5_AD_REALM, GITEA_KRB5_AD_USER config vars.
- Fix promote_gitea_admin: exec with 'su git' (gitea refuses to run as root);
  use KNOE_ADMIN_PRINCIPAL instead of hardcoded 'admin'; fall back to
  'gitea_admin' (Helm chart default) if the primary user has no token yet.
- Rename initialize() step numbers (9=ArgoCD, 10=SPNEGO keytab, 11=Gitea, 12=GitLab).
- Fix PROLE.LOCAL → KNOE.LOCAL in: default realm, SQL schema default, comments,
  next-steps message, trust principal comments.
- show_status: add gitea-krb5-keytab check alongside pg keytab check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:31:16 -07:00
chrisfu
ae23dbb9f8 fix(knoe-users): use sh-compatible base64 pipeline (drop pipefail for dash)
The container's /bin/sh is dash, which doesn't support set -o pipefail.
The preceding [ -s /tmp/pg.keytab ] check already guarantees the file is
non-empty before this line runs, so pipefail was redundant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 21:10:23 -07:00
chrisfu
30c7bc88fd fix(knoe-users): keytab reliability + mode-aware next steps + Gitea auto-token
Keytab export (silent failure bug):
- Always delete+recreate the postgres SPN with fresh random key; old key gone
  after EmptyDir wipe; kadmin.local -q exits 0 even on error so -norandkey
  silently failed
- Clean /tmp/pg.keytab before ktadd; verify non-empty with [ -s ] before
  proceeding; die loudly if keytab not written
- Fix base64 pipeline: set -o pipefail inside sh -c so base64 failure is not
  masked by tr exit code

Next steps:
- Suppress myrddin.prole.org Samba trust step in k3d mode (no AD server)
- Remove Grafana auth.proxy reminder (configured by Helm values already)

Gitea admin token:
- Auto-generate via kubectl exec into running Gitea pod before falling back to
  manual warning; persist as gitea-admin-token secret for future re-runs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 16:34:03 -07:00
chrisfu
9d7ef668b1 fix(knoe-users): fix KDC re-init pod selection and add readiness poll
Two bugs in the EmptyDir auto-recovery path:

1. get_kdc_pod returned items[0] without a phase filter, so a Terminating
   pod from the previous rollout could be selected. Add
   --field-selector=status.phase=Running to always get a live pod.

2. The rollout completes (pod Running) before the in-container entrypoint
   finishes kdb5_util create (no readiness probe configured). The single
   immediate kadmin.local check raced against DB init and lost. Replace
   with a 120s poll loop (5s interval) that re-queries the pod each tick
   so it handles both the timing race and any remaining pod-selection lag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 16:10:39 -07:00
chrisfu
e25090e6e1 fix(knoe-users): re-query KDC pod after re-init before retrying pre-flight
init_kdc.sh initialize rolls out a new deployment, creating a new pod with a
new name. The retry pre-flight was still exec-ing into the old (terminated)
pod. Re-query get_kdc_pod() after re-init so the retry and all subsequent
kadmin calls use the new pod.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 15:46:20 -07:00
chrisfu
7466bbee3a fix(knoe-users): auto-recover KDC database when pod restarted with EmptyDir
When the KDC pod restarts (e.g. after k3d node restart to apply registry
mirror config), its EmptyDir volume is wiped. ensure_kdc_pod() finds the
running pod and returns it, bypassing the bootstrap path. The subsequent
pre-flight check then fails because kadmin.local cannot access the database.

Instead of dying with a manual-intervention message, auto-recover by calling
init_kdc.sh initialize (with PROLE_KDC_NAMESPACE/NAME/MASTER_PASSWORD set)
then retrying the pre-flight. Only die if the retry also fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 15:38:18 -07:00
chrisfu
40ea30e4c3 feat(auth): init scripts and k3s/k8s auth manifests for knoe-auth
init_knoe_auth.sh: provisions KDC secrets via 1Password, applies GKE manifests
init_knoe_users.sh: creates Kerberos principals for initial contributors
kerberos-configmap.yaml: krb5.conf for OpenBao Kerberos auth (KNOE.DEV realm)
prole-auth-deployment.yaml: k3s auth + kdc sidecar deployment for homelab
prole-kdc-configmap.yaml: k3s KDC config for homelab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 14:47:27 -07:00
chrisfu
55b6a6aff3 Complete rebranding from prole to knoe and fix macOS application identity. Bulk renamed 'prole' to 'knoe' across code, scripts, and manifests. Updated Makefile with 'knoe', 'build', and 'start' targets. Implemented macOS Application Bundle (.app) support for correct identity. Fixed macOS 'Python' process name to 'Knoe.DB Installer' via Objective-C bridge. Standardized application name to 'Knoe.DB Installer' across all interfaces.
Co-authored-by: Junie <junie@jetbrains.com>
2026-04-22 15:08:35 -07:00
chrisfu
a14fa654e6 chore: checkpoint deployment and identity bootstrap updates
- switch default git provider to GitLab and add cluster kubecontext settings in prole config

- add knoe user/user_role schema grants in GKE and k3s database manifests

- extend init_knoe_users bootstrap to create schema tables and provision default users

- make deploy pipeline honor configured cluster env and gate GKE-only defaults

- update GitLab OIDC default frontdoor host and refresh generated manifest/db metadata

Co-authored-by: Junie <junie@jetbrains.com>
2026-04-10 20:55:30 -07:00
chrisfu
4a8d9cc90d feat: full GKE/prod deployment pipeline from UI to Artifact Registry
## GCP / Cluster Environment Screen
- Auto-populate Cloud tab from conf/prod/gcp.cfg on screen open (org_id,
  billing_account, billing_project, project_id)
- gcloud auth validity checked on screen startup; friendly modal dialog
  streams gcloud auth login output live so user never leaves the app
- Live GKE cluster browser: fetches clusters via gcloud container clusters
  list, displays with checkmark selector, auto-selects saved cluster
- Selecting a cluster runs get-credentials, sets KUBECONFIG/KUBECONTEXT,
  and syncs the region dropdown to the selected cluster's location
- Region dropdown populated live from gcloud compute regions list with
  checkmark on currently selected region; graceful fallback when offline
- New 'GCP Storage' tab with workload->StorageClass mapping (CNPG->premium-rwo,
  Redis/Monitoring->standard-rwo, Garage->garage-hdd) and Fetch from Cluster
- Provider readonly field styled correctly (no solid-black on macOS)
- Stale prole.cfg/conf/prole.cfg symlinks removed; all config I/O now
  resolves env-specific paths via prole_conf.entrypoint_path()

## GKE Autopilot Compatibility (Common Services)
- Synology iSCSI StorageClass and static PVs guarded behind PROLE_MODE!=k8s
  in init_openbao.sh (GKE Autopilot forbids hostPath/iSCSI volumes)
- In-cluster Docker registry (hostPath) skipped in k8s mode; GCP Artifact
  Registry used instead
- Kong renamed knoe-svc-kong in k8s mode; all health-check kubectl calls in
  init_common_services.sh and status_common_services.sh updated accordingly
- DNS endpoints switched from *.prole.org to *.knoe.dev in k8s mode
  (api.knoe.dev, git.knoe.dev, svc.knoe.dev); ingress uses gce class
- New GKE-clean Kong manifests under deploy/opentofu/k8s/manifests/prole/:
  no k3s node affinity, explicit Autopilot resource requests/limits

## Garage S3 Store (GKE)
- New garage-statefulset-gcp.yaml targeting garage-hdd StorageClass
  (pd-standard, avoids SSD_TOTAL_GB quota exhaustion in us-west3)
- New storageclass-gcp-hdd.yaml (pd-standard, Retain, WaitForFirstConsumer)
- GCP StorageClass manifests skipped on re-runs (Autopilot built-ins are
  immutable; skip-if-exists guard added)
- PVC deletion guard extended to cover any storageClass (not just synology)
  so stale claims are cleaned before StatefulSet recreation

## Topology (GKE Autopilot)
- DaemonSet collector skipped in prod mode (forbidden in kube-system by
  GKE Warden); Kubernetes-only node facts path used instead
- All ready GKE nodes assumed cnpg-eligible and monitoring-eligible without
  taint/synology-mount checks (skip_collector + assume_nodes_eligible flags)

## KUBECONFIG / kubectl (k8s mode)
- actions.py: new elif mode==k8s branch sets KUBECONFIG=~/.kube/config
  and injects KUBECONTEXT from prole_cfg_data into script env
- _build_kubectl_cmd falls back to Global.KUBECONTEXT when
  init_cluster.selected_kubectx is empty
- _activate_selected_gke_cluster persists KUBECONFIG/KUBECONTEXT to
  prole_cfg_data and saves prole.cfg immediately after get-credentials

## Database Build Screen (GKE)
- Registry display shows correct Artifact Registry URL
  (<region>-docker.pkg.dev/<project>/<namespace>/knoe-db) in green
- Build+push: gcloud auth configure-docker, auto-creates AR repository
  named after SERVICE_NAMESPACE (e.g. knoe-system) if missing, then
  docker tag + push; falls back to gcr.io if region unavailable
- GCP config loaded from conf/prod/gcp.cfg on every screen entry;
  keys normalised to lowercase so project_id lookup is always consistent

## Config / Namespace persistence
- prole_conf.py activate_environment: symlink creation removed; sets
  CLUSTER_ENV env-var so all subsequent calls resolve correct env directory
- knoe/ui/screens/__init__.py: startup config load uses entrypoint_path()
  instead of hardcoded conf/prole.cfg; seeds SERVICE_NAMESPACE=knoe-system
  for managed envs so Common Services never defaults to 'default'
- cfg.py _save_prole_cfg: saves to env-specific path via entrypoint_path()
- etc/prole_cfg.sh: removed all ln -snf symlink creation

Co-authored-by: Junie <junie@jetbrains.com>
2026-04-04 12:38:16 -07:00
chrisfu
a5cd0c45f4 Update conf, k8s registry, knoe UI screens, etc init scripts, and scan data
- conf/service/prole.cfg, port-mapping.cfg, cnpg-placement updated

- k8s/registry/deployment.yaml updated

- knoe/ui/screens: base.py, knoe_users.py, __init__.py updated

- etc/ init scripts refreshed (gitlab, knoe_users, registry, prole_cfg)

- modes/k3s/knoe-db/.version bumped; scan network_description and ansible_inventory updated

Co-authored-by: Junie <junie@jetbrains.com>
2026-04-02 23:40:16 -07:00
chrisfu
e2dd122111 fix(init_knoe_users): namespace/secret resolution aligned with init_kdc.sh
- Add PROLE_KDC_NAMESPACE to namespace fallback chain; never falls to default
- Add get_secret_value() helper mirroring init_kdc.sh pattern
- Log resolved namespaces at startup
- Improve PROLE_KDC_MASTER_PASSWORD error with actionable resolution steps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 21:34:41 -07:00
chrisfu
83938eba51 fix: use correct KDC pod label selector (app=auth not app=prole-auth)
The auth deployment in knoe-system uses label app=auth; init_knoe_users.sh
was searching for app=prole-auth causing the pod lookup to always fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:56:53 -07:00
chrisfu
872d715864 feat: Kerberos identity management — PROLE.LOCAL realm, cross-realm trust, user provisioning
Kerberos is now the single source of truth for all knoe-system accounts.

In-cluster KDC (PROLE.LOCAL):
- Rename in-cluster KDC realm PROLE.ORG → PROLE.LOCAL to avoid collision
  with the myrddin.prole.org Samba AD DC which owns PROLE.ORG
- Add [capaths] cross-realm trust block: PROLE.ORG (Samba AD) ↔ PROLE.LOCAL
- Create admin@PROLE.LOCAL on KDC startup (master password) for UI login
- Create guest@PROLE.LOCAL on KDC startup (read-only, PROLE_KDC_GUEST_PASSWORD)
- Update prole-kdc-secrets.example.yaml with trust_shared_password and guest_password
- Update prole-auth-kerberos ConfigMap realm/servicePrincipal to PROLE.LOCAL
- Add PROLE_AUTH_ADMIN_PRINCIPALS and PROLE_KDC_GUEST_PASSWORD to prole-auth Deployment

Database (knoe-db — k3s and GKE):
- Add spec.managed.roles: admin (superuser), guest (login), developer (group)
- Enable GSS pg_hba rules for both PROLE.ORG and PROLE.LOCAL realms
- Add krb_server_keyfile parameter for postgres service principal keytab
- Mount knoe-db-pg-keytab Secret via spec.additionalVolumes
- Add demo schema in postInitSQL with guest read-only grants

prole-auth (Spring Boot):
- SessionUser: add List<String> groups field
- AuthProperties: add adminPrincipals list (env: PROLE_AUTH_ADMIN_PRINCIPALS)
- LoginController: stamp groups=["admin"] for configured admin principals at login
- SessionTokenService: carry groups through TokenPayload; @JsonIgnoreProperties
  for backward compat with existing 2-field session cookies
- VerifyController: emit X-Prole-Groups header on /auth/verify
- grafana-proxy nginx: strip inbound X-Prole-Groups; capture and forward from auth

Services:
- ArgoCD (k3s + GKE): add g, admin, role:admin to argocd-rbac-cm policy.csv

etc/init_knoe_users.sh (new):
- Orchestrates full user provisioning: KDC principals, postgres keytab export,
  CNPG managed.roles patch, demo schema SQL, ArgoCD RBAC patch,
  Gitea and GitLab admin promotion via API
- Actions: initialize | status | cleanup
- Idempotent; sources prole_cfg.sh; follows existing init_*.sh style

Cross-realm trust activation (myrddin side):
  samba-tool user add krbtgt_PROLELOCAL --random-password
  samba-tool user setpassword krbtgt_PROLELOCAL --newpassword=<trust_shared_password>
  samba-tool spn add "krbtgt/PROLE.LOCAL" krbtgt_PROLELOCAL -U administrator
  samba-tool user setexpiry krbtgt_PROLELOCAL --noexpiry

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-01 20:38:57 -07:00