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>
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>
- 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>
- 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>
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>
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>
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>
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>
- 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>
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>
- 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.
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.
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>