📝 Description
A sidecar lane can now terminate the client's login and log in upstream with a credential the client never holds. The lane names a verifier for the client leg (identity: none on a unix socket, a Kubernetes projected token, a control plane credential, a SPIFFE JWT-SVID), a pool of upstream credentials drawn from process-wide sources (secret_files, gcp_impersonation, x509, spiffe, kerberos) and the rules that pick one from the identity and the lane context. The relay runs a per-protocol login driver before the pumps (Postgres, MySQL, MongoDB, SQL Server; Oracle relays pass-through users only), ClickHouse swaps the Hello in its codec, HTTP and gRPC swap the authorization per request. A refused login is audited as a one-event session under the claimed user before any upstream byte.
The control plane learns the person: a sidecar row carries owner_email (written by the GitOps reconciler for Kumo pods), reviews are filed under the sidecar's principal or the owner instead of a placeholder, and the requester is DMed on Slack. Users mint lane credentials after SSO when a grant names them or their group; the plane serves the hashes and SCRAM material to that sidecar as credential_set, which the heartbeat swaps in place, so a mint or a revocation is in force at the next login with no restart.
Every new configuration key is cap:-gated: the plane never serves one to a build that did not declare it, and a lane with none of them is byte-identical to today.
Design: sidecar/README.md "Client identity and upstream credentials"; reconciler contract in deploy/helm-chart/chart/sidecar/RECONCILER.md.
Depends on hoophq/libhoop#140 (login toolkits). This branch pins its branch commit a59ec27; merge #140 first, then re-pin the squash commit here.
📣 User-facing impact
A sidecar can give a person access to a database or an API without handing them its credential: they log in with a key hoop issued, the sidecar logs in upstream for them, and the session, the hold and the Slack message name the person.
🔗 Related Issue
Fixes #
🚀 Type of Change
- 🐛 Bug fix (non-breaking change which fixes an issue)
- ✨ New feature (non-breaking change which adds functionality)
- 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- 📚 Documentation update
- 🎨 Style/UI update
- ♻️ Code refactor
- ⚡ Performance improvement
- ✅ Test update
- 🔧 Build configuration change
- 🧹 Chore
📋 Changes Made
sidecar/proxy:LoginFunc/RegisterLoginhook andConfig.Authenticate; the relay adopts the verified identity beforesession_start, audits refusals as one-event sessions, maps Postgres cancel requests through a registry. Drivers:login_postgres.go(cleartext on unix/TLS or SCRAM-SHA-256 server; SCRAM, MD5, cleartext, cert or GSS upstream),login_mysql.go(on the generalized RSA auth bridge),login_mongodb.go(hello answered locally, SCRAM server, SCRAM or X.509 upstream),login_mssql.go(login-only TLS terminated, LOGIN7 rewritten or SSPI via the Kerberos source; TDS 8 strict refused with the setting named),login_oracle.go(pass-through users relayed, every other login refused with ORA-01017: O5LOGON never carries the client's secret, the verifier shape a follow-up needs is on the refusal).sidecar/daemon:identity,credentials(sources, pool,selectrules or an OPAcredentialfield),context,passthrough_users,propagate;credential_setswapped on every heartbeat; verifiersnone,kubernetes(OIDC discovery + JWKS,aud/exp/iss, pod UID pin),hoop_credential(constant-time hash match, SCRAM lookup); sourcessecret_files,gcp_impersonation,x509; registries for nested-module verifiers and sources; capabilities header entries; validator matrix; a built-in guardrail on pooled SQL lanes refusingSET ROLE,RESET ROLE,SET SESSION AUTHORIZATION,EXECUTE AS,REVERT;HOOP_SIDECAR_IDENTITY_TYPE=spiffefor the plane leg; schema regenerated; README sections.sidecar/codec/http: per-requestSwapofAuthorization(bearer or basic) andImpersonate-User/-Groupfrom the identity; client-sentImpersonate-*always dropped on a swap lane.sidecar/codec/clickhouse: the Hello swap and the login identity to the gate.sidecar/daemon/grpc.go: per-RPC authorization through libhoop'sUpstreamAuthorizationhook.sidecar/identity/spiffe,sidecar/identity/krb5: nested modules (go-spiffe, gokrb5), linked intohoop-inspectonly, added togo.work.- gateway: migration
000145sidecars.owner_email; review bodyprincipal;newSidecarReviewresolves principal → owner → placeholder and the requester's linked Slack id; DM on hold, approval, denial; owner on anonymous sidecar sessions; flagexperimental.sidecar_review_owner(default on). Migrations000146(connection_credentials.scram_sha256) and000147(sidecar_lane_grants); grants CRUD, mint/list/revoke routes, SCRAM-SHA-256 material at mint,credential_setcomposed per sidecar; analytics events; OpenAPI regenerated;hoop sidecar credential <sidecar>/<lane>. webapp_v2: owner field on the sidecar settings page.- deploy: sidecar chart
controlPlane.identity: spiffe(Workload API socket via CSI or hostPath),credentials.secretMounts,kerberos.ccache, downward APIPOD_UID/POD_NAME/POD_NAMESPACE;RECONCILER.md; envoy-stackcredentials/overlay with a fake IAM credentials endpoint;sidecar/scripts/dev/credentials-check.sh. - libhoop pinned to
a59ec27(interim).
🧪 Testing
Test Configuration:
- Browser(s): n/a
- OS: macOS 15 (arm64), Go 1.26,
CGO_ENABLED=0
Tests performed:
-
Unit tests pass
-
Integration tests pass
-
Manual testing completed
-
sidecarroot module and every nested module (cmd,config/yaml,pii/alcatraz,store/sqlite,analyzer/vertex,descriptors/gcs,mcp,ica,lexer/conformance,codec/wasm,identity/spiffe,identity/krb5):go test ./...green.go mod tidyleavessidecar/go.modunchanged (one-dependency invariant). -
gateway:
models,services,api/sidecar,api/sidecarcredentials,api/review,slack,transport/plugins/slackgreen when run one package at a time (the PGlite suites time out when run concurrently on one machine; CI shards them);go vet ./...clean. -
webapp_v2:npm run lint0 errors,npm run buildok. -
Fuzz, committed seed corpora, no crashers:
FuzzLogin(pgwire),FuzzMySQLLogin,FuzzMongoLogin,FuzzMSSQLLogin,FuzzOracleLogininsidecar/proxy; the libhoop toolkits' own targets in #140. -
Manual: real
mongod:7+mongoshthroughproxy.Server(swap, wrong key, pass-through, monitor connections, OP_QUERY and OP_MSG handshakes); realgo-mssqldblogin through the relay;hoop-inspect -validateacceptsidentity: {type: none}on unix with asecret_filespool and refuses the same on tcp;helm lintandhelm templatefor default,kubernetes,spiffe,spiffe+hostPath, and mounts value sets. -
Not run here:
make test-oss,make test-sidecar-compat,make test-sidecar-e2e(CI).
📸 Screenshots (if applicable)
✅ Checklist
- My code follows the style guidelines of this project
- I have performed a self-review of my own code
- I have commented my code, particularly in hard-to-understand areas
- My changes generate no new warnings
- New and existing unit tests pass locally with my changes
- I have checked my code and corrected any misspellings
📄 Additional Notes
- Feature flag:
experimental.sidecar_review_ownergates the gateway behaviour (reviews under the person, Slack DM); off restores the placeholder. The sidecar keys need no flag: they arecap:-gated and absent keys mean today's behaviour. - Gateway ↔ agent contract untouched. Sidecar ↔ plane: additive keys only, all with
cap:;test-sidecar-compatis the check to watch. - Oracle ships pass-through only; MSSQL integrated (Kerberos) logins relay only when
passthrough_userslistsintegrated; MongoDB upstream SCRAM authenticates onadmin. - Commits are unsigned; the author signs before marking ready.
- Review focus:
sidecar/proxy/login.go(the hook and the adopt/start order),sidecar/daemon/select.go(verify → select → source),gateway/api/sidecar/reviews.go(person resolution),sidecar/codec/http/credential.go(header swap andImpersonate-*stripping).
Activity
43✅ Build Completed with Success, Version=2047.0.0-g25c9843
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/a21b69cb09fa22c21a2d0621ac8181c6f06abca8
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/bead962c5278fe7955467822049278184fe351e1
✅ Build Completed with Success, Version=2047.0.0-gbead962
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/ee0731ddd960dec4aad4c472865a46b385445004
This pull request is a draft
Merging is unavailable until it is marked ready for review.
Code review by qodo was updated up to the latest commit https://github.com/hoophq/hoop/commit/25c9843f71e2a2a5d6af62ad69710ef9fa9f44cf