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
Introduces a single-source-of-truth ownership model using OCI referrers: - api: add OwnershipClaim proto + MarshalReferrer/UnmarshalReferrer - api/core: add OwnershipClaimReferrerType constant and CEL whitelist entry - server/store/oci: add OwnershipClaimArtifactMediaType mapping - server/database: add owners table (Owner model, AddOwner, RemoveOwners, WithOwners filter) - server/controller: enforce caller SPIFFE ID == owner_id; index claim on push - reconciler: add ownership task to walk referrers and sync owners table - cli: add dirctl ownership claim command and --owner search filter - cli/daemon: register ownership reconciler defaults - tests/e2e: add ownership-based search test suite (7 cases, all passing) Signed-off-by: Vivek Krishna Choppa <vivekkrishnachoppa@gmail.com>
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
Summary
agntcy.dir.ownership.v1.Claim)ownersDB table as a derived search index synced from OCI referrersdirctl ownership claimcommand and--ownersearch filterChanges
Proto / API
proto/agntcy/dir/ownership/v1/claim.proto: newClaimmessage (owner_id,claimed_at)api/ownership/v1/claim.go:MarshalReferrer/UnmarshalReferrer+ReferrerType()api/core/v1/referrer_types.go:OwnershipClaimReferrerType = "agntcy.dir.ownership.v1.Claim"proto/agntcy/dir/core/v1/rules.proto: CEL whitelist extended with ownership typeproto/agntcy/dir/search/v1/record_query.proto:RECORD_QUERY_TYPE_OWNER = 17Server
server/store/oci/types.go:OwnershipClaimArtifactMediaType+ OCI type mappingserver/database/gorm/record.go:Ownermodel,AddOwner,RemoveOwners, ownership JOIN filterserver/database/gorm/gorm.go:Ownertable migrationserver/types/database.go:OwnershipDatabaseAPIinterfaceserver/types/search.go:Owners []stringfield +WithOwners()filter optionserver/database/utils/utils.go:RECORD_QUERY_TYPE_OWNER→WithOwners()server/controller/store.go: ownership claim enforcement + immediate DB indexing on pushReconciler
reconciler/tasks/ownership/: new task that walks OCI referrers and syncs owners tablereconciler/config/config.go:Ownershipconfig field + env var bindingsreconciler/service/service.go: ownership task registrationCLI
cli/cmd/ownership/ownership.go:dirctl ownership claim --record <CID> --owner <id>cli/cmd/root.go: ownership command registeredcli/cmd/search/filters.go:--ownerflag +RECORD_QUERY_TYPE_OWNERquerycli/cmd/daemon/config.go: ownership reconciler defaults (enabled=true,interval=5m)Tests
tests/e2e/local/13_ownership_search_test.go: 7 e2e tests (all passing) covering exact match, wildcards, combined filters, and negative casesTest plan
go test ./...in server, reconciler, api, cli modules)task lint)🤖 Generated with Claude Code