feat(dir): add ownership claim referrer, search, and CLI (v1) - #1478
feat(dir): add ownership claim referrer, search, and CLI (v1)#1478vivekkrishna wants to merge 2 commits into
Conversation
9cca9e0 to
21fdd72
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
21fdd72 to
330f7ad
Compare
516e1e8 to
f01858e
Compare
|
@ramizpolic Any comments on this? |
|
Hey @vivekkrishna, thanks for the PR. It looks good from my end, the only issue is that we are working on simplifying the interfaces and API surface in this PR #1500, and I am not sure which order to take (merge that PR, adjust this, or something else). I think that work will be done soon (end of next week). Can we hold this PR open until then and do a rebase together to merge this PR? Alternatively, I think you don't even need any code changes for your functionality since you can already push whichever referrer you want and it should work out of box. Code snippet below dir/server/store/oci/referrers.go Lines 62 to 75 in 0c58515 The only issue would be that the CLI is not available, but this can be handled through a separate CLI for your needs built with DIR Go SDK. Lets sync tomorrow/when it works for you on Slack and we can decide next steps together. |
|
Sure @ramizpolic , I will sync up over slack. |
|
Hi @ramizpolic . After analysis, merging as-is makes sense for these reasons. The generic PushReferrer path (referrers.go:72-74) just stores a blob in OCI with no DB indexing — no owners table, no --owner search, and no foundation for manager subtree search. #1478's owners table + eager indexing + reconciler is exactly what makes ownership searchable and is the prerequisite for manager search (#1468). On the #1500 question: planning to land #1478 on v1 PushReferrer as-is, then rebuild ownership onto the v2 ObjectStore + ObjectReferrer path once #1500 merges — does that sequencing work? Only immediate blocker is Target() added to StoreAPI in #1500 will break #1478's compile, so I'll add that method to the v1 OCI store when rebasing. Happy to hold if you'd rather do it right in v2 from the start — just didn't want to block manager search on #1500's timeline. |
| var ownershipClaim *ownershipv1.Claim | ||
|
|
||
| if request.GetType() == corev1.OwnershipClaimReferrerType { | ||
| claim := &ownershipv1.Claim{} | ||
| if err := claim.UnmarshalReferrer(&corev1.RecordReferrer{Data: request.GetData()}); err != nil { | ||
| errMsg := fmt.Sprintf("failed to decode ownership claim: %v", err) | ||
|
|
There was a problem hiding this comment.
this ownership claim is currently unverifiable since the following things can happen:
- user has write access to the OCI store (either directly or via sync)
- user writes an ownership claim to a non-owned resource through OCI referrers (either directly or through its own OCI that gets synced to a targeted node)
- target node indexes ownership through the reconciler
- the resource is reported as owned by someone who actually doesnt own it
this behavior enables targeted attacks across the network since the data has 2 access points:
- unsafe route through OCI that doesnt get verified in the reconciler
- secure route through the controller that does get verified
my concern here is that the ownership claims are not verifiable. we already support verifiable claims through a public key and keyless OIDC signing (dirctl sign) via COSIGN which gets verified and indexed into the database. I am not sure if we currently have a mechanism to search over this data, but it is present and may only need query params to be exposed on the client-side. perhaps @paralta can assist on this question
i think a secure/verifiable way to enable this functionality is to add support for SPIFFE-based signing. this would provide all the things you need, and i would happily merge it. it would also be a super important feature for us, not just for local use-case, but across the (insecure) network.
could you please adjust the implementation according to the notes above and we can merge it right away
There was a problem hiding this comment.
Thanks for the input @ramizpolic , addressed with new commit.
f01858e to
6f92157
Compare
Addresses ramizpolic's security comment on PR agntcy#1478. Without signing, any caller with OCI write access could forge ownership claims; the reconciler would index them blindly. Changes: - proto: add signature (bytes) and certificate (bytes) fields to Claim - api/ownership/v1/sign.go: SignClaim, VerifyClaim, IsSigned; supports ECDSA (P256/P384/P521) and RSA; canonical payload SHA-256(owner_id:claimed_at) - cli: --key / --cert flags on ownership claim; identity mismatch caught client-side before any network call - reconciler: TrustedCACertFile config; verifyClaim() gate before db.AddOwner(); unsigned claims accepted with warning in dev mode (no SPIFFE configured) - server/controller: indexOwnershipClaim() helper verifies signed claims before eager db.AddOwner() so tampered claims are rejected at push time - tests: GenerateSpiffeTestCert helper (pure crypto/x509, no SPIRE needed); 14_ownership_signing_test.go (happy path, identity mismatch, idempotent re-push); 5 unit tests covering round-trip, tamper detection, and unsigned-claim rejection
Addresses ramizpolic's security comment on PR agntcy#1478. Without signing, any caller with OCI write access could forge ownership claims; the reconciler would index them blindly. Changes: - proto: add signature (bytes) and certificate (bytes) fields to Claim - api/ownership/v1/sign.go: SignClaim, VerifyClaim, IsSigned; supports ECDSA (P256/P384/P521) and RSA; canonical payload SHA-256(owner_id:claimed_at) - cli: --key / --cert flags on ownership claim; identity mismatch caught client-side before any network call - reconciler: TrustedCACertFile config; verifyClaim() gate before db.AddOwner(); unsigned claims accepted with warning in dev mode (no SPIFFE configured) - server/controller: indexOwnershipClaim() helper verifies signed claims before eager db.AddOwner() so tampered claims are rejected at push time - tests: GenerateSpiffeTestCert helper (pure crypto/x509, no SPIRE needed); 14_ownership_signing_test.go (happy path, identity mismatch, idempotent re-push); 5 unit tests covering round-trip, tamper detection, and unsigned-claim rejection Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
fca09a8 to
e03f587
Compare
Addresses ramizpolic's security comment on PR agntcy#1478. Without signing, any caller with OCI write access could forge ownership claims; the reconciler would index them blindly. Changes: - proto: add signature (bytes) and certificate (bytes) fields to Claim - api/ownership/v1/sign.go: SignClaim, VerifyClaim, IsSigned; supports ECDSA (P256/P384/P521) and RSA; canonical payload SHA-256(owner_id:claimed_at) - cli: --key / --cert flags on ownership claim; identity mismatch caught client-side before any network call - reconciler: TrustedCACertFile config; verifyClaim() gate before db.AddOwner(); unsigned claims accepted with warning in dev mode (no SPIFFE configured) - server/controller: indexOwnershipClaim() helper verifies signed claims before eager db.AddOwner() so tampered claims are rejected at push time - tests: GenerateSpiffeTestCert helper (pure crypto/x509, no SPIRE needed); 14_ownership_signing_test.go (happy path, identity mismatch, idempotent re-push); 5 unit tests covering round-trip, tamper detection, and unsigned-claim rejection Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
e03f587 to
a1d7ba7
Compare
|
should we add this 1.5 milestone? |
Addresses ramizpolic's security comment on PR agntcy#1478. Without signing, any caller with OCI write access could forge ownership claims; the reconciler would index them blindly. Changes: - proto: add signature (bytes) and certificate (bytes) fields to Claim - api/ownership/v1/sign.go: SignClaim, VerifyClaim, IsSigned; supports ECDSA (P256/P384/P521) and RSA; canonical payload SHA-256(owner_id:claimed_at) - cli: --key / --cert flags on ownership claim; identity mismatch caught client-side before any network call - reconciler: TrustedCACertFile config; verifyClaim() gate before db.AddOwner(); unsigned claims accepted with warning in dev mode (no SPIFFE configured) - server/controller: indexOwnershipClaim() helper verifies signed claims before eager db.AddOwner() so tampered claims are rejected at push time - tests: GenerateSpiffeTestCert helper (pure crypto/x509, no SPIRE needed); 14_ownership_signing_test.go (happy path, identity mismatch, idempotent re-push); 5 unit tests covering round-trip, tamper detection, and unsigned-claim rejection Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
a1d7ba7 to
67cc5b8
Compare
|
Hi @vivekkrishna, we will include this feature in v1.6, but we will need to make it part of the standard |
|
Hi @ramizpolic , that works for me. Can you let me know the planned timeline for v1.6 release? Also see if you can add me or tag me in new PR for review or modify this same PR?. Also can you confirm search filter by --owner is still preserved after the signature approach, that is crucial for overall epic? |
67cc5b8 to
47fdcff
Compare
Addresses ramizpolic's security comment on PR agntcy#1478. Without signing, any caller with OCI write access could forge ownership claims; the reconciler would index them blindly. Changes: - proto: add signature (bytes) and certificate (bytes) fields to Claim - api/ownership/v1/sign.go: SignClaim, VerifyClaim, IsSigned; supports ECDSA (P256/P384/P521) and RSA; canonical payload SHA-256(owner_id:claimed_at) - cli: --key / --cert flags on ownership claim; identity mismatch caught client-side before any network call - reconciler: TrustedCACertFile config; verifyClaim() gate before db.AddOwner(); unsigned claims accepted with warning in dev mode (no SPIFFE configured) - server/controller: indexOwnershipClaim() helper verifies signed claims before eager db.AddOwner() so tampered claims are rejected at push time - tests: GenerateSpiffeTestCert helper (pure crypto/x509, no SPIRE needed); 14_ownership_signing_test.go (happy path, identity mismatch, idempotent re-push); 5 unit tests covering round-trip, tamper detection, and unsigned-claim rejection Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
|
@ramizpolic Can you check if these updates look good to approve based on recent comments with signature? |
75cd280 to
caa182a
Compare
3eed9f0 to
dca5214
Compare
fbf9b7b to
2b8aacb
Compare
…rrer Implements ownership claims as part of the standard api/sign/v1/Signature referrer (content_type = "agntcy.dir.ownership.v1") rather than a bespoke proto type, following reviewer feedback from ramizpolic. Changes: - Add api/ownership/v1/sign.go: NewClaim, SignClaim, VerifyClaim, IsOwnershipClaim, GetOwnerID helpers operating on *signv1.Signature; SPIFFE/X.509 SVID signing with ECDSA/RSA support; canonical payload SHA-256(owner_id + ":" + signed_at) - Add ownership reconciler task (reconciler/tasks/ownership/): walks SignatureReferrerType referrers, filters by content_type, verifies SPIFFE cert chain and signature, indexes owners into DB - Add server/database/gorm/owners.go: Owner model (record_cid, owner_id, claimed_at) with AddOwner/RemoveOwners; migrate via AutoMigrate - Add OwnershipDatabaseAPI to server/types/database.go and DatabaseAPI composite - Add owner search filter (RECORD_QUERY_TYPE_OWNER = 20, WithOwners/WithoutOwners) wired through proto, pb.go, DB utils, and search types, including --exclude-owner to match the repo's include/exclude filter invariant - Add server/ingest/ingest.go: eager ownership index on Signature referrer push so search reflects new owner immediately without waiting for reconciler - Add CLI subcommand: dirctl ownership claim [--key/--cert] <record-cid> <owner-id> - Add e2e tests: 13_ownership_search_test.go (unsigned claims, wildcard owner search), 14_ownership_signing_test.go (signed SPIFFE claims, reconciler re-index, identity mismatch rejection) - Bump buf.lock for protovalidate to resolve a stale dependency lock that failed CI's "task gen" clean-tree check Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
2b8aacb to
f21d694
Compare
Summary
Implements SPIFFE-based record ownership using the existing `api/sign/v1/Signature` referrer type — as requested in the review: "make it part of the standard Signature field instead of adding new claims object."
Ownership claims are now pushed as `SignatureReferrerType` referrers with `content_type = "agntcy.dir.ownership.v1"` and `annotations["owner_id"] = `. No new proto message is needed.
Design
Unsigned claims (no `--key`/`--cert`) are accepted in dev environments; the reconciler logs a warning and indexes them without verification.
Changes
API
Search
Database
Ingestor
Reconciler
CLI
Test plan
Generated with Claude Code