Repository navigation
test(qdrant): exercise the tenant trust anchor end to end - #4701
Conversation
The fixture left TLS unset with external off, so the chart rendered no CA and no TenantProjection, and no e2e run walked the chain from the sentinel through the CA-extraction controller to the tenantsecrets read path. Turn TLS on explicitly, without allocating a LoadBalancer, and check both ends: the projection is key-free, carries the chart's CA and is served through tenantsecrets while the key-bearing Secrets are not; and a client trusting only that anchor verifies the listener, while a wrong anchor, no anchor and plaintext HTTP all fail. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: cozystack/cozystack/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe Qdrant e2e fixture now enables TLS. New checks validate tenant anchor publication and test which trust sources can verify the Qdrant TLS endpoint. ChangesQdrant trust-anchor verification
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Other Merge Risk: ⚪ Minimal · up to This change adds end-to-end coverage for Qdrant's tenant trust anchor by enabling TLS in the test fixture. Production code is unchanged, and no defects were identified. It is ready to merge. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The changes strengthen trust checks without changing production templates or enabling external exposure. No introduced vulnerability was established. Tenant-identity enforcement and certificate cleanup after interrupted runs remain unverified. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
IvanHunters
left a comment
There was a problem hiding this comment.
LGTM
Test-only, nothing under production code moves: both files live in hack/e2e-chainsaw/qdrant/. The new coverage actually walks the tenant trust-anchor path instead of asserting something trivial, and every name it pins lines up with the chart and the cacert controller.
What I checked against the source:
- Fixture tls.enabled is a real field (values.schema.json and the generated qdrant-rd CRD); the release prefix makes the release name qdrant-test.
- certmanager.yaml renders CA secret qdrant-test-ca and serving secret qdrant-test-tls, and the serving cert has CN qdrant-test with qdrant-test in its SAN list, so -servername/-verify_hostname qdrant-test matches. No client cert is required, so the server-auth-only handshake the test performs is right.
- tenant-projection.yaml names the TenantProjection qdrant-test-ca, which is exactly what the first step asserts.
- cacert/reconciler.go publishes .tenant-ca as an Opaque, key-free Secret labeled internal.cozystack.io/tenant-ca=true, with condition Ready/Projected.
- The qdrant-rd cozyrd spec.secrets selects only the tenant-ca label plus the apikey Secret by name, so the key-bearing qdrant-test-ca and qdrant-test-tls are correctly not served through tenantsecrets, which is the negative the test leans on.
- The chart sets service.enable_tls on 6333/6334, so the plaintext probe expecting no HTTP response (http_code 000) holds up.
The negated checks are the opposite of inert. Each one separates "the thing I wanted to fail failed" from "something else failed": verify error versus a refused connection or a hang, NotFound versus an RBAC denial or an unavailable apiserver, curl 000 versus exit 7/28. Every timeout is bounded, and the existing steps are untouched, so no coverage quietly goes away.
[NIT] hack/e2e-chainsaw/qdrant/chainsaw-test.yaml:256 the port-forward readiness gate (nc -z localhost 6333) only proves the local socket is open, not that kubectl finished dialing the backend, so the first positive handshake can fail on the connection rather than on verification and then report a trust failure that isn't real. You already called this out in the TODO(e2e-replace-fixed-timeouts) comment, and it matches what the bucket test does, so the flake risk is low and nothing needs to change.
What this PR does
The qdrant e2e suite now runs with TLS on and checks the tenant trust anchor end to end. Before this, the fixture left
tlsunset withexternal: false, so the chart rendered no CA and noTenantProjection, and nothing in e2e reached the controller ortenantsecrets. Settingtls.enabled: trueturns TLS on without the LoadBalancer thatexternal: truewould allocate.The first new step checks that the anchor is published. The
qdrant-test-casentinel has to reportReady=True/Projected, andqdrant-test.tenant-cahas to carry both thetenant-caandtenantresource=truelabels. The projection must hold onlyca.crtwith the same fingerprint as the chart's CA, andtenantsecretsmust serve it. The key-bearingqdrant-test-caandqdrant-test-tlsmust answer NotFound there, and any other error fails the step.The second step checks that the anchor is what decides trust. It reads the anchor through
tenantsecrets, port-forwards the qdrant Service and runsopenssl s_clientwith that file as the only CA, hostname check included. A CA with the same subject and another key must fail, and so must the runner's default trust store. A handshake failure counts only when the output shows a verification error, so a refused connection does not pass as a rejection. Plaintext HTTP must get no response. Curl exits 7 and 28 fail the step, and a second verified handshake afterwards proves the forward was still alive.TLS does not break the existing steps, because none of them talk to qdrant.
create-qdrant,wait-helmreleases-ready,verify-statefulset-and-storageandverify-pvc-reclaim-on-deleteonly read Kubernetes objects: nohttp://, no port 6333 or 6334, no curl or exec, and the suite has no backup round trip. The release now also renders two Issuers, two Certificates and the sentinel. The chart switches its probes to HTTPS withenable_tls, soreadyReplicas: 1now covers a pod serving TLS.Local checks:
chainsaw lint test(v0.2.15) says the file is valid.helm templatewith the fixture's spec renders 2 Certificates, 2 Issuers and 1 TenantProjection, and none without TLS. I ran the handshake logic against a localopenssl s_serverwith a CA and leaf shaped like the chart's. The anchor passes, the wrong CA and the default store fail on verification, a wrong hostname fails, plaintext gets000, and a closed port is not counted as a rejection.A sentinel with a missing source (
SourceNotFound) is not tested here, the reconciler unit tests cover it.Review notes I left as they are:
kubectl port-forwardthe local socket is always accepted, so curl exits 7 and 28 only catch a dead forward process. A false pass would need qdrant to answer plaintext and a pod-side blip on that one request at the same time.lost connection to podthere, the plaintext check should get its own port-forward.catchhas nodescribeof theTenantProjection. The sentinel assert prints its conditions when it fails, but a red Secret assert or script after it would not.servicemonitor.yamlhardcodesscheme: http, so metrics scraping fails for any qdrant with TLS on. That was already the case before this PR, and the suite does not check metrics. Tracked in qdrant: the ServiceMonitor scrapes http even when TLS is on #4703.cert-manager does not set owner references here, so
qdrant-test-caandqdrant-test-tlsstay in the namespace with their private keys after the app is deleted. That is tracked in #4704. The nats half of #3706 is tracked in #4702.Closes #3706
Screenshots
No UI change.
Downstream repositories
The diff touches only
hack/e2e-chainsaw/qdrant/, which no entry in the trigger map covers.Release note