From 1dba0ae4c334df67b4ae4a373e9c430fb55c50b6 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 6 Oct 2025 23:04:36 +0100 Subject: [PATCH 01/51] drafted x509 cache hint for registration entries Signed-off-by: Valentin Fadeev --- pkg/agent/manager/sync.go | 4 +- pkg/server/datastore/sqlstore/migration.go | 13 +- pkg/server/datastore/sqlstore/models.go | 4 + pkg/server/datastore/sqlstore/sqlstore.go | 177 +++++++++++------- .../datastore/sqlstore/sqlstore_test.go | 75 +++++--- .../datastore/sqlstore/testdata/entries.json | 27 ++- proto/spire/common/common.pb.go | 166 +++++++++++----- proto/spire/common/common.proto | 9 + 8 files changed, 327 insertions(+), 148 deletions(-) diff --git a/pkg/agent/manager/sync.go b/pkg/agent/manager/sync.go index 84ede9ab08..59c2b2c23b 100644 --- a/pkg/agent/manager/sync.go +++ b/pkg/agent/manager/sync.go @@ -345,7 +345,9 @@ func (m *manager) fetchEntries(ctx context.Context) (_ *cache.UpdateEntries, _ * case entry.StoreSvid: storeEntries[entryID] = entry default: - cacheEntries[entryID] = entry + if !entry.X509SvidCacheHint.GetJwtOnly() { + cacheEntries[entryID] = entry + } } } diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 0fbde9a7e3..88d5e66c91 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -267,12 +267,12 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 23 + latestSchemaVersion = 24 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 23 + lastMinorReleaseSchemaVersion = 24 ) // the current code version @@ -498,6 +498,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { //nolint: gocritic,revive // No upgrade required yet, keeping switch for future additions + case 24: + err = migrateToV24(tx) default: err = newSQLError("no migration support for unknown schema version %d", currVersion) } @@ -508,6 +510,13 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi return nextVersion, nil } +func migrateToV24(tx *gorm.DB) error { + if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { + return newWrappedSQLError(err) + } + return nil +} + func addFederatedRegistrationEntriesRegisteredEntryIDIndex(tx *gorm.DB) error { // GORM creates the federated_registration_entries implicitly with a primary // key tuple (bundle_id, registered_entry_id). Unfortunately, MySQL5 does diff --git a/pkg/server/datastore/sqlstore/models.go b/pkg/server/datastore/sqlstore/models.go index c66f7e7436..073192c10c 100644 --- a/pkg/server/datastore/sqlstore/models.go +++ b/pkg/server/datastore/sqlstore/models.go @@ -112,6 +112,10 @@ type RegisteredEntry struct { // TTL of JWT identities derived from this entry JWTSvidTTL int32 `gorm:"column:jwt_svid_ttl"` + + // X509SvidCacheHint contains a set of options and flags that inform the + // agent behaviour with respect to pre-fetching and refreshing X509 SVIDs + X509SvidCacheHint []byte `gorm:"size:255,column:x509_svid_cache_hint"` // corresponds to TINYBLOB in MySQL } // RegisteredEntryEvent holds the entry id of a registered entry that had an event diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index f8e54f0ae0..d3ea58ad5a 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -2504,17 +2504,23 @@ func createRegistrationEntry(tx *gorm.DB, entry *common.RegistrationEntry) (*com return nil, err } + X509SvidCacheHint, err := proto.Marshal(entry.X509SvidCacheHint) + if err != nil { + return nil, err + } + newRegisteredEntry := RegisteredEntry{ - EntryID: entryID, - SpiffeID: entry.SpiffeId, - ParentID: entry.ParentId, - TTL: entry.X509SvidTtl, - Admin: entry.Admin, - Downstream: entry.Downstream, - Expiry: entry.EntryExpiry, - StoreSvid: entry.StoreSvid, - JWTSvidTTL: entry.JwtSvidTtl, - Hint: entry.Hint, + EntryID: entryID, + SpiffeID: entry.SpiffeId, + ParentID: entry.ParentId, + TTL: entry.X509SvidTtl, + Admin: entry.Admin, + Downstream: entry.Downstream, + Expiry: entry.EntryExpiry, + StoreSvid: entry.StoreSvid, + JWTSvidTTL: entry.JwtSvidTtl, + Hint: entry.Hint, + X509SvidCacheHint: X509SvidCacheHint, } if err := tx.Create(&newRegisteredEntry).Error; err != nil { @@ -2629,7 +2635,8 @@ SELECT NULL AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2637,7 +2644,7 @@ WHERE id IN (SELECT id FROM listing) UNION SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -2650,7 +2657,7 @@ WHERE UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names WHERE registered_entry_id IN (SELECT id FROM listing) @@ -2658,7 +2665,7 @@ WHERE registered_entry_id IN (SELECT id FROM listing) UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors WHERE registered_entry_id IN (SELECT id FROM listing) @@ -2693,7 +2700,8 @@ SELECT NULL ::integer AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2701,7 +2709,7 @@ WHERE id IN (SELECT id FROM listing) UNION SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -2714,7 +2722,7 @@ WHERE UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names WHERE registered_entry_id IN (SELECT id FROM listing) @@ -2722,7 +2730,7 @@ WHERE registered_entry_id IN (SELECT id FROM listing) UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors WHERE registered_entry_id IN (SELECT id FROM listing) @@ -2753,7 +2761,8 @@ SELECT D.id AS dns_name_id, D.value AS dns_name, E.revision_number, - E.jwt_svid_ttl AS reg_jwt_svid_ttl + E.jwt_svid_ttl AS reg_jwt_svid_ttl, + E.x509_svid_cache_hint AS x509_svid_cache_hint FROM registered_entries E LEFT JOIN @@ -2795,7 +2804,8 @@ SELECT NULL AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2803,7 +2813,7 @@ WHERE id IN (SELECT id FROM listing) UNION SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -2816,7 +2826,7 @@ WHERE UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names WHERE registered_entry_id IN (SELECT id FROM listing) @@ -2824,7 +2834,7 @@ WHERE registered_entry_id IN (SELECT id FROM listing) UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors WHERE registered_entry_id IN (SELECT id FROM listing) @@ -3006,7 +3016,8 @@ SELECT NULL AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries `) @@ -3025,7 +3036,7 @@ FROM UNION SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -3040,7 +3051,7 @@ ON UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names `) @@ -3051,7 +3062,7 @@ FROM UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors `) @@ -3101,7 +3112,8 @@ SELECT NULL ::integer AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries `) @@ -3119,7 +3131,7 @@ FROM UNION ALL SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -3134,7 +3146,7 @@ ON UNION ALL SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names `) @@ -3145,7 +3157,7 @@ FROM UNION ALL SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors `) @@ -3194,7 +3206,8 @@ SELECT D.id AS dns_name_id, D.value AS dns_name, E.revision_number, - E.jwt_svid_ttl AS reg_jwt_svid_ttl + E.jwt_svid_ttl AS reg_jwt_svid_ttl, + E.x509_svid_cache_hint AS x509_svid_cache_hint FROM registered_entries E LEFT JOIN @@ -3268,7 +3281,8 @@ SELECT NULL AS dns_name_id, NULL AS dns_name, revision_number, - jwt_svid_ttl AS reg_jwt_svid_ttl + jwt_svid_ttl AS reg_jwt_svid_ttl, + x509_svid_cache_hint FROM registered_entries `) @@ -3286,7 +3300,7 @@ FROM UNION SELECT - F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL + F.registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, B.trust_domain, NULL, NULL, NULL, NULL, NULL FROM bundles B INNER JOIN @@ -3301,7 +3315,7 @@ ON UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, value, NULL, NULL, NULL FROM dns_names `) @@ -3312,7 +3326,7 @@ FROM UNION SELECT - registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL + registered_entry_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, id, type, value, NULL, NULL, NULL, NULL, NULL, NULL FROM selectors `) @@ -3804,25 +3818,26 @@ func fillNodeSelectorFromRow(nodeSelector *common.Selector, r *nodeSelectorRow) } type entryRow struct { - EId uint64 - EntryID sql.NullString - SpiffeID sql.NullString - ParentID sql.NullString - RegTTL sql.NullInt64 - Admin sql.NullBool - Downstream sql.NullBool - Expiry sql.NullInt64 - SelectorID sql.NullInt64 - SelectorType sql.NullString - SelectorValue sql.NullString - StoreSvid sql.NullBool - Hint sql.NullString - CreatedAt sql.NullTime - TrustDomain sql.NullString - DNSNameID sql.NullInt64 - DNSName sql.NullString - RevisionNumber sql.NullInt64 - RegJwtSvidTTL sql.NullInt64 + EId uint64 + EntryID sql.NullString + SpiffeID sql.NullString + ParentID sql.NullString + RegTTL sql.NullInt64 + Admin sql.NullBool + Downstream sql.NullBool + Expiry sql.NullInt64 + SelectorID sql.NullInt64 + SelectorType sql.NullString + SelectorValue sql.NullString + StoreSvid sql.NullBool + Hint sql.NullString + CreatedAt sql.NullTime + TrustDomain sql.NullString + DNSNameID sql.NullInt64 + DNSName sql.NullString + RevisionNumber sql.NullInt64 + RegJwtSvidTTL sql.NullInt64 + X509SvidCacheHint sql.Null[[]byte] } func scanEntryRow(rs *sql.Rows, r *entryRow) error { @@ -3846,6 +3861,7 @@ func scanEntryRow(rs *sql.Rows, r *entryRow) error { &r.DNSName, &r.RevisionNumber, &r.RegJwtSvidTTL, + &r.X509SvidCacheHint, )) } @@ -3908,6 +3924,11 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { entry.CreatedAt = roundedInSecondsUnix(r.CreatedAt.Time) } + entry.X509SvidCacheHint = &common.RegistrationEntry_X509SvidCacheHint{} + if err := proto.Unmarshal(r.X509SvidCacheHint.V, entry.X509SvidCacheHint); err != nil { + return newSQLError("invalid value for X.509 cache hint: %s", err) + } + return nil } @@ -4005,6 +4026,13 @@ func updateRegistrationEntry(tx *gorm.DB, e *common.RegistrationEntry, mask *com if mask == nil || mask.Hint { entry.Hint = e.Hint } + if mask == nil || mask.X509SvidCacheHint { + X509SvidCacheHint, err := proto.Marshal(e.X509SvidCacheHint) + if err != nil { + return nil, err + } + entry.X509SvidCacheHint = X509SvidCacheHint + } // Revision number is increased by 1 on every update call entry.RevisionNumber++ @@ -4458,6 +4486,9 @@ func validateRegistrationEntry(entry *common.RegistrationEntry) error { // it is done to avoid users to mix selectors from different platforms in // entries with storable SVIDs if entry.StoreSvid { + if entry.X509SvidCacheHint.GetJwtOnly() { + return newValidationError("specifying cache behaviour is incompatible with storable SVIDs") + } // Selectors must never be empty tpe := entry.Selectors[0].Type for _, t := range entry.Selectors { @@ -4587,22 +4618,28 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry federatesWith = append(federatesWith, bundle.TrustDomain) } + X509SvidCacheHint := &common.RegistrationEntry_X509SvidCacheHint{} + if err := proto.Unmarshal(model.X509SvidCacheHint, X509SvidCacheHint); err != nil { + return nil, err + } + return &common.RegistrationEntry{ - EntryId: model.EntryID, - Selectors: selectors, - SpiffeId: model.SpiffeID, - ParentId: model.ParentID, - X509SvidTtl: model.TTL, - FederatesWith: federatesWith, - Admin: model.Admin, - Downstream: model.Downstream, - EntryExpiry: model.Expiry, - DnsNames: dnsList, - RevisionNumber: model.RevisionNumber, - StoreSvid: model.StoreSvid, - JwtSvidTtl: model.JWTSvidTTL, - Hint: model.Hint, - CreatedAt: roundedInSecondsUnix(model.CreatedAt), + EntryId: model.EntryID, + Selectors: selectors, + SpiffeId: model.SpiffeID, + ParentId: model.ParentID, + X509SvidTtl: model.TTL, + FederatesWith: federatesWith, + Admin: model.Admin, + Downstream: model.Downstream, + EntryExpiry: model.Expiry, + DnsNames: dnsList, + RevisionNumber: model.RevisionNumber, + StoreSvid: model.StoreSvid, + JwtSvidTtl: model.JWTSvidTTL, + Hint: model.Hint, + X509SvidCacheHint: X509SvidCacheHint, + CreatedAt: roundedInSecondsUnix(model.CreatedAt), }, nil } diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 9176ec349d..c3e061529d 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -2119,6 +2119,9 @@ func (s *PluginSuite) TestCreateOrReturnRegistrationEntry() { "abcd.efg", "somehost", }, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, } entry = tt.modifyEntry(entry) @@ -2175,6 +2178,9 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { "abcd.efg", "somehost", }, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, }, }, { @@ -2186,7 +2192,10 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - StoreSvid: true, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, + StoreSvid: true, }, }, { @@ -2198,7 +2207,10 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - Hint: "external", + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, + Hint: "external", }, }, } { @@ -2323,6 +2335,9 @@ func (s *PluginSuite) TestPruneRegistrationEntries() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, EntryExpiry: now.Unix(), } @@ -3187,7 +3202,10 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { SpiffeId: "spiffe://example.org/foo", ParentId: "spiffe://example.org/bar", X509SvidTtl: 1, - JwtSvidTtl: 20, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + }, + JwtSvidTtl: 20, }) entry.X509SvidTtl = 11 @@ -3195,6 +3213,9 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { entry.Admin = true entry.Downstream = true entry.Hint = "internal" + entry.X509SvidCacheHint = &common.RegistrationEntry_X509SvidCacheHint{ + JwtOnly: false, + } updatedRegistrationEntry, err := s.ds.UpdateRegistrationEntry(ctx, entry, nil) s.Require().NoError(err) @@ -3260,31 +3281,33 @@ func (s *PluginSuite) TestUpdateRegistrationEntryWithMask() { // Note that most of the input validation is done in the API layer and has more extensive tests there. now := time.Now().Unix() oldEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/oldSpiffeId", - X509SvidTtl: 1000, - JwtSvidTtl: 3000, - Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, - FederatesWith: []string{"spiffe://dom1.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns1"}, - Downstream: false, - StoreSvid: false, + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/oldSpiffeId", + X509SvidTtl: 1000, + JwtSvidTtl: 3000, + Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, + FederatesWith: []string{"spiffe://dom1.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns1"}, + Downstream: false, + StoreSvid: false, + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{}, } newEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/newSpiffeId", - X509SvidTtl: 4000, - JwtSvidTtl: 6000, - Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, - FederatesWith: []string{"spiffe://dom2.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns2"}, - Downstream: false, - StoreSvid: true, - Hint: "internal", + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/newSpiffeId", + X509SvidTtl: 4000, + JwtSvidTtl: 6000, + Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, + FederatesWith: []string{"spiffe://dom2.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns2"}, + Downstream: false, + StoreSvid: true, + Hint: "internal", + X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{}, } badEntry := &common.RegistrationEntry{ ParentId: "not a good parent id", diff --git a/pkg/server/datastore/sqlstore/testdata/entries.json b/pkg/server/datastore/sqlstore/testdata/entries.json index b09f848515..26ae179770 100644 --- a/pkg/server/datastore/sqlstore/testdata/entries.json +++ b/pkg/server/datastore/sqlstore/testdata/entries.json @@ -17,7 +17,10 @@ "spiffe_id": "spiffe://id1", "parent_id": "spiffe://parent", "ttl": 200, - "dns_names": ["a", "b"] + "dns_names": [ + "a", + "b" + ] }, { "selectors": [ @@ -90,5 +93,27 @@ "spiffe_id": "spiffe://id5", "parent_id": "spiffe://parent2", "ttl": 200 + }, + { + "selectors": [ + { + "type": "b", + "value": "2" + }, + { + "type": "c", + "value": "3" + }, + { + "type": "d", + "value": "4" + } + ], + "spiffe_id": "spiffe://id6", + "parent_id": "spiffe://parent2", + "x509_svid_cache_hint": { + "jwt_only": true + }, + "ttl": 200 } ] diff --git a/proto/spire/common/common.pb.go b/proto/spire/common/common.pb.go index fe533e52aa..16d3502d8d 100644 --- a/proto/spire/common/common.pb.go +++ b/proto/spire/common/common.pb.go @@ -364,9 +364,10 @@ type RegistrationEntry struct { // identity should be used by a workload when more than one SVID is returned. Hint string `protobuf:"bytes,14,opt,name=hint,proto3" json:"hint,omitempty"` // * Time of creation, in seconds from epoch - CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + X509SvidCacheHint *RegistrationEntry_X509SvidCacheHint `protobuf:"bytes,16,opt,name=x509_svid_cache_hint,json=x509SvidCacheHint,proto3" json:"x509_svid_cache_hint,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntry) Reset() { @@ -504,24 +505,32 @@ func (x *RegistrationEntry) GetCreatedAt() int64 { return 0 } +func (x *RegistrationEntry) GetX509SvidCacheHint() *RegistrationEntry_X509SvidCacheHint { + if x != nil { + return x.X509SvidCacheHint + } + return nil +} + // * The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry type RegistrationEntryMask struct { - state protoimpl.MessageState `protogen:"open.v1"` - Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` - ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` - SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` - X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` - FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` - EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` - Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` - Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` - EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` - DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` - StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` - JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` - Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` + ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` + SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` + X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` + FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` + EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` + Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` + Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` + EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` + DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` + StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` + JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` + Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` + X509SvidCacheHint bool `protobuf:"varint,14,opt,name=x509_svid_cache_hint,json=x509SvidCacheHint,proto3" json:"x509_svid_cache_hint,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntryMask) Reset() { @@ -645,6 +654,13 @@ func (x *RegistrationEntryMask) GetHint() bool { return false } +func (x *RegistrationEntryMask) GetX509SvidCacheHint() bool { + if x != nil { + return x.X509SvidCacheHint + } + return false +} + // * A list of registration entries. type RegistrationEntries struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1060,6 +1076,54 @@ func (x *AttestedNodeMask) GetCanReattest() bool { return false } +// * Represents a set of options informing the agent behaviour with respect to +// pre-fecthing and caching X509 SVIDs. This is meant to prevent unnecessary effort +// spent on providing X509 SVIDs to workloads, which are likely to ask only for JWT SVID. +type RegistrationEntry_X509SvidCacheHint struct { + state protoimpl.MessageState `protogen:"open.v1"` + // * Flag indicating whether the workload is likely to require only JWT SVID. + JwtOnly bool `protobuf:"varint,1,opt,name=jwt_only,json=jwtOnly,proto3" json:"jwt_only,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegistrationEntry_X509SvidCacheHint) Reset() { + *x = RegistrationEntry_X509SvidCacheHint{} + mi := &file_spire_common_common_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegistrationEntry_X509SvidCacheHint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegistrationEntry_X509SvidCacheHint) ProtoMessage() {} + +func (x *RegistrationEntry_X509SvidCacheHint) ProtoReflect() protoreflect.Message { + mi := &file_spire_common_common_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegistrationEntry_X509SvidCacheHint.ProtoReflect.Descriptor instead. +func (*RegistrationEntry_X509SvidCacheHint) Descriptor() ([]byte, []int) { + return file_spire_common_common_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *RegistrationEntry_X509SvidCacheHint) GetJwtOnly() bool { + if x != nil { + return x.JwtOnly + } + return false +} + var File_spire_common_common_proto protoreflect.FileDescriptor const file_spire_common_common_proto_rawDesc = "" + @@ -1082,7 +1146,7 @@ const file_spire_common_common_proto_rawDesc = "" + "\x16new_cert_serial_number\x18\x05 \x01(\tR\x13newCertSerialNumber\x12+\n" + "\x12new_cert_not_after\x18\x06 \x01(\x03R\x0fnewCertNotAfter\x124\n" + "\tselectors\x18\a \x03(\v2\x16.spire.common.SelectorR\tselectors\x12!\n" + - "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\xfb\x03\n" + + "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\x8f\x05\n" + "\x11RegistrationEntry\x124\n" + "\tselectors\x18\x01 \x03(\v2\x16.spire.common.SelectorR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\tR\bparentId\x12\x1b\n" + @@ -1104,7 +1168,10 @@ const file_spire_common_common_proto_rawDesc = "" + "jwtSvidTtl\x12\x12\n" + "\x04hint\x18\x0e \x01(\tR\x04hint\x12\x1d\n" + "\n" + - "created_at\x18\x0f \x01(\x03R\tcreatedAt\"\x9f\x03\n" + + "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12b\n" + + "\x14x509_svid_cache_hint\x18\x10 \x01(\v21.spire.common.RegistrationEntry.X509SvidCacheHintR\x11x509SvidCacheHint\x1a.\n" + + "\x11X509SvidCacheHint\x12\x19\n" + + "\bjwt_only\x18\x01 \x01(\bR\ajwtOnly\"\xd0\x03\n" + "\x15RegistrationEntryMask\x12\x1c\n" + "\tselectors\x18\x01 \x01(\bR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\bR\bparentId\x12\x1b\n" + @@ -1123,7 +1190,8 @@ const file_spire_common_common_proto_rawDesc = "" + "store_svid\x18\v \x01(\bR\tstoreSvid\x12 \n" + "\fjwt_svid_ttl\x18\f \x01(\bR\n" + "jwtSvidTtl\x12\x12\n" + - "\x04hint\x18\r \x01(\bR\x04hint\"P\n" + + "\x04hint\x18\r \x01(\bR\x04hint\x12/\n" + + "\x14x509_svid_cache_hint\x18\x0e \x01(\bR\x11x509SvidCacheHint\"P\n" + "\x13RegistrationEntries\x129\n" + "\aentries\x18\x01 \x03(\v2\x1f.spire.common.RegistrationEntryR\aentries\"K\n" + "\vCertificate\x12\x1b\n" + @@ -1170,34 +1238,36 @@ func file_spire_common_common_proto_rawDescGZIP() []byte { return file_spire_common_common_proto_rawDescData } -var file_spire_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_spire_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_spire_common_common_proto_goTypes = []any{ - (*Empty)(nil), // 0: spire.common.Empty - (*AttestationData)(nil), // 1: spire.common.AttestationData - (*Selector)(nil), // 2: spire.common.Selector - (*Selectors)(nil), // 3: spire.common.Selectors - (*AttestedNode)(nil), // 4: spire.common.AttestedNode - (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry - (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask - (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries - (*Certificate)(nil), // 8: spire.common.Certificate - (*PublicKey)(nil), // 9: spire.common.PublicKey - (*Bundle)(nil), // 10: spire.common.Bundle - (*BundleMask)(nil), // 11: spire.common.BundleMask - (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask + (*Empty)(nil), // 0: spire.common.Empty + (*AttestationData)(nil), // 1: spire.common.AttestationData + (*Selector)(nil), // 2: spire.common.Selector + (*Selectors)(nil), // 3: spire.common.Selectors + (*AttestedNode)(nil), // 4: spire.common.AttestedNode + (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry + (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask + (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries + (*Certificate)(nil), // 8: spire.common.Certificate + (*PublicKey)(nil), // 9: spire.common.PublicKey + (*Bundle)(nil), // 10: spire.common.Bundle + (*BundleMask)(nil), // 11: spire.common.BundleMask + (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask + (*RegistrationEntry_X509SvidCacheHint)(nil), // 13: spire.common.RegistrationEntry.X509SvidCacheHint } var file_spire_common_common_proto_depIdxs = []int32{ - 2, // 0: spire.common.Selectors.entries:type_name -> spire.common.Selector - 2, // 1: spire.common.AttestedNode.selectors:type_name -> spire.common.Selector - 2, // 2: spire.common.RegistrationEntry.selectors:type_name -> spire.common.Selector - 5, // 3: spire.common.RegistrationEntries.entries:type_name -> spire.common.RegistrationEntry - 8, // 4: spire.common.Bundle.root_cas:type_name -> spire.common.Certificate - 9, // 5: spire.common.Bundle.jwt_signing_keys:type_name -> spire.common.PublicKey - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 2, // 0: spire.common.Selectors.entries:type_name -> spire.common.Selector + 2, // 1: spire.common.AttestedNode.selectors:type_name -> spire.common.Selector + 2, // 2: spire.common.RegistrationEntry.selectors:type_name -> spire.common.Selector + 13, // 3: spire.common.RegistrationEntry.x509_svid_cache_hint:type_name -> spire.common.RegistrationEntry.X509SvidCacheHint + 5, // 4: spire.common.RegistrationEntries.entries:type_name -> spire.common.RegistrationEntry + 8, // 5: spire.common.Bundle.root_cas:type_name -> spire.common.Certificate + 9, // 6: spire.common.Bundle.jwt_signing_keys:type_name -> spire.common.PublicKey + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_spire_common_common_proto_init() } @@ -1211,7 +1281,7 @@ func file_spire_common_common_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_spire_common_common_proto_rawDesc), len(file_spire_common_common_proto_rawDesc)), NumEnums: 0, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/spire/common/common.proto b/proto/spire/common/common.proto index 779934b08a..0fb41fecc4 100644 --- a/proto/spire/common/common.proto +++ b/proto/spire/common/common.proto @@ -96,6 +96,14 @@ message RegistrationEntry { string hint = 14; /** Time of creation, in seconds from epoch */ int64 created_at = 15; + /** Represents a set of options informing the agent behaviour with respect to + pre-fecthing and caching X509 SVIDs. This is meant to prevent unnecessary effort + spent on providing X509 SVIDs to workloads, which are likely to ask only for JWT SVID. */ + message X509SvidCacheHint { + /** Flag indicating whether the workload is likely to require only JWT SVID. */ + bool jwt_only = 1; + } + X509SvidCacheHint x509_svid_cache_hint = 16; } /** The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry */ @@ -113,6 +121,7 @@ message RegistrationEntryMask { bool store_svid = 11; bool jwt_svid_ttl = 12; bool hint = 13; + bool x509_svid_cache_hint = 14; } From 72289e311dbdad100b25c67b54b0a4ebb088dd4c Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sat, 11 Oct 2025 21:10:34 +0100 Subject: [PATCH 02/51] renamed fields as per review comments Signed-off-by: Valentin Fadeev --- pkg/agent/manager/sync.go | 2 +- pkg/server/datastore/sqlstore/models.go | 4 +- pkg/server/datastore/sqlstore/sqlstore.go | 130 +++++++++--------- .../datastore/sqlstore/sqlstore_test.go | 78 +++++------ .../datastore/sqlstore/testdata/entries.json | 4 +- proto/spire/common/common.pb.go | 129 ++++++++--------- proto/spire/common/common.proto | 17 +-- 7 files changed, 183 insertions(+), 181 deletions(-) diff --git a/pkg/agent/manager/sync.go b/pkg/agent/manager/sync.go index 59c2b2c23b..dd4355d5d7 100644 --- a/pkg/agent/manager/sync.go +++ b/pkg/agent/manager/sync.go @@ -345,7 +345,7 @@ func (m *manager) fetchEntries(ctx context.Context) (_ *cache.UpdateEntries, _ * case entry.StoreSvid: storeEntries[entryID] = entry default: - if !entry.X509SvidCacheHint.GetJwtOnly() { + if !entry.CacheHintFlags.GetDisableX509SvidPrefetch() { cacheEntries[entryID] = entry } } diff --git a/pkg/server/datastore/sqlstore/models.go b/pkg/server/datastore/sqlstore/models.go index 073192c10c..8e63de0c8d 100644 --- a/pkg/server/datastore/sqlstore/models.go +++ b/pkg/server/datastore/sqlstore/models.go @@ -113,9 +113,9 @@ type RegisteredEntry struct { // TTL of JWT identities derived from this entry JWTSvidTTL int32 `gorm:"column:jwt_svid_ttl"` - // X509SvidCacheHint contains a set of options and flags that inform the + // CacheHintFlags contains a set of options and flags that inform the // agent behaviour with respect to pre-fetching and refreshing X509 SVIDs - X509SvidCacheHint []byte `gorm:"size:255,column:x509_svid_cache_hint"` // corresponds to TINYBLOB in MySQL + CacheHintFlags []byte `gorm:"size:255,column:cache_hint_flags"` // corresponds to TINYBLOB in MySQL } // RegisteredEntryEvent holds the entry id of a registered entry that had an event diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index d3ea58ad5a..395e3a188b 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -2504,23 +2504,23 @@ func createRegistrationEntry(tx *gorm.DB, entry *common.RegistrationEntry) (*com return nil, err } - X509SvidCacheHint, err := proto.Marshal(entry.X509SvidCacheHint) + CacheHintFlags, err := proto.Marshal(entry.CacheHintFlags) if err != nil { return nil, err } newRegisteredEntry := RegisteredEntry{ - EntryID: entryID, - SpiffeID: entry.SpiffeId, - ParentID: entry.ParentId, - TTL: entry.X509SvidTtl, - Admin: entry.Admin, - Downstream: entry.Downstream, - Expiry: entry.EntryExpiry, - StoreSvid: entry.StoreSvid, - JWTSvidTTL: entry.JwtSvidTtl, - Hint: entry.Hint, - X509SvidCacheHint: X509SvidCacheHint, + EntryID: entryID, + SpiffeID: entry.SpiffeId, + ParentID: entry.ParentId, + TTL: entry.X509SvidTtl, + Admin: entry.Admin, + Downstream: entry.Downstream, + Expiry: entry.EntryExpiry, + StoreSvid: entry.StoreSvid, + JWTSvidTTL: entry.JwtSvidTtl, + Hint: entry.Hint, + CacheHintFlags: CacheHintFlags, } if err := tx.Create(&newRegisteredEntry).Error; err != nil { @@ -2636,7 +2636,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2701,7 +2701,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2762,7 +2762,7 @@ SELECT D.value AS dns_name, E.revision_number, E.jwt_svid_ttl AS reg_jwt_svid_ttl, - E.x509_svid_cache_hint AS x509_svid_cache_hint + E.cache_hint_flags AS cache_hint_flags FROM registered_entries E LEFT JOIN @@ -2805,7 +2805,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -3017,7 +3017,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries `) @@ -3113,7 +3113,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries `) @@ -3207,7 +3207,7 @@ SELECT D.value AS dns_name, E.revision_number, E.jwt_svid_ttl AS reg_jwt_svid_ttl, - E.x509_svid_cache_hint AS x509_svid_cache_hint + E.cache_hint_flags AS cache_hint_flags FROM registered_entries E LEFT JOIN @@ -3282,7 +3282,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - x509_svid_cache_hint + cache_hint_flags FROM registered_entries `) @@ -3818,26 +3818,26 @@ func fillNodeSelectorFromRow(nodeSelector *common.Selector, r *nodeSelectorRow) } type entryRow struct { - EId uint64 - EntryID sql.NullString - SpiffeID sql.NullString - ParentID sql.NullString - RegTTL sql.NullInt64 - Admin sql.NullBool - Downstream sql.NullBool - Expiry sql.NullInt64 - SelectorID sql.NullInt64 - SelectorType sql.NullString - SelectorValue sql.NullString - StoreSvid sql.NullBool - Hint sql.NullString - CreatedAt sql.NullTime - TrustDomain sql.NullString - DNSNameID sql.NullInt64 - DNSName sql.NullString - RevisionNumber sql.NullInt64 - RegJwtSvidTTL sql.NullInt64 - X509SvidCacheHint sql.Null[[]byte] + EId uint64 + EntryID sql.NullString + SpiffeID sql.NullString + ParentID sql.NullString + RegTTL sql.NullInt64 + Admin sql.NullBool + Downstream sql.NullBool + Expiry sql.NullInt64 + SelectorID sql.NullInt64 + SelectorType sql.NullString + SelectorValue sql.NullString + StoreSvid sql.NullBool + Hint sql.NullString + CreatedAt sql.NullTime + TrustDomain sql.NullString + DNSNameID sql.NullInt64 + DNSName sql.NullString + RevisionNumber sql.NullInt64 + RegJwtSvidTTL sql.NullInt64 + CacheHintFlags sql.Null[[]byte] } func scanEntryRow(rs *sql.Rows, r *entryRow) error { @@ -3861,7 +3861,7 @@ func scanEntryRow(rs *sql.Rows, r *entryRow) error { &r.DNSName, &r.RevisionNumber, &r.RegJwtSvidTTL, - &r.X509SvidCacheHint, + &r.CacheHintFlags, )) } @@ -3924,8 +3924,8 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { entry.CreatedAt = roundedInSecondsUnix(r.CreatedAt.Time) } - entry.X509SvidCacheHint = &common.RegistrationEntry_X509SvidCacheHint{} - if err := proto.Unmarshal(r.X509SvidCacheHint.V, entry.X509SvidCacheHint); err != nil { + entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} + if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { return newSQLError("invalid value for X.509 cache hint: %s", err) } @@ -4026,12 +4026,12 @@ func updateRegistrationEntry(tx *gorm.DB, e *common.RegistrationEntry, mask *com if mask == nil || mask.Hint { entry.Hint = e.Hint } - if mask == nil || mask.X509SvidCacheHint { - X509SvidCacheHint, err := proto.Marshal(e.X509SvidCacheHint) + if mask == nil || mask.CacheHintFlags { + CacheHintFlags, err := proto.Marshal(e.CacheHintFlags) if err != nil { return nil, err } - entry.X509SvidCacheHint = X509SvidCacheHint + entry.CacheHintFlags = CacheHintFlags } // Revision number is increased by 1 on every update call @@ -4486,7 +4486,7 @@ func validateRegistrationEntry(entry *common.RegistrationEntry) error { // it is done to avoid users to mix selectors from different platforms in // entries with storable SVIDs if entry.StoreSvid { - if entry.X509SvidCacheHint.GetJwtOnly() { + if entry.CacheHintFlags.GetDisableX509SvidPrefetch() { return newValidationError("specifying cache behaviour is incompatible with storable SVIDs") } // Selectors must never be empty @@ -4618,28 +4618,28 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry federatesWith = append(federatesWith, bundle.TrustDomain) } - X509SvidCacheHint := &common.RegistrationEntry_X509SvidCacheHint{} - if err := proto.Unmarshal(model.X509SvidCacheHint, X509SvidCacheHint); err != nil { + CacheHintFlags := &common.RegistrationEntry_CacheHintFlags{} + if err := proto.Unmarshal(model.CacheHintFlags, CacheHintFlags); err != nil { return nil, err } return &common.RegistrationEntry{ - EntryId: model.EntryID, - Selectors: selectors, - SpiffeId: model.SpiffeID, - ParentId: model.ParentID, - X509SvidTtl: model.TTL, - FederatesWith: federatesWith, - Admin: model.Admin, - Downstream: model.Downstream, - EntryExpiry: model.Expiry, - DnsNames: dnsList, - RevisionNumber: model.RevisionNumber, - StoreSvid: model.StoreSvid, - JwtSvidTtl: model.JWTSvidTTL, - Hint: model.Hint, - X509SvidCacheHint: X509SvidCacheHint, - CreatedAt: roundedInSecondsUnix(model.CreatedAt), + EntryId: model.EntryID, + Selectors: selectors, + SpiffeId: model.SpiffeID, + ParentId: model.ParentID, + X509SvidTtl: model.TTL, + FederatesWith: federatesWith, + Admin: model.Admin, + Downstream: model.Downstream, + EntryExpiry: model.Expiry, + DnsNames: dnsList, + RevisionNumber: model.RevisionNumber, + StoreSvid: model.StoreSvid, + JwtSvidTtl: model.JWTSvidTTL, + Hint: model.Hint, + CacheHintFlags: CacheHintFlags, + CreatedAt: roundedInSecondsUnix(model.CreatedAt), }, nil } diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index c3e061529d..5c546ab890 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -2119,8 +2119,8 @@ func (s *PluginSuite) TestCreateOrReturnRegistrationEntry() { "abcd.efg", "somehost", }, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, } entry = tt.modifyEntry(entry) @@ -2178,8 +2178,8 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { "abcd.efg", "somehost", }, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, }, }, @@ -2192,8 +2192,8 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, StoreSvid: true, }, @@ -2207,8 +2207,8 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, Hint: "external", }, @@ -2335,8 +2335,8 @@ func (s *PluginSuite) TestPruneRegistrationEntries() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, EntryExpiry: now.Unix(), } @@ -3202,8 +3202,8 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { SpiffeId: "spiffe://example.org/foo", ParentId: "spiffe://example.org/bar", X509SvidTtl: 1, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, }, JwtSvidTtl: 20, }) @@ -3213,8 +3213,8 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { entry.Admin = true entry.Downstream = true entry.Hint = "internal" - entry.X509SvidCacheHint = &common.RegistrationEntry_X509SvidCacheHint{ - JwtOnly: false, + entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, } updatedRegistrationEntry, err := s.ds.UpdateRegistrationEntry(ctx, entry, nil) @@ -3281,33 +3281,33 @@ func (s *PluginSuite) TestUpdateRegistrationEntryWithMask() { // Note that most of the input validation is done in the API layer and has more extensive tests there. now := time.Now().Unix() oldEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/oldSpiffeId", - X509SvidTtl: 1000, - JwtSvidTtl: 3000, - Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, - FederatesWith: []string{"spiffe://dom1.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns1"}, - Downstream: false, - StoreSvid: false, - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{}, + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/oldSpiffeId", + X509SvidTtl: 1000, + JwtSvidTtl: 3000, + Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, + FederatesWith: []string{"spiffe://dom1.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns1"}, + Downstream: false, + StoreSvid: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, } newEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/newSpiffeId", - X509SvidTtl: 4000, - JwtSvidTtl: 6000, - Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, - FederatesWith: []string{"spiffe://dom2.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns2"}, - Downstream: false, - StoreSvid: true, - Hint: "internal", - X509SvidCacheHint: &common.RegistrationEntry_X509SvidCacheHint{}, + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/newSpiffeId", + X509SvidTtl: 4000, + JwtSvidTtl: 6000, + Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, + FederatesWith: []string{"spiffe://dom2.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns2"}, + Downstream: false, + StoreSvid: true, + Hint: "internal", + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, } badEntry := &common.RegistrationEntry{ ParentId: "not a good parent id", diff --git a/pkg/server/datastore/sqlstore/testdata/entries.json b/pkg/server/datastore/sqlstore/testdata/entries.json index 26ae179770..16b1c449f1 100644 --- a/pkg/server/datastore/sqlstore/testdata/entries.json +++ b/pkg/server/datastore/sqlstore/testdata/entries.json @@ -111,8 +111,8 @@ ], "spiffe_id": "spiffe://id6", "parent_id": "spiffe://parent2", - "x509_svid_cache_hint": { - "jwt_only": true + "cache_hint_flags": { + "disable_x509_svid_prefetch": true }, "ttl": 200 } diff --git a/proto/spire/common/common.pb.go b/proto/spire/common/common.pb.go index 16d3502d8d..296ad6aaf6 100644 --- a/proto/spire/common/common.pb.go +++ b/proto/spire/common/common.pb.go @@ -364,10 +364,10 @@ type RegistrationEntry struct { // identity should be used by a workload when more than one SVID is returned. Hint string `protobuf:"bytes,14,opt,name=hint,proto3" json:"hint,omitempty"` // * Time of creation, in seconds from epoch - CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - X509SvidCacheHint *RegistrationEntry_X509SvidCacheHint `protobuf:"bytes,16,opt,name=x509_svid_cache_hint,json=x509SvidCacheHint,proto3" json:"x509_svid_cache_hint,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + CacheHintFlags *RegistrationEntry_CacheHintFlags `protobuf:"bytes,16,opt,name=cache_hint_flags,json=cacheHintFlags,proto3" json:"cache_hint_flags,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntry) Reset() { @@ -505,32 +505,32 @@ func (x *RegistrationEntry) GetCreatedAt() int64 { return 0 } -func (x *RegistrationEntry) GetX509SvidCacheHint() *RegistrationEntry_X509SvidCacheHint { +func (x *RegistrationEntry) GetCacheHintFlags() *RegistrationEntry_CacheHintFlags { if x != nil { - return x.X509SvidCacheHint + return x.CacheHintFlags } return nil } // * The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry type RegistrationEntryMask struct { - state protoimpl.MessageState `protogen:"open.v1"` - Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` - ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` - SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` - X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` - FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` - EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` - Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` - Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` - EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` - DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` - StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` - JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` - Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` - X509SvidCacheHint bool `protobuf:"varint,14,opt,name=x509_svid_cache_hint,json=x509SvidCacheHint,proto3" json:"x509_svid_cache_hint,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` + ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` + SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` + X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` + FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` + EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` + Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` + Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` + EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` + DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` + StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` + JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` + Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` + CacheHintFlags bool `protobuf:"varint,14,opt,name=cache_hint_flags,json=cacheHintFlags,proto3" json:"cache_hint_flags,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntryMask) Reset() { @@ -654,9 +654,9 @@ func (x *RegistrationEntryMask) GetHint() bool { return false } -func (x *RegistrationEntryMask) GetX509SvidCacheHint() bool { +func (x *RegistrationEntryMask) GetCacheHintFlags() bool { if x != nil { - return x.X509SvidCacheHint + return x.CacheHintFlags } return false } @@ -1076,31 +1076,32 @@ func (x *AttestedNodeMask) GetCanReattest() bool { return false } -// * Represents a set of options informing the agent behaviour with respect to -// pre-fecthing and caching X509 SVIDs. This is meant to prevent unnecessary effort -// spent on providing X509 SVIDs to workloads, which are likely to ask only for JWT SVID. -type RegistrationEntry_X509SvidCacheHint struct { +// * Represents a set of flags informing the agent behaviour with respect to +// pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort +// spent on generating SVIDs of types, which are unlikely to be needed. +type RegistrationEntry_CacheHintFlags struct { state protoimpl.MessageState `protogen:"open.v1"` - // * Flag indicating whether the workload is likely to require only JWT SVID. - JwtOnly bool `protobuf:"varint,1,opt,name=jwt_only,json=jwtOnly,proto3" json:"jwt_only,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // * Flag indicating whether the agent should prefetch and cache X509 SVID. + // This can be set to `true` if the workload is unlikely to request an X509 SVID. + DisableX509SvidPrefetch bool `protobuf:"varint,1,opt,name=disable_x509_svid_prefetch,json=disableX509SvidPrefetch,proto3" json:"disable_x509_svid_prefetch,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *RegistrationEntry_X509SvidCacheHint) Reset() { - *x = RegistrationEntry_X509SvidCacheHint{} +func (x *RegistrationEntry_CacheHintFlags) Reset() { + *x = RegistrationEntry_CacheHintFlags{} mi := &file_spire_common_common_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *RegistrationEntry_X509SvidCacheHint) String() string { +func (x *RegistrationEntry_CacheHintFlags) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RegistrationEntry_X509SvidCacheHint) ProtoMessage() {} +func (*RegistrationEntry_CacheHintFlags) ProtoMessage() {} -func (x *RegistrationEntry_X509SvidCacheHint) ProtoReflect() protoreflect.Message { +func (x *RegistrationEntry_CacheHintFlags) ProtoReflect() protoreflect.Message { mi := &file_spire_common_common_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1112,14 +1113,14 @@ func (x *RegistrationEntry_X509SvidCacheHint) ProtoReflect() protoreflect.Messag return mi.MessageOf(x) } -// Deprecated: Use RegistrationEntry_X509SvidCacheHint.ProtoReflect.Descriptor instead. -func (*RegistrationEntry_X509SvidCacheHint) Descriptor() ([]byte, []int) { +// Deprecated: Use RegistrationEntry_CacheHintFlags.ProtoReflect.Descriptor instead. +func (*RegistrationEntry_CacheHintFlags) Descriptor() ([]byte, []int) { return file_spire_common_common_proto_rawDescGZIP(), []int{5, 0} } -func (x *RegistrationEntry_X509SvidCacheHint) GetJwtOnly() bool { +func (x *RegistrationEntry_CacheHintFlags) GetDisableX509SvidPrefetch() bool { if x != nil { - return x.JwtOnly + return x.DisableX509SvidPrefetch } return false } @@ -1146,7 +1147,7 @@ const file_spire_common_common_proto_rawDesc = "" + "\x16new_cert_serial_number\x18\x05 \x01(\tR\x13newCertSerialNumber\x12+\n" + "\x12new_cert_not_after\x18\x06 \x01(\x03R\x0fnewCertNotAfter\x124\n" + "\tselectors\x18\a \x03(\v2\x16.spire.common.SelectorR\tselectors\x12!\n" + - "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\x8f\x05\n" + + "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\xa4\x05\n" + "\x11RegistrationEntry\x124\n" + "\tselectors\x18\x01 \x03(\v2\x16.spire.common.SelectorR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\tR\bparentId\x12\x1b\n" + @@ -1168,10 +1169,10 @@ const file_spire_common_common_proto_rawDesc = "" + "jwtSvidTtl\x12\x12\n" + "\x04hint\x18\x0e \x01(\tR\x04hint\x12\x1d\n" + "\n" + - "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12b\n" + - "\x14x509_svid_cache_hint\x18\x10 \x01(\v21.spire.common.RegistrationEntry.X509SvidCacheHintR\x11x509SvidCacheHint\x1a.\n" + - "\x11X509SvidCacheHint\x12\x19\n" + - "\bjwt_only\x18\x01 \x01(\bR\ajwtOnly\"\xd0\x03\n" + + "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12X\n" + + "\x10cache_hint_flags\x18\x10 \x01(\v2..spire.common.RegistrationEntry.CacheHintFlagsR\x0ecacheHintFlags\x1aM\n" + + "\x0eCacheHintFlags\x12;\n" + + "\x1adisable_x509_svid_prefetch\x18\x01 \x01(\bR\x17disableX509SvidPrefetch\"\xc9\x03\n" + "\x15RegistrationEntryMask\x12\x1c\n" + "\tselectors\x18\x01 \x01(\bR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\bR\bparentId\x12\x1b\n" + @@ -1190,8 +1191,8 @@ const file_spire_common_common_proto_rawDesc = "" + "store_svid\x18\v \x01(\bR\tstoreSvid\x12 \n" + "\fjwt_svid_ttl\x18\f \x01(\bR\n" + "jwtSvidTtl\x12\x12\n" + - "\x04hint\x18\r \x01(\bR\x04hint\x12/\n" + - "\x14x509_svid_cache_hint\x18\x0e \x01(\bR\x11x509SvidCacheHint\"P\n" + + "\x04hint\x18\r \x01(\bR\x04hint\x12(\n" + + "\x10cache_hint_flags\x18\x0e \x01(\bR\x0ecacheHintFlags\"P\n" + "\x13RegistrationEntries\x129\n" + "\aentries\x18\x01 \x03(\v2\x1f.spire.common.RegistrationEntryR\aentries\"K\n" + "\vCertificate\x12\x1b\n" + @@ -1240,26 +1241,26 @@ func file_spire_common_common_proto_rawDescGZIP() []byte { var file_spire_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_spire_common_common_proto_goTypes = []any{ - (*Empty)(nil), // 0: spire.common.Empty - (*AttestationData)(nil), // 1: spire.common.AttestationData - (*Selector)(nil), // 2: spire.common.Selector - (*Selectors)(nil), // 3: spire.common.Selectors - (*AttestedNode)(nil), // 4: spire.common.AttestedNode - (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry - (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask - (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries - (*Certificate)(nil), // 8: spire.common.Certificate - (*PublicKey)(nil), // 9: spire.common.PublicKey - (*Bundle)(nil), // 10: spire.common.Bundle - (*BundleMask)(nil), // 11: spire.common.BundleMask - (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask - (*RegistrationEntry_X509SvidCacheHint)(nil), // 13: spire.common.RegistrationEntry.X509SvidCacheHint + (*Empty)(nil), // 0: spire.common.Empty + (*AttestationData)(nil), // 1: spire.common.AttestationData + (*Selector)(nil), // 2: spire.common.Selector + (*Selectors)(nil), // 3: spire.common.Selectors + (*AttestedNode)(nil), // 4: spire.common.AttestedNode + (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry + (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask + (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries + (*Certificate)(nil), // 8: spire.common.Certificate + (*PublicKey)(nil), // 9: spire.common.PublicKey + (*Bundle)(nil), // 10: spire.common.Bundle + (*BundleMask)(nil), // 11: spire.common.BundleMask + (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask + (*RegistrationEntry_CacheHintFlags)(nil), // 13: spire.common.RegistrationEntry.CacheHintFlags } var file_spire_common_common_proto_depIdxs = []int32{ 2, // 0: spire.common.Selectors.entries:type_name -> spire.common.Selector 2, // 1: spire.common.AttestedNode.selectors:type_name -> spire.common.Selector 2, // 2: spire.common.RegistrationEntry.selectors:type_name -> spire.common.Selector - 13, // 3: spire.common.RegistrationEntry.x509_svid_cache_hint:type_name -> spire.common.RegistrationEntry.X509SvidCacheHint + 13, // 3: spire.common.RegistrationEntry.cache_hint_flags:type_name -> spire.common.RegistrationEntry.CacheHintFlags 5, // 4: spire.common.RegistrationEntries.entries:type_name -> spire.common.RegistrationEntry 8, // 5: spire.common.Bundle.root_cas:type_name -> spire.common.Certificate 9, // 6: spire.common.Bundle.jwt_signing_keys:type_name -> spire.common.PublicKey diff --git a/proto/spire/common/common.proto b/proto/spire/common/common.proto index 0fb41fecc4..0e4ea90db5 100644 --- a/proto/spire/common/common.proto +++ b/proto/spire/common/common.proto @@ -96,14 +96,15 @@ message RegistrationEntry { string hint = 14; /** Time of creation, in seconds from epoch */ int64 created_at = 15; - /** Represents a set of options informing the agent behaviour with respect to - pre-fecthing and caching X509 SVIDs. This is meant to prevent unnecessary effort - spent on providing X509 SVIDs to workloads, which are likely to ask only for JWT SVID. */ - message X509SvidCacheHint { - /** Flag indicating whether the workload is likely to require only JWT SVID. */ - bool jwt_only = 1; + /** Represents a set of flags informing the agent behaviour with respect to + pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort + spent on generating SVIDs of types, which are unlikely to be needed. */ + message CacheHintFlags { + /** Flag indicating whether the agent should prefetch and cache X509 SVID. + This can be set to `true` if the workload is unlikely to request an X509 SVID. */ + bool disable_x509_svid_prefetch = 1; } - X509SvidCacheHint x509_svid_cache_hint = 16; + CacheHintFlags cache_hint_flags = 16; } /** The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry */ @@ -121,7 +122,7 @@ message RegistrationEntryMask { bool store_svid = 11; bool jwt_svid_ttl = 12; bool hint = 13; - bool x509_svid_cache_hint = 14; + bool cache_hint_flags = 14; } From 3f71b9f4a5827013bbbebed29249c19524d5d529 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 13 Oct 2025 13:25:13 +0100 Subject: [PATCH 03/51] fixed existing tests Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 4 +- pkg/server/datastore/sqlstore/sqlstore.go | 16 ++-- .../datastore/sqlstore/sqlstore_test.go | 81 ++++++++----------- .../datastore/sqlstore/testdata/entries.json | 22 ----- proto/spire/common/common.pb.go | 12 +-- proto/spire/common/common.proto | 2 +- 6 files changed, 53 insertions(+), 84 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 88d5e66c91..360b272db9 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -267,12 +267,12 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 24 + latestSchemaVersion = 23 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 24 + lastMinorReleaseSchemaVersion = 23 ) // the current code version diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index 395e3a188b..8a0f962785 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -3924,9 +3924,11 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { entry.CreatedAt = roundedInSecondsUnix(r.CreatedAt.Time) } - entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} - if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { - return newSQLError("invalid value for X.509 cache hint: %s", err) + if r.CacheHintFlags.Valid { + entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} + if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { + return newSQLError("invalid value for X.509 cache hint: %s", err) + } } return nil @@ -4619,8 +4621,12 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry } CacheHintFlags := &common.RegistrationEntry_CacheHintFlags{} - if err := proto.Unmarshal(model.CacheHintFlags, CacheHintFlags); err != nil { - return nil, err + if model.CacheHintFlags != nil { + if err := proto.Unmarshal(model.CacheHintFlags, CacheHintFlags); err != nil { + return nil, err + } + } else { + CacheHintFlags = nil } return &common.RegistrationEntry{ diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 5c546ab890..5fa09f64dd 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -2119,9 +2119,6 @@ func (s *PluginSuite) TestCreateOrReturnRegistrationEntry() { "abcd.efg", "somehost", }, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, } entry = tt.modifyEntry(entry) @@ -2178,9 +2175,6 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { "abcd.efg", "somehost", }, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, }, }, { @@ -2192,10 +2186,7 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, - StoreSvid: true, + StoreSvid: true, }, }, { @@ -2207,10 +2198,7 @@ func (s *PluginSuite) TestFetchRegistrationEntry() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, - Hint: "external", + Hint: "external", }, }, } { @@ -2335,9 +2323,6 @@ func (s *PluginSuite) TestPruneRegistrationEntries() { SpiffeId: "SpiffeId", ParentId: "ParentId", X509SvidTtl: 1, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, EntryExpiry: now.Unix(), } @@ -3202,10 +3187,7 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { SpiffeId: "spiffe://example.org/foo", ParentId: "spiffe://example.org/bar", X509SvidTtl: 1, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, - JwtSvidTtl: 20, + JwtSvidTtl: 20, }) entry.X509SvidTtl = 11 @@ -3213,9 +3195,6 @@ func (s *PluginSuite) TestUpdateRegistrationEntry() { entry.Admin = true entry.Downstream = true entry.Hint = "internal" - entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - } updatedRegistrationEntry, err := s.ds.UpdateRegistrationEntry(ctx, entry, nil) s.Require().NoError(err) @@ -3281,33 +3260,37 @@ func (s *PluginSuite) TestUpdateRegistrationEntryWithMask() { // Note that most of the input validation is done in the API layer and has more extensive tests there. now := time.Now().Unix() oldEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/oldSpiffeId", - X509SvidTtl: 1000, - JwtSvidTtl: 3000, - Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, - FederatesWith: []string{"spiffe://dom1.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns1"}, - Downstream: false, - StoreSvid: false, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/oldSpiffeId", + X509SvidTtl: 1000, + JwtSvidTtl: 3000, + Selectors: []*common.Selector{{Type: "Type1", Value: "Value1"}}, + FederatesWith: []string{"spiffe://dom1.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns1"}, + Downstream: false, + StoreSvid: false, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, + }, } newEntry := &common.RegistrationEntry{ - ParentId: "spiffe://example.org/oldParentId", - SpiffeId: "spiffe://example.org/newSpiffeId", - X509SvidTtl: 4000, - JwtSvidTtl: 6000, - Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, - FederatesWith: []string{"spiffe://dom2.org"}, - Admin: false, - EntryExpiry: 1000, - DnsNames: []string{"dns2"}, - Downstream: false, - StoreSvid: true, - Hint: "internal", - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, + ParentId: "spiffe://example.org/oldParentId", + SpiffeId: "spiffe://example.org/newSpiffeId", + X509SvidTtl: 4000, + JwtSvidTtl: 6000, + Selectors: []*common.Selector{{Type: "Type2", Value: "Value2"}}, + FederatesWith: []string{"spiffe://dom2.org"}, + Admin: false, + EntryExpiry: 1000, + DnsNames: []string{"dns2"}, + Downstream: false, + StoreSvid: true, + Hint: "internal", + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, + }, } badEntry := &common.RegistrationEntry{ ParentId: "not a good parent id", diff --git a/pkg/server/datastore/sqlstore/testdata/entries.json b/pkg/server/datastore/sqlstore/testdata/entries.json index 16b1c449f1..0c925f0890 100644 --- a/pkg/server/datastore/sqlstore/testdata/entries.json +++ b/pkg/server/datastore/sqlstore/testdata/entries.json @@ -93,27 +93,5 @@ "spiffe_id": "spiffe://id5", "parent_id": "spiffe://parent2", "ttl": 200 - }, - { - "selectors": [ - { - "type": "b", - "value": "2" - }, - { - "type": "c", - "value": "3" - }, - { - "type": "d", - "value": "4" - } - ], - "spiffe_id": "spiffe://id6", - "parent_id": "spiffe://parent2", - "cache_hint_flags": { - "disable_x509_svid_prefetch": true - }, - "ttl": 200 } ] diff --git a/proto/spire/common/common.pb.go b/proto/spire/common/common.pb.go index 296ad6aaf6..5a7e8229eb 100644 --- a/proto/spire/common/common.pb.go +++ b/proto/spire/common/common.pb.go @@ -365,7 +365,7 @@ type RegistrationEntry struct { Hint string `protobuf:"bytes,14,opt,name=hint,proto3" json:"hint,omitempty"` // * Time of creation, in seconds from epoch CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - CacheHintFlags *RegistrationEntry_CacheHintFlags `protobuf:"bytes,16,opt,name=cache_hint_flags,json=cacheHintFlags,proto3" json:"cache_hint_flags,omitempty"` + CacheHintFlags *RegistrationEntry_CacheHintFlags `protobuf:"bytes,16,opt,name=cache_hint_flags,json=cacheHintFlags,proto3,oneof" json:"cache_hint_flags,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1147,7 +1147,7 @@ const file_spire_common_common_proto_rawDesc = "" + "\x16new_cert_serial_number\x18\x05 \x01(\tR\x13newCertSerialNumber\x12+\n" + "\x12new_cert_not_after\x18\x06 \x01(\x03R\x0fnewCertNotAfter\x124\n" + "\tselectors\x18\a \x03(\v2\x16.spire.common.SelectorR\tselectors\x12!\n" + - "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\xa4\x05\n" + + "\fcan_reattest\x18\b \x01(\bR\vcanReattest\"\xbe\x05\n" + "\x11RegistrationEntry\x124\n" + "\tselectors\x18\x01 \x03(\v2\x16.spire.common.SelectorR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\tR\bparentId\x12\x1b\n" + @@ -1169,10 +1169,11 @@ const file_spire_common_common_proto_rawDesc = "" + "jwtSvidTtl\x12\x12\n" + "\x04hint\x18\x0e \x01(\tR\x04hint\x12\x1d\n" + "\n" + - "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12X\n" + - "\x10cache_hint_flags\x18\x10 \x01(\v2..spire.common.RegistrationEntry.CacheHintFlagsR\x0ecacheHintFlags\x1aM\n" + + "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12]\n" + + "\x10cache_hint_flags\x18\x10 \x01(\v2..spire.common.RegistrationEntry.CacheHintFlagsH\x00R\x0ecacheHintFlags\x88\x01\x01\x1aM\n" + "\x0eCacheHintFlags\x12;\n" + - "\x1adisable_x509_svid_prefetch\x18\x01 \x01(\bR\x17disableX509SvidPrefetch\"\xc9\x03\n" + + "\x1adisable_x509_svid_prefetch\x18\x01 \x01(\bR\x17disableX509SvidPrefetchB\x13\n" + + "\x11_cache_hint_flags\"\xc9\x03\n" + "\x15RegistrationEntryMask\x12\x1c\n" + "\tselectors\x18\x01 \x01(\bR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\bR\bparentId\x12\x1b\n" + @@ -1276,6 +1277,7 @@ func file_spire_common_common_proto_init() { if File_spire_common_common_proto != nil { return } + file_spire_common_common_proto_msgTypes[5].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proto/spire/common/common.proto b/proto/spire/common/common.proto index 0e4ea90db5..9c7e7041ab 100644 --- a/proto/spire/common/common.proto +++ b/proto/spire/common/common.proto @@ -104,7 +104,7 @@ message RegistrationEntry { This can be set to `true` if the workload is unlikely to request an X509 SVID. */ bool disable_x509_svid_prefetch = 1; } - CacheHintFlags cache_hint_flags = 16; + optional CacheHintFlags cache_hint_flags = 16; } /** The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry */ From 1c93083cb5457019bf35d174104175eea52645ac Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 25 Nov 2025 22:57:27 +0000 Subject: [PATCH 04/51] removed unused linter directive Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 52f7c8340c..e606a5eac3 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -500,7 +500,7 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // return nil // } // - switch currVersion { //nolint: gocritic,revive // No upgrade required yet, keeping switch for future additions + switch currVersion { //nolint: revive // No upgrade required yet, keeping switch for future additions case 24: err = migrateToV24(tx) default: From 660f5d1a0157ce207c0ee029aa5424e434d07a9d Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 25 Nov 2025 23:05:04 +0000 Subject: [PATCH 05/51] undid drive-by formatting Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/testdata/entries.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/server/datastore/sqlstore/testdata/entries.json b/pkg/server/datastore/sqlstore/testdata/entries.json index 0c925f0890..b09f848515 100644 --- a/pkg/server/datastore/sqlstore/testdata/entries.json +++ b/pkg/server/datastore/sqlstore/testdata/entries.json @@ -17,10 +17,7 @@ "spiffe_id": "spiffe://id1", "parent_id": "spiffe://parent", "ttl": 200, - "dns_names": [ - "a", - "b" - ] + "dns_names": ["a", "b"] }, { "selectors": [ From 086383f0c55048c2dae1bc7b3c201fc28d441c26 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 25 Nov 2025 23:33:35 +0000 Subject: [PATCH 06/51] removed unused linter directive Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index e606a5eac3..c4a9b8166f 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -500,7 +500,7 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // return nil // } // - switch currVersion { //nolint: revive // No upgrade required yet, keeping switch for future additions + switch currVersion { case 24: err = migrateToV24(tx) default: From 3776a45cd67366a161d80626dee4521c3c3cefe7 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 25 Nov 2025 23:56:57 +0000 Subject: [PATCH 07/51] corrected check for missing value when mapping from model Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore.go | 2 +- pkg/server/datastore/sqlstore/sqlstore_test.go | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index 1c6586eb35..827e695074 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -4657,7 +4657,7 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry } CacheHintFlags := &common.RegistrationEntry_CacheHintFlags{} - if model.CacheHintFlags != nil { + if len(model.CacheHintFlags) != 0 { if err := proto.Unmarshal(model.CacheHintFlags, CacheHintFlags); err != nil { return nil, err } diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 5fa09f64dd..9176ec349d 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -3271,9 +3271,6 @@ func (s *PluginSuite) TestUpdateRegistrationEntryWithMask() { DnsNames: []string{"dns1"}, Downstream: false, StoreSvid: false, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, } newEntry := &common.RegistrationEntry{ ParentId: "spiffe://example.org/oldParentId", @@ -3288,9 +3285,6 @@ func (s *PluginSuite) TestUpdateRegistrationEntryWithMask() { Downstream: false, StoreSvid: true, Hint: "internal", - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{ - DisableX509SvidPrefetch: false, - }, } badEntry := &common.RegistrationEntry{ ParentId: "not a good parent id", From 8daf8d70f292d3aea0e9e0013a37f35d32be0034 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 26 Nov 2025 00:22:05 +0000 Subject: [PATCH 08/51] increment schema version to trigger migration Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index c4a9b8166f..fbdeeaad8b 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -270,7 +270,7 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 23 + latestSchemaVersion = 24 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned From 33440fccc7a336d5b815bfe012e1640efe3b0a6c Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 27 Nov 2025 22:49:03 +0000 Subject: [PATCH 09/51] added migration test Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 2 +- .../datastore/sqlstore/migration_test.go | 42 +++++++++++++++++++ .../datastore/sqlstore/sqlstore_test.go | 4 ++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index fbdeeaad8b..fbb3037d7f 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -501,7 +501,7 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { - case 24: + case 23: err = migrateToV24(tx) default: err = newSQLError("no migration support for unknown schema version %d", currVersion) diff --git a/pkg/server/datastore/sqlstore/migration_test.go b/pkg/server/datastore/sqlstore/migration_test.go index 9c0b11f64d..1d4e0b9bb6 100644 --- a/pkg/server/datastore/sqlstore/migration_test.go +++ b/pkg/server/datastore/sqlstore/migration_test.go @@ -57,6 +57,48 @@ var ( CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; COMMIT; `, + 24: ` + PRAGMA foreign_keys=OFF; + BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS "federated_registration_entries" ("bundle_id" integer,"registered_entry_id" integer, PRIMARY KEY ("bundle_id","registered_entry_id")); + CREATE TABLE IF NOT EXISTS "bundles" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"data" blob ); + INSERT INTO bundles VALUES(1,'2025-11-27 22:38:52.925885+00:00','2025-11-27 22:39:08.716042+00:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712df030adc03308201d83082015ea0030201020214449db4c88cda977653f4d5e4770aec9b4b1e970c300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3233303531353032303530365a170d3238303531333032303530365a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b8104002203620004f57073b72f16fdec785ebd117735018227bfa2475a51385e485d0f42f540693b1768fd49ef2bf40e195ac38e48ec2bfd1cfdb51ce98cc48959d177aab0e97db0ce47e7b1c1416bb46c83577f0e2375e1dd079be4d57c8dc81410c5e5294b1867a35d305b301d0603551d0e04160414928ae360c6aaa7cf6aff8d1716b0046aa61c10ff300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040368003065023100e7843c85f844778a95c9cc1b2cdcce9bf1d0ae9d67d7e6b6c5cf3c894d37e8530f6a7711d4f2ea82c3833df5b2b6d75102300a2287548b879888c6bdf88dab55b8fc80ec490059f484b2c4177403997b463e9011b3da82f8a6e29254eee45a6293641a85010a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004df7861b02a59e0afd752c0bfad8a11a6f0210289dccb1b58fcc85a92b1e3475b891f65d06df61feb1581452f1f7205d9cd2e30439c97dc0a7023d9caf8a63db812205a5577616d4764333178446e79614459704c70536e3449564b454b346a33545518fcc8a8c9061a85010a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004755093caa518ea29a86ce57ba78addcecbe041770b63690d16b67c92f4726f4790948ba153e23b563e8fcc4463bce2ae46e73d71bfb05d5e2583d00b5e947a2512205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c188cc9a8c9062802'); + CREATE TABLE IF NOT EXISTS "attested_node_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"data_type" varchar(255),"serial_number" varchar(255),"expires_at" datetime,"new_serial_number" varchar(255),"new_expires_at" datetime,"can_reattest" bool ); + CREATE TABLE IF NOT EXISTS "attested_node_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "node_resolver_map_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "registered_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255),"spiffe_id" varchar(255),"parent_id" varchar(255),"ttl" integer,"admin" bool,"downstream" bool,"expiry" bigint,"revision_number" bigint,"store_svid" bool,"hint" varchar(255),"jwt_svid_ttl" integer,"cache_hint_flags" blob ); + CREATE TABLE IF NOT EXISTS "registered_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "join_tokens" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"token" varchar(255),"expiry" bigint ); + CREATE TABLE IF NOT EXISTS "selectors" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer,"code_version" varchar(255) ); + INSERT INTO migrations VALUES(1,'2025-11-27 22:38:52.916517+00:00','2025-11-27 22:38:52.916517+00:00',24,'1.14.0-dev-unk'); + CREATE TABLE IF NOT EXISTS "dns_names" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "federated_trust_domains" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"bundle_endpoint_url" varchar(255),"bundle_endpoint_profile" varchar(255),"endpoint_spiffe_id" varchar(255),"implicit" bool ); + CREATE TABLE IF NOT EXISTS "ca_journals" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"data" blob,"active_x509_authority_id" varchar(255),"active_jwt_authority_id" varchar(255) ); + INSERT INTO ca_journals VALUES(1,'2025-11-27 22:38:52.926142+00:00','2025-11-27 22:38:52.927057+00:00',X'0a97090a014110fca5a3c9061a96043082021230820198a00302010202104b7c57cdd409f4e4beff2fd3841f0d49300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333834325a170d3235313132383232333835325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273236323533333634313632333534323032313737303238343631393534373635303838353432363059301306072a8648ce3d020106082a8648ce3d03010703420004a504f26372e87d4952379b970a41048c7ab40e378599a3da1cd54386c90ffd9e1bc583d9c1a2b0931f3bfb552bfb4c6d01a54cbed0bf5c218ee9a936ebfdf82ca38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414c7119341a904cddfb8bbedd37c099f22aa40337b301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023014490296e27bc6d879103906d5d01bbdb63cbc6263e7d583713cb9fa8dde7f0188f7cba8e29b2130f1d8d6d56c739beb023100dbefdaf5f8d78b34d63e97843316aca3d1996a372911c38d73ef2f19a9dc4f555d4427c49c089d17d677a9a2aac884742296043082021230820198a00302010202104b7c57cdd409f4e4beff2fd3841f0d49300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333834325a170d3235313132383232333835325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273236323533333634313632333534323032313737303238343631393534373635303838353432363059301306072a8648ce3d020106082a8648ce3d03010703420004a504f26372e87d4952379b970a41048c7ab40e378599a3da1cd54386c90ffd9e1bc583d9c1a2b0931f3bfb552bfb4c6d01a54cbed0bf5c218ee9a936ebfdf82ca38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414c7119341a904cddfb8bbedd37c099f22aa40337b301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023014490296e27bc6d879103906d5d01bbdb63cbc6263e7d583713cb9fa8dde7f0188f7cba8e29b2130f1d8d6d56c739beb023100dbefdaf5f8d78b34d63e97843316aca3d1996a372911c38d73ef2f19a9dc4f555d4427c49c089d17d677a9a2aac88474280332286337313139333431613930346364646662386262656464333763303939663232616134303333376238fcc8a8c90642283932386165333630633661616137636636616666386431373136623030343661613631633130666612b2010a014110fca5a3c90618fcc8a8c90622205a5577616d4764333178446e79614459704c70536e3449564b454b346a3354552a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004df7861b02a59e0afd752c0bfad8a11a6f0210289dccb1b58fcc85a92b1e3475b891f65d06df61feb1581452f1f7205d9cd2e30439c97dc0a7023d9caf8a63db830033a205a5577616d4764333178446e79614459704c70536e3449564b454b346a335455','c7119341a904cddfb8bbedd37c099f22aa40337b',''); + INSERT INTO ca_journals VALUES(2,'2025-11-27 22:39:08.714878+00:00','2025-11-27 22:39:08.716313+00:00',X'0a97090a0141108ca6a3c9061a96043082021230820199a003020102021100eb4caf13fe59fe74ab456093810bb241300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333835385a170d3235313132383232333930385a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273130313330343837333231343837323132343839353738393230303936333036303430373638363059301306072a8648ce3d020106082a8648ce3d0301070342000497a28fef0c85936ff3e2451c5341e1176ea41a5f8f2a412b3bd89dbf24b90600f6e6cdb8193363105f711e0f11d2a1ac563d89234500bf2fd5ddf1689707560aa38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d34d1428b3efa1383b80bcb2ae752f26eda6e8b9301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d04030303670030640230477b1b9dab150250286f216ccf018f92fc2cd8ffc560678e9392d2f0e3a44797e9b483f101b1b9d81251dc06aec1bc8602305c045d243451d6966673dfe67bf1c7089b62b1baf1625b8bcfc4de637d34e94edafcd5add0c72acb6032215b933ffb8e2296043082021230820199a003020102021100eb4caf13fe59fe74ab456093810bb241300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333835385a170d3235313132383232333930385a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273130313330343837333231343837323132343839353738393230303936333036303430373638363059301306072a8648ce3d020106082a8648ce3d0301070342000497a28fef0c85936ff3e2451c5341e1176ea41a5f8f2a412b3bd89dbf24b90600f6e6cdb8193363105f711e0f11d2a1ac563d89234500bf2fd5ddf1689707560aa38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d34d1428b3efa1383b80bcb2ae752f26eda6e8b9301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d04030303670030640230477b1b9dab150250286f216ccf018f92fc2cd8ffc560678e9392d2f0e3a44797e9b483f101b1b9d81251dc06aec1bc8602305c045d243451d6966673dfe67bf1c7089b62b1baf1625b8bcfc4de637d34e94edafcd5add0c72acb6032215b933ffb8e2803322864333464313432386233656661313338336238306263623261653735326632366564613665386239388cc9a8c90642283932386165333630633661616137636636616666386431373136623030343661613631633130666612b2010a0141108ca6a3c906188cc9a8c90622205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c2a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004755093caa518ea29a86ce57ba78addcecbe041770b63690d16b67c92f4726f4790948ba153e23b563e8fcc4463bce2ae46e73d71bfb05d5e2583d00b5e947a2530033a205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c','d34d1428b3efa1383b80bcb2ae752f26eda6e8b9',''); + DELETE FROM sqlite_sequence; + INSERT INTO sqlite_sequence VALUES('migrations',1); + INSERT INTO sqlite_sequence VALUES('bundles',1); + INSERT INTO sqlite_sequence VALUES('ca_journals',2); + CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ; + CREATE INDEX idx_attested_node_entries_expires_at ON "attested_node_entries"(expires_at) ; + CREATE UNIQUE INDEX uix_attested_node_entries_spiffe_id ON "attested_node_entries"(spiffe_id) ; + CREATE UNIQUE INDEX idx_node_resolver_map ON "node_resolver_map_entries"(spiffe_id, "type", "value") ; + CREATE INDEX idx_registered_entries_spiffe_id ON "registered_entries"(spiffe_id) ; + CREATE INDEX idx_registered_entries_parent_id ON "registered_entries"(parent_id) ; + CREATE INDEX idx_registered_entries_expiry ON "registered_entries"("expiry") ; + CREATE INDEX idx_registered_entries_hint ON "registered_entries"("hint") ; + CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ; + CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ; + CREATE INDEX idx_selectors_type_value ON "selectors"("type", "value") ; + CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ; + CREATE UNIQUE INDEX idx_dns_entry ON "dns_names"(registered_entry_id, "value") ; + CREATE UNIQUE INDEX uix_federated_trust_domains_trust_domain ON "federated_trust_domains"(trust_domain) ; + CREATE INDEX idx_ca_journals_active_x509_authority_id ON "ca_journals"(active_x509_authority_id) ; + CREATE INDEX idx_ca_journals_active_jwt_authority_id ON "ca_journals"(active_jwt_authority_id) ; + CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; + COMMIT;`, } ) diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 9176ec349d..5a728b8448 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5213,6 +5213,10 @@ func (s *PluginSuite) TestMigration() { // of SPIRE server and no longer have migration code. case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22: prepareDB(false) + case 23: + prepareDB(true) + case 24: + prepareDB(true) default: t.Fatalf("no migration test added for schema version %d", schemaVersion) } From 2fb8b6db4b63086275d58537ad0bc8452ce7c98c Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 27 Nov 2025 23:10:23 +0000 Subject: [PATCH 10/51] added test data entries Signed-off-by: Valentin Fadeev --- .../testdata/invalid_registration_entries.json | 13 +++++++++++++ .../testdata/valid_registration_entries.json | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json b/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json index dc357fde3f..6829a36f53 100644 --- a/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json +++ b/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json @@ -63,5 +63,18 @@ ], "spiffe_id": "SpiffeId5" }, + { + "selectors": [ + { + "type": "Type", + "value": "Value" + } + ], + "spiffe_id": "SpiffeId1", + "store_svid": true, + "cache_hint_flags": { + "disable_x509_svid_prefetch": true + } + }, null ] diff --git a/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json b/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json index da10f93a0c..f512b685eb 100644 --- a/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json +++ b/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json @@ -53,5 +53,17 @@ } ], "spiffe_id": "SpiffeId4" + }, + { + "selectors": [ + { + "type": "Type1", + "value": "Value3" + } + ], + "spiffe_id": "SpiffeId1", + "cache_hint_flags": { + "disable_x509_svid_prefetch": true + } } ] From ac9b01f3f0660c1afc6f9d9fefd65cea0bab97f0 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 27 Nov 2025 23:21:46 +0000 Subject: [PATCH 11/51] updated test helper Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 5a728b8448..0df62dccd5 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -2454,11 +2454,12 @@ func (s *PluginSuite) testListRegistrationEntries(dataConsistency datastore.Data makeEntry := func(parentIDSuffix, spiffeIDSuffix, hint string, selectors ...string) *common.RegistrationEntry { return &common.RegistrationEntry{ - EntryId: fmt.Sprintf("%s%s%s", parentIDSuffix, spiffeIDSuffix, strings.Join(selectors, "")), - ParentId: makeID(parentIDSuffix), - SpiffeId: makeID(spiffeIDSuffix), - Selectors: makeSelectors(selectors...), - Hint: hint, + EntryId: fmt.Sprintf("%s%s%s", parentIDSuffix, spiffeIDSuffix, strings.Join(selectors, "")), + ParentId: makeID(parentIDSuffix), + SpiffeId: makeID(spiffeIDSuffix), + Selectors: makeSelectors(selectors...), + Hint: hint, + CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, } } From ab0b8e93aa469a08e54b1b3b7da327588b7fdcf4 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 30 Nov 2025 21:31:02 +0000 Subject: [PATCH 12/51] conditionally populate cache hint flags from row Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore.go | 8 +++++--- pkg/server/datastore/sqlstore/sqlstore_test.go | 11 +++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index 827e695074..02f946bd9a 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -3961,9 +3961,11 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { } if r.CacheHintFlags.Valid { - entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} - if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { - return newSQLError("invalid value for X.509 cache hint: %s", err) + if len(r.CacheHintFlags.V) > 0 { + entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} + if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { + return newSQLError("invalid value for X.509 cache hint: %s", err) + } } } diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 0df62dccd5..5a728b8448 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -2454,12 +2454,11 @@ func (s *PluginSuite) testListRegistrationEntries(dataConsistency datastore.Data makeEntry := func(parentIDSuffix, spiffeIDSuffix, hint string, selectors ...string) *common.RegistrationEntry { return &common.RegistrationEntry{ - EntryId: fmt.Sprintf("%s%s%s", parentIDSuffix, spiffeIDSuffix, strings.Join(selectors, "")), - ParentId: makeID(parentIDSuffix), - SpiffeId: makeID(spiffeIDSuffix), - Selectors: makeSelectors(selectors...), - Hint: hint, - CacheHintFlags: &common.RegistrationEntry_CacheHintFlags{}, + EntryId: fmt.Sprintf("%s%s%s", parentIDSuffix, spiffeIDSuffix, strings.Join(selectors, "")), + ParentId: makeID(parentIDSuffix), + SpiffeId: makeID(spiffeIDSuffix), + Selectors: makeSelectors(selectors...), + Hint: hint, } } From 88fa1db4dd944736b7dfc0a97c055e7b385ec754 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 6 Jan 2026 23:13:49 +0000 Subject: [PATCH 13/51] applied suggestion to error message Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index 02f946bd9a..0fb8fdd60b 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -3964,7 +3964,7 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { if len(r.CacheHintFlags.V) > 0 { entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { - return newSQLError("invalid value for X.509 cache hint: %s", err) + return newSQLError("could not parse cache hint flags: %s", err) } } } From a46cb8144d34e25fc7bb7cdae85b3f4de682ea25 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 8 Jan 2026 23:45:00 +0000 Subject: [PATCH 14/51] undid the change to fetchEntries Signed-off-by: Valentin Fadeev --- pkg/agent/manager/sync.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/agent/manager/sync.go b/pkg/agent/manager/sync.go index dd4355d5d7..84ede9ab08 100644 --- a/pkg/agent/manager/sync.go +++ b/pkg/agent/manager/sync.go @@ -345,9 +345,7 @@ func (m *manager) fetchEntries(ctx context.Context) (_ *cache.UpdateEntries, _ * case entry.StoreSvid: storeEntries[entryID] = entry default: - if !entry.CacheHintFlags.GetDisableX509SvidPrefetch() { - cacheEntries[entryID] = entry - } + cacheEntries[entryID] = entry } } From 16ad462e8361e7ae98b3c3a80835fb5c09724427 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 21 Jan 2026 23:26:52 +0000 Subject: [PATCH 15/51] incremented schema version for migration Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 15 +++++---------- pkg/server/datastore/sqlstore/sqlstore_test.go | 9 ++------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index b87f5cf19c..3d3f5f4742 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -273,12 +273,12 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 24 + latestSchemaVersion = 25 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 23 + lastMinorReleaseSchemaVersion = 24 ) // the current code version @@ -504,8 +504,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { - case 23: - err = migrateToV24(tx) + case 24: + err = migrateToV25(tx) default: err = newSQLError("no migration support for unknown schema version %d", currVersion) } @@ -516,13 +516,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi return nextVersion, nil } -func migrateToV24(tx *gorm.DB) error { -<<<<<<< HEAD +func migrateToV25(tx *gorm.DB) error { if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { -======= - // Add agent_version column to attested_node_entries table - if err := tx.AutoMigrate(&AttestedNode{}).Error; err != nil { ->>>>>>> main return newWrappedSQLError(err) } return nil diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index fe4ccd55b6..6ee4233303 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5236,15 +5236,10 @@ func (s *PluginSuite) TestMigration() { switch schemaVersion { // All of these schema versions were migrated by previous versions // of SPIRE server and no longer have migration code. - case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22: + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 34: prepareDB(false) - case 23: -<<<<<<< HEAD - prepareDB(true) case 24: -======= - // Migration from v23 to v24 adds agent_version column ->>>>>>> main + // Migration from v24 to v25 adds agent_version column prepareDB(true) default: t.Fatalf("no migration test added for schema version %d", schemaVersion) From 921edcfa0603c21d8fd8f5eef7de2f42c8a94def Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 00:12:17 +0000 Subject: [PATCH 16/51] corrected typo in version Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 6ee4233303..ccfd6266df 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5236,7 +5236,7 @@ func (s *PluginSuite) TestMigration() { switch schemaVersion { // All of these schema versions were migrated by previous versions // of SPIRE server and no longer have migration code. - case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 34: + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23: prepareDB(false) case 24: // Migration from v24 to v25 adds agent_version column From 13e0d309fb22c7acb472d10eff0b0ecc53f4e50b Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 00:57:08 +0000 Subject: [PATCH 17/51] excluded records from cache Signed-off-by: Valentin Fadeev --- pkg/agent/manager/cache/lru_cache.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/agent/manager/cache/lru_cache.go b/pkg/agent/manager/cache/lru_cache.go index e66e475db6..3928182130 100644 --- a/pkg/agent/manager/cache/lru_cache.go +++ b/pkg/agent/manager/cache/lru_cache.go @@ -779,13 +779,15 @@ func (c *LRUCache) syncSVIDsWithSubscribers() (map[string]struct{}, []recordAcce remainderSize := c.x509SvidCacheMaxSize - len(c.svids) // add records which are not cached for remainder of cache size - for id := range c.records { + for id, record := range c.records { if len(c.staleEntries) >= remainderSize { break } - if _, svidCached := c.svids[id]; !svidCached { - if _, ok := c.staleEntries[id]; !ok { - c.staleEntries[id] = true + if !record.entry.CacheHintFlags.DisableX509SvidPrefetch { + if _, svidCached := c.svids[id]; !svidCached { + if _, ok := c.staleEntries[id]; !ok { + c.staleEntries[id] = true + } } } } From 19935b9ea47c5206afaf0079d02acd839a01c682 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 17:26:13 +0000 Subject: [PATCH 18/51] updated migrations and tests Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 12 +++--- .../datastore/sqlstore/migration_test.go | 42 ------------------- .../datastore/sqlstore/sqlstore_test.go | 6 +-- 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 3d3f5f4742..7abd5e6963 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -273,12 +273,12 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 25 + latestSchemaVersion = 24 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 24 + lastMinorReleaseSchemaVersion = 23 ) // the current code version @@ -504,8 +504,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { - case 24: - err = migrateToV25(tx) + case 23: + err = migrateToV24(tx) default: err = newSQLError("no migration support for unknown schema version %d", currVersion) } @@ -516,8 +516,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi return nextVersion, nil } -func migrateToV25(tx *gorm.DB) error { - if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { +func migrateToV24(tx *gorm.DB) error { + if err := tx.AutoMigrate(&AttestedNode{}, &RegisteredEntry{}).Error; err != nil { return newWrappedSQLError(err) } return nil diff --git a/pkg/server/datastore/sqlstore/migration_test.go b/pkg/server/datastore/sqlstore/migration_test.go index 1d4e0b9bb6..9c0b11f64d 100644 --- a/pkg/server/datastore/sqlstore/migration_test.go +++ b/pkg/server/datastore/sqlstore/migration_test.go @@ -57,48 +57,6 @@ var ( CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; COMMIT; `, - 24: ` - PRAGMA foreign_keys=OFF; - BEGIN TRANSACTION; - CREATE TABLE IF NOT EXISTS "federated_registration_entries" ("bundle_id" integer,"registered_entry_id" integer, PRIMARY KEY ("bundle_id","registered_entry_id")); - CREATE TABLE IF NOT EXISTS "bundles" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"data" blob ); - INSERT INTO bundles VALUES(1,'2025-11-27 22:38:52.925885+00:00','2025-11-27 22:39:08.716042+00:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712df030adc03308201d83082015ea0030201020214449db4c88cda977653f4d5e4770aec9b4b1e970c300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3233303531353032303530365a170d3238303531333032303530365a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b8104002203620004f57073b72f16fdec785ebd117735018227bfa2475a51385e485d0f42f540693b1768fd49ef2bf40e195ac38e48ec2bfd1cfdb51ce98cc48959d177aab0e97db0ce47e7b1c1416bb46c83577f0e2375e1dd079be4d57c8dc81410c5e5294b1867a35d305b301d0603551d0e04160414928ae360c6aaa7cf6aff8d1716b0046aa61c10ff300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040368003065023100e7843c85f844778a95c9cc1b2cdcce9bf1d0ae9d67d7e6b6c5cf3c894d37e8530f6a7711d4f2ea82c3833df5b2b6d75102300a2287548b879888c6bdf88dab55b8fc80ec490059f484b2c4177403997b463e9011b3da82f8a6e29254eee45a6293641a85010a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004df7861b02a59e0afd752c0bfad8a11a6f0210289dccb1b58fcc85a92b1e3475b891f65d06df61feb1581452f1f7205d9cd2e30439c97dc0a7023d9caf8a63db812205a5577616d4764333178446e79614459704c70536e3449564b454b346a33545518fcc8a8c9061a85010a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004755093caa518ea29a86ce57ba78addcecbe041770b63690d16b67c92f4726f4790948ba153e23b563e8fcc4463bce2ae46e73d71bfb05d5e2583d00b5e947a2512205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c188cc9a8c9062802'); - CREATE TABLE IF NOT EXISTS "attested_node_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"data_type" varchar(255),"serial_number" varchar(255),"expires_at" datetime,"new_serial_number" varchar(255),"new_expires_at" datetime,"can_reattest" bool ); - CREATE TABLE IF NOT EXISTS "attested_node_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255) ); - CREATE TABLE IF NOT EXISTS "node_resolver_map_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"type" varchar(255),"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "registered_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255),"spiffe_id" varchar(255),"parent_id" varchar(255),"ttl" integer,"admin" bool,"downstream" bool,"expiry" bigint,"revision_number" bigint,"store_svid" bool,"hint" varchar(255),"jwt_svid_ttl" integer,"cache_hint_flags" blob ); - CREATE TABLE IF NOT EXISTS "registered_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255) ); - CREATE TABLE IF NOT EXISTS "join_tokens" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"token" varchar(255),"expiry" bigint ); - CREATE TABLE IF NOT EXISTS "selectors" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"type" varchar(255),"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer,"code_version" varchar(255) ); - INSERT INTO migrations VALUES(1,'2025-11-27 22:38:52.916517+00:00','2025-11-27 22:38:52.916517+00:00',24,'1.14.0-dev-unk'); - CREATE TABLE IF NOT EXISTS "dns_names" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "federated_trust_domains" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"bundle_endpoint_url" varchar(255),"bundle_endpoint_profile" varchar(255),"endpoint_spiffe_id" varchar(255),"implicit" bool ); - CREATE TABLE IF NOT EXISTS "ca_journals" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"data" blob,"active_x509_authority_id" varchar(255),"active_jwt_authority_id" varchar(255) ); - INSERT INTO ca_journals VALUES(1,'2025-11-27 22:38:52.926142+00:00','2025-11-27 22:38:52.927057+00:00',X'0a97090a014110fca5a3c9061a96043082021230820198a00302010202104b7c57cdd409f4e4beff2fd3841f0d49300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333834325a170d3235313132383232333835325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273236323533333634313632333534323032313737303238343631393534373635303838353432363059301306072a8648ce3d020106082a8648ce3d03010703420004a504f26372e87d4952379b970a41048c7ab40e378599a3da1cd54386c90ffd9e1bc583d9c1a2b0931f3bfb552bfb4c6d01a54cbed0bf5c218ee9a936ebfdf82ca38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414c7119341a904cddfb8bbedd37c099f22aa40337b301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023014490296e27bc6d879103906d5d01bbdb63cbc6263e7d583713cb9fa8dde7f0188f7cba8e29b2130f1d8d6d56c739beb023100dbefdaf5f8d78b34d63e97843316aca3d1996a372911c38d73ef2f19a9dc4f555d4427c49c089d17d677a9a2aac884742296043082021230820198a00302010202104b7c57cdd409f4e4beff2fd3841f0d49300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333834325a170d3235313132383232333835325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273236323533333634313632333534323032313737303238343631393534373635303838353432363059301306072a8648ce3d020106082a8648ce3d03010703420004a504f26372e87d4952379b970a41048c7ab40e378599a3da1cd54386c90ffd9e1bc583d9c1a2b0931f3bfb552bfb4c6d01a54cbed0bf5c218ee9a936ebfdf82ca38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414c7119341a904cddfb8bbedd37c099f22aa40337b301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023014490296e27bc6d879103906d5d01bbdb63cbc6263e7d583713cb9fa8dde7f0188f7cba8e29b2130f1d8d6d56c739beb023100dbefdaf5f8d78b34d63e97843316aca3d1996a372911c38d73ef2f19a9dc4f555d4427c49c089d17d677a9a2aac88474280332286337313139333431613930346364646662386262656464333763303939663232616134303333376238fcc8a8c90642283932386165333630633661616137636636616666386431373136623030343661613631633130666612b2010a014110fca5a3c90618fcc8a8c90622205a5577616d4764333178446e79614459704c70536e3449564b454b346a3354552a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004df7861b02a59e0afd752c0bfad8a11a6f0210289dccb1b58fcc85a92b1e3475b891f65d06df61feb1581452f1f7205d9cd2e30439c97dc0a7023d9caf8a63db830033a205a5577616d4764333178446e79614459704c70536e3449564b454b346a335455','c7119341a904cddfb8bbedd37c099f22aa40337b',''); - INSERT INTO ca_journals VALUES(2,'2025-11-27 22:39:08.714878+00:00','2025-11-27 22:39:08.716313+00:00',X'0a97090a0141108ca6a3c9061a96043082021230820199a003020102021100eb4caf13fe59fe74ab456093810bb241300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333835385a170d3235313132383232333930385a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273130313330343837333231343837323132343839353738393230303936333036303430373638363059301306072a8648ce3d020106082a8648ce3d0301070342000497a28fef0c85936ff3e2451c5341e1176ea41a5f8f2a412b3bd89dbf24b90600f6e6cdb8193363105f711e0f11d2a1ac563d89234500bf2fd5ddf1689707560aa38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d34d1428b3efa1383b80bcb2ae752f26eda6e8b9301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d04030303670030640230477b1b9dab150250286f216ccf018f92fc2cd8ffc560678e9392d2f0e3a44797e9b483f101b1b9d81251dc06aec1bc8602305c045d243451d6966673dfe67bf1c7089b62b1baf1625b8bcfc4de637d34e94edafcd5add0c72acb6032215b933ffb8e2296043082021230820199a003020102021100eb4caf13fe59fe74ab456093810bb241300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3235313132373232333835385a170d3235313132383232333930385a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273130313330343837333231343837323132343839353738393230303936333036303430373638363059301306072a8648ce3d020106082a8648ce3d0301070342000497a28fef0c85936ff3e2451c5341e1176ea41a5f8f2a412b3bd89dbf24b90600f6e6cdb8193363105f711e0f11d2a1ac563d89234500bf2fd5ddf1689707560aa38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d34d1428b3efa1383b80bcb2ae752f26eda6e8b9301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d04030303670030640230477b1b9dab150250286f216ccf018f92fc2cd8ffc560678e9392d2f0e3a44797e9b483f101b1b9d81251dc06aec1bc8602305c045d243451d6966673dfe67bf1c7089b62b1baf1625b8bcfc4de637d34e94edafcd5add0c72acb6032215b933ffb8e2803322864333464313432386233656661313338336238306263623261653735326632366564613665386239388cc9a8c90642283932386165333630633661616137636636616666386431373136623030343661613631633130666612b2010a0141108ca6a3c906188cc9a8c90622205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c2a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004755093caa518ea29a86ce57ba78addcecbe041770b63690d16b67c92f4726f4790948ba153e23b563e8fcc4463bce2ae46e73d71bfb05d5e2583d00b5e947a2530033a205a6a67344b4873736144585950376233445a446b5341744f627a456b476c7a6c','d34d1428b3efa1383b80bcb2ae752f26eda6e8b9',''); - DELETE FROM sqlite_sequence; - INSERT INTO sqlite_sequence VALUES('migrations',1); - INSERT INTO sqlite_sequence VALUES('bundles',1); - INSERT INTO sqlite_sequence VALUES('ca_journals',2); - CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ; - CREATE INDEX idx_attested_node_entries_expires_at ON "attested_node_entries"(expires_at) ; - CREATE UNIQUE INDEX uix_attested_node_entries_spiffe_id ON "attested_node_entries"(spiffe_id) ; - CREATE UNIQUE INDEX idx_node_resolver_map ON "node_resolver_map_entries"(spiffe_id, "type", "value") ; - CREATE INDEX idx_registered_entries_spiffe_id ON "registered_entries"(spiffe_id) ; - CREATE INDEX idx_registered_entries_parent_id ON "registered_entries"(parent_id) ; - CREATE INDEX idx_registered_entries_expiry ON "registered_entries"("expiry") ; - CREATE INDEX idx_registered_entries_hint ON "registered_entries"("hint") ; - CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ; - CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ; - CREATE INDEX idx_selectors_type_value ON "selectors"("type", "value") ; - CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ; - CREATE UNIQUE INDEX idx_dns_entry ON "dns_names"(registered_entry_id, "value") ; - CREATE UNIQUE INDEX uix_federated_trust_domains_trust_domain ON "federated_trust_domains"(trust_domain) ; - CREATE INDEX idx_ca_journals_active_x509_authority_id ON "ca_journals"(active_x509_authority_id) ; - CREATE INDEX idx_ca_journals_active_jwt_authority_id ON "ca_journals"(active_jwt_authority_id) ; - CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; - COMMIT;`, } ) diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index ccfd6266df..cdbf3022d7 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5236,10 +5236,10 @@ func (s *PluginSuite) TestMigration() { switch schemaVersion { // All of these schema versions were migrated by previous versions // of SPIRE server and no longer have migration code. - case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23: + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22: prepareDB(false) - case 24: - // Migration from v24 to v25 adds agent_version column + case 23: + // Migration from v23 to v24 adds agent_version column prepareDB(true) default: t.Fatalf("no migration test added for schema version %d", schemaVersion) From c4508f2730910df2116707a4223da0990b641513 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 22:52:51 +0000 Subject: [PATCH 19/51] increased size of the blob Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/datastore/sqlstore/models.go b/pkg/server/datastore/sqlstore/models.go index 816b47c16f..6f1aa4aa7e 100644 --- a/pkg/server/datastore/sqlstore/models.go +++ b/pkg/server/datastore/sqlstore/models.go @@ -103,7 +103,7 @@ type RegisteredEntry struct { // CacheHintFlags contains a set of options and flags that inform the // agent behaviour with respect to pre-fetching and refreshing X509 SVIDs - CacheHintFlags []byte `gorm:"size:255,column:cache_hint_flags"` // corresponds to TINYBLOB in MySQL + CacheHintFlags []byte `gorm:"size:65535,column:cache_hint_flags"` } // RegisteredEntryEvent holds the entry id of a registered entry that had an event From 478a81fdc068e9c3628564f40f6930f9b69f64e7 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 23:00:52 +0000 Subject: [PATCH 20/51] checked if hints exist Signed-off-by: Valentin Fadeev --- pkg/agent/manager/cache/lru_cache.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/agent/manager/cache/lru_cache.go b/pkg/agent/manager/cache/lru_cache.go index 3928182130..f2ee99449d 100644 --- a/pkg/agent/manager/cache/lru_cache.go +++ b/pkg/agent/manager/cache/lru_cache.go @@ -783,11 +783,12 @@ func (c *LRUCache) syncSVIDsWithSubscribers() (map[string]struct{}, []recordAcce if len(c.staleEntries) >= remainderSize { break } - if !record.entry.CacheHintFlags.DisableX509SvidPrefetch { - if _, svidCached := c.svids[id]; !svidCached { - if _, ok := c.staleEntries[id]; !ok { - c.staleEntries[id] = true - } + if record.entry.GetCacheHintFlags() != nil && record.entry.CacheHintFlags.DisableX509SvidPrefetch { + continue + } + if _, svidCached := c.svids[id]; !svidCached { + if _, ok := c.staleEntries[id]; !ok { + c.staleEntries[id] = true } } } From 4caec666a7c317ae5776c066a6ddb8340835009d Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 22 Jan 2026 23:27:11 +0000 Subject: [PATCH 21/51] added unit test for DisableX509SvidPrefetch Signed-off-by: Valentin Fadeev --- pkg/agent/manager/cache/lru_cache_test.go | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pkg/agent/manager/cache/lru_cache_test.go b/pkg/agent/manager/cache/lru_cache_test.go index c251dd415c..cf87cee684 100644 --- a/pkg/agent/manager/cache/lru_cache_test.go +++ b/pkg/agent/manager/cache/lru_cache_test.go @@ -554,11 +554,42 @@ func TestLRUCacheCheckSVIDCallback(t *testing.T) { assert.Equal(t, map[string]bool{foo.EntryId: true}, cache.staleEntries) } -func TestLRUCacheGetStaleEntries(t *testing.T) { +func TestLRUCacheGetStaleEntriesDisablePrefetch(t *testing.T) { cache := newTestLRUCache(t) + foo := makeRegistrationEntryWithTTL("FOO", 130, 140, "F") + fooCacheHintFlags := &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: true, + } + foo.CacheHintFlags = fooCacheHintFlags + bar := makeRegistrationEntryWithTTL("BAR", 130, 140, "B") + barCacheHintFlags := &common.RegistrationEntry_CacheHintFlags{ + DisableX509SvidPrefetch: false, + } + bar.CacheHintFlags = barCacheHintFlags + + qux := makeRegistrationEntryWithTTL("QUX", 130, 140, "Q") + // Create entry but don't mark it stale from checkSVID method; + // it will be marked stale because it does not have SVID cached + cache.UpdateEntries(&UpdateEntries{ + Bundles: makeBundles(bundleV2), + RegistrationEntries: makeRegistrationEntries(foo, bar, qux), + }, func(existingEntry, newEntry *common.RegistrationEntry, svid *X509SVID) bool { + return false + }) + + // Assert that the entry is returned as stale. The `ExpiresAt` field should be unset since there is no SVID. + expectedEntries := []*StaleEntry{ + {Entry: cache.records[bar.EntryId].entry}, + {Entry: cache.records[qux.EntryId].entry}, + } + assert.Equal(t, expectedEntries, cache.GetStaleEntries()) +} +func TestLRUCacheGetStaleEntries(t *testing.T) { + cache := newTestLRUCache(t) + bar := makeRegistrationEntryWithTTL("BAR", 130, 140, "B") // Create entry but don't mark it stale from checkSVID method; // it will be marked stale because it does not have SVID cached cache.UpdateEntries(&UpdateEntries{ From 7f1e2b41e34b2812bca30a825ab04082ef0e863b Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 25 Jan 2026 19:07:32 +0000 Subject: [PATCH 22/51] renamed new field to additional attributes Signed-off-by: Valentin Fadeev --- pkg/agent/manager/cache/lru_cache.go | 2 +- pkg/agent/manager/cache/lru_cache_test.go | 8 +- pkg/server/datastore/sqlstore/models.go | 4 +- pkg/server/datastore/sqlstore/sqlstore.go | 138 +++++++++--------- .../invalid_registration_entries.json | 2 +- .../testdata/valid_registration_entries.json | 2 +- proto/spire/common/common.pb.go | 114 +++++++-------- proto/spire/common/common.proto | 6 +- 8 files changed, 138 insertions(+), 138 deletions(-) diff --git a/pkg/agent/manager/cache/lru_cache.go b/pkg/agent/manager/cache/lru_cache.go index f2ee99449d..b2f879922f 100644 --- a/pkg/agent/manager/cache/lru_cache.go +++ b/pkg/agent/manager/cache/lru_cache.go @@ -783,7 +783,7 @@ func (c *LRUCache) syncSVIDsWithSubscribers() (map[string]struct{}, []recordAcce if len(c.staleEntries) >= remainderSize { break } - if record.entry.GetCacheHintFlags() != nil && record.entry.CacheHintFlags.DisableX509SvidPrefetch { + if record.entry.GetAdditionalAttributes() != nil && record.entry.AdditionalAttributes.DisableX509SvidPrefetch { continue } if _, svidCached := c.svids[id]; !svidCached { diff --git a/pkg/agent/manager/cache/lru_cache_test.go b/pkg/agent/manager/cache/lru_cache_test.go index cf87cee684..a08c83802b 100644 --- a/pkg/agent/manager/cache/lru_cache_test.go +++ b/pkg/agent/manager/cache/lru_cache_test.go @@ -558,16 +558,16 @@ func TestLRUCacheGetStaleEntriesDisablePrefetch(t *testing.T) { cache := newTestLRUCache(t) foo := makeRegistrationEntryWithTTL("FOO", 130, 140, "F") - fooCacheHintFlags := &common.RegistrationEntry_CacheHintFlags{ + fooAdditionalAttributes := &common.RegistrationEntry_AdditionalAttributes{ DisableX509SvidPrefetch: true, } - foo.CacheHintFlags = fooCacheHintFlags + foo.AdditionalAttributes = fooAdditionalAttributes bar := makeRegistrationEntryWithTTL("BAR", 130, 140, "B") - barCacheHintFlags := &common.RegistrationEntry_CacheHintFlags{ + barAdditionalAttributes := &common.RegistrationEntry_AdditionalAttributes{ DisableX509SvidPrefetch: false, } - bar.CacheHintFlags = barCacheHintFlags + bar.AdditionalAttributes = barAdditionalAttributes qux := makeRegistrationEntryWithTTL("QUX", 130, 140, "Q") // Create entry but don't mark it stale from checkSVID method; diff --git a/pkg/server/datastore/sqlstore/models.go b/pkg/server/datastore/sqlstore/models.go index 6f1aa4aa7e..336347724a 100644 --- a/pkg/server/datastore/sqlstore/models.go +++ b/pkg/server/datastore/sqlstore/models.go @@ -101,9 +101,9 @@ type RegisteredEntry struct { // TTL of JWT identities derived from this entry JWTSvidTTL int32 `gorm:"column:jwt_svid_ttl"` - // CacheHintFlags contains a set of options and flags that inform the + // AdditionalAttributes contains a set of options and flags that inform the // agent behaviour with respect to pre-fetching and refreshing X509 SVIDs - CacheHintFlags []byte `gorm:"size:65535,column:cache_hint_flags"` + AdditionalAttributes []byte `gorm:"size:65535,column:additional_attributes"` } // RegisteredEntryEvent holds the entry id of a registered entry that had an event diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index bb066d3a3e..a45bd9c1c5 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -2550,23 +2550,23 @@ func createRegistrationEntry(tx *gorm.DB, entry *common.RegistrationEntry) (*com return nil, err } - CacheHintFlags, err := proto.Marshal(entry.CacheHintFlags) + AdditionalAttributes, err := proto.Marshal(entry.AdditionalAttributes) if err != nil { return nil, err } newRegisteredEntry := RegisteredEntry{ - EntryID: entryID, - SpiffeID: entry.SpiffeId, - ParentID: entry.ParentId, - TTL: entry.X509SvidTtl, - Admin: entry.Admin, - Downstream: entry.Downstream, - Expiry: entry.EntryExpiry, - StoreSvid: entry.StoreSvid, - JWTSvidTTL: entry.JwtSvidTtl, - Hint: entry.Hint, - CacheHintFlags: CacheHintFlags, + EntryID: entryID, + SpiffeID: entry.SpiffeId, + ParentID: entry.ParentId, + TTL: entry.X509SvidTtl, + Admin: entry.Admin, + Downstream: entry.Downstream, + Expiry: entry.EntryExpiry, + StoreSvid: entry.StoreSvid, + JWTSvidTTL: entry.JwtSvidTtl, + Hint: entry.Hint, + AdditionalAttributes: AdditionalAttributes, } if err := tx.Create(&newRegisteredEntry).Error; err != nil { @@ -2682,7 +2682,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2747,7 +2747,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -2808,7 +2808,7 @@ SELECT D.value AS dns_name, E.revision_number, E.jwt_svid_ttl AS reg_jwt_svid_ttl, - E.cache_hint_flags AS cache_hint_flags + E.additional_attributes AS additional_attributes FROM registered_entries E LEFT JOIN @@ -2851,7 +2851,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries WHERE id IN (SELECT id FROM listing) @@ -3063,7 +3063,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries `) @@ -3159,7 +3159,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries `) @@ -3253,7 +3253,7 @@ SELECT D.value AS dns_name, E.revision_number, E.jwt_svid_ttl AS reg_jwt_svid_ttl, - E.cache_hint_flags AS cache_hint_flags + E.additional_attributes AS additional_attributes FROM registered_entries E LEFT JOIN @@ -3328,7 +3328,7 @@ SELECT NULL AS dns_name, revision_number, jwt_svid_ttl AS reg_jwt_svid_ttl, - cache_hint_flags + additional_attributes FROM registered_entries `) @@ -3870,26 +3870,26 @@ func fillNodeSelectorFromRow(nodeSelector *common.Selector, r *nodeSelectorRow) } type entryRow struct { - EId uint64 - EntryID sql.NullString - SpiffeID sql.NullString - ParentID sql.NullString - RegTTL sql.NullInt64 - Admin sql.NullBool - Downstream sql.NullBool - Expiry sql.NullInt64 - SelectorID sql.NullInt64 - SelectorType sql.NullString - SelectorValue sql.NullString - StoreSvid sql.NullBool - Hint sql.NullString - CreatedAt sql.NullTime - TrustDomain sql.NullString - DNSNameID sql.NullInt64 - DNSName sql.NullString - RevisionNumber sql.NullInt64 - RegJwtSvidTTL sql.NullInt64 - CacheHintFlags sql.Null[[]byte] + EId uint64 + EntryID sql.NullString + SpiffeID sql.NullString + ParentID sql.NullString + RegTTL sql.NullInt64 + Admin sql.NullBool + Downstream sql.NullBool + Expiry sql.NullInt64 + SelectorID sql.NullInt64 + SelectorType sql.NullString + SelectorValue sql.NullString + StoreSvid sql.NullBool + Hint sql.NullString + CreatedAt sql.NullTime + TrustDomain sql.NullString + DNSNameID sql.NullInt64 + DNSName sql.NullString + RevisionNumber sql.NullInt64 + RegJwtSvidTTL sql.NullInt64 + AdditionalAttributes sql.Null[[]byte] } func scanEntryRow(rs *sql.Rows, r *entryRow) error { @@ -3913,7 +3913,7 @@ func scanEntryRow(rs *sql.Rows, r *entryRow) error { &r.DNSName, &r.RevisionNumber, &r.RegJwtSvidTTL, - &r.CacheHintFlags, + &r.AdditionalAttributes, )) } @@ -3976,10 +3976,10 @@ func fillEntryFromRow(entry *common.RegistrationEntry, r *entryRow) error { entry.CreatedAt = roundedInSecondsUnix(r.CreatedAt.Time) } - if r.CacheHintFlags.Valid { - if len(r.CacheHintFlags.V) > 0 { - entry.CacheHintFlags = &common.RegistrationEntry_CacheHintFlags{} - if err := proto.Unmarshal(r.CacheHintFlags.V, entry.CacheHintFlags); err != nil { + if r.AdditionalAttributes.Valid { + if len(r.AdditionalAttributes.V) > 0 { + entry.AdditionalAttributes = &common.RegistrationEntry_AdditionalAttributes{} + if err := proto.Unmarshal(r.AdditionalAttributes.V, entry.AdditionalAttributes); err != nil { return newSQLError("could not parse cache hint flags: %s", err) } } @@ -4082,12 +4082,12 @@ func updateRegistrationEntry(tx *gorm.DB, e *common.RegistrationEntry, mask *com if mask == nil || mask.Hint { entry.Hint = e.Hint } - if mask == nil || mask.CacheHintFlags { - CacheHintFlags, err := proto.Marshal(e.CacheHintFlags) + if mask == nil || mask.AdditionalAttributes { + AdditionalAttributes, err := proto.Marshal(e.AdditionalAttributes) if err != nil { return nil, err } - entry.CacheHintFlags = CacheHintFlags + entry.AdditionalAttributes = AdditionalAttributes } // Revision number is increased by 1 on every update call @@ -4542,7 +4542,7 @@ func validateRegistrationEntry(entry *common.RegistrationEntry) error { // it is done to avoid users to mix selectors from different platforms in // entries with storable SVIDs if entry.StoreSvid { - if entry.CacheHintFlags.GetDisableX509SvidPrefetch() { + if entry.AdditionalAttributes.GetDisableX509SvidPrefetch() { return newValidationError("specifying cache behaviour is incompatible with storable SVIDs") } // Selectors must never be empty @@ -4674,32 +4674,32 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry federatesWith = append(federatesWith, bundle.TrustDomain) } - CacheHintFlags := &common.RegistrationEntry_CacheHintFlags{} - if len(model.CacheHintFlags) != 0 { - if err := proto.Unmarshal(model.CacheHintFlags, CacheHintFlags); err != nil { + AdditionalAttributes := &common.RegistrationEntry_AdditionalAttributes{} + if len(model.AdditionalAttributes) != 0 { + if err := proto.Unmarshal(model.AdditionalAttributes, AdditionalAttributes); err != nil { return nil, err } } else { - CacheHintFlags = nil + AdditionalAttributes = nil } return &common.RegistrationEntry{ - EntryId: model.EntryID, - Selectors: selectors, - SpiffeId: model.SpiffeID, - ParentId: model.ParentID, - X509SvidTtl: model.TTL, - FederatesWith: federatesWith, - Admin: model.Admin, - Downstream: model.Downstream, - EntryExpiry: model.Expiry, - DnsNames: dnsList, - RevisionNumber: model.RevisionNumber, - StoreSvid: model.StoreSvid, - JwtSvidTtl: model.JWTSvidTTL, - Hint: model.Hint, - CacheHintFlags: CacheHintFlags, - CreatedAt: roundedInSecondsUnix(model.CreatedAt), + EntryId: model.EntryID, + Selectors: selectors, + SpiffeId: model.SpiffeID, + ParentId: model.ParentID, + X509SvidTtl: model.TTL, + FederatesWith: federatesWith, + Admin: model.Admin, + Downstream: model.Downstream, + EntryExpiry: model.Expiry, + DnsNames: dnsList, + RevisionNumber: model.RevisionNumber, + StoreSvid: model.StoreSvid, + JwtSvidTtl: model.JWTSvidTTL, + Hint: model.Hint, + AdditionalAttributes: AdditionalAttributes, + CreatedAt: roundedInSecondsUnix(model.CreatedAt), }, nil } diff --git a/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json b/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json index 6829a36f53..f8b4820adc 100644 --- a/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json +++ b/pkg/server/datastore/sqlstore/testdata/invalid_registration_entries.json @@ -72,7 +72,7 @@ ], "spiffe_id": "SpiffeId1", "store_svid": true, - "cache_hint_flags": { + "additional_attributes": { "disable_x509_svid_prefetch": true } }, diff --git a/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json b/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json index f512b685eb..cdfc1dd5f6 100644 --- a/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json +++ b/pkg/server/datastore/sqlstore/testdata/valid_registration_entries.json @@ -62,7 +62,7 @@ } ], "spiffe_id": "SpiffeId1", - "cache_hint_flags": { + "additional_attributes": { "disable_x509_svid_prefetch": true } } diff --git a/proto/spire/common/common.pb.go b/proto/spire/common/common.pb.go index e927c30948..11b23ff311 100644 --- a/proto/spire/common/common.pb.go +++ b/proto/spire/common/common.pb.go @@ -373,10 +373,10 @@ type RegistrationEntry struct { // identity should be used by a workload when more than one SVID is returned. Hint string `protobuf:"bytes,14,opt,name=hint,proto3" json:"hint,omitempty"` // * Time of creation, in seconds from epoch - CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - CacheHintFlags *RegistrationEntry_CacheHintFlags `protobuf:"bytes,16,opt,name=cache_hint_flags,json=cacheHintFlags,proto3,oneof" json:"cache_hint_flags,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + CreatedAt int64 `protobuf:"varint,15,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + AdditionalAttributes *RegistrationEntry_AdditionalAttributes `protobuf:"bytes,16,opt,name=additional_attributes,json=additionalAttributes,proto3,oneof" json:"additional_attributes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntry) Reset() { @@ -514,32 +514,32 @@ func (x *RegistrationEntry) GetCreatedAt() int64 { return 0 } -func (x *RegistrationEntry) GetCacheHintFlags() *RegistrationEntry_CacheHintFlags { +func (x *RegistrationEntry) GetAdditionalAttributes() *RegistrationEntry_AdditionalAttributes { if x != nil { - return x.CacheHintFlags + return x.AdditionalAttributes } return nil } // * The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry type RegistrationEntryMask struct { - state protoimpl.MessageState `protogen:"open.v1"` - Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` - ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` - SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` - X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` - FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` - EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` - Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` - Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` - EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` - DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` - StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` - JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` - Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` - CacheHintFlags bool `protobuf:"varint,14,opt,name=cache_hint_flags,json=cacheHintFlags,proto3" json:"cache_hint_flags,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Selectors bool `protobuf:"varint,1,opt,name=selectors,proto3" json:"selectors,omitempty"` + ParentId bool `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` + SpiffeId bool `protobuf:"varint,3,opt,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` + X509SvidTtl bool `protobuf:"varint,4,opt,name=x509_svid_ttl,json=x509SvidTtl,proto3" json:"x509_svid_ttl,omitempty"` + FederatesWith bool `protobuf:"varint,5,opt,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` + EntryId bool `protobuf:"varint,6,opt,name=entry_id,json=entryId,proto3" json:"entry_id,omitempty"` + Admin bool `protobuf:"varint,7,opt,name=admin,proto3" json:"admin,omitempty"` + Downstream bool `protobuf:"varint,8,opt,name=downstream,proto3" json:"downstream,omitempty"` + EntryExpiry bool `protobuf:"varint,9,opt,name=entryExpiry,proto3" json:"entryExpiry,omitempty"` + DnsNames bool `protobuf:"varint,10,opt,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` + StoreSvid bool `protobuf:"varint,11,opt,name=store_svid,json=storeSvid,proto3" json:"store_svid,omitempty"` + JwtSvidTtl bool `protobuf:"varint,12,opt,name=jwt_svid_ttl,json=jwtSvidTtl,proto3" json:"jwt_svid_ttl,omitempty"` + Hint bool `protobuf:"varint,13,opt,name=hint,proto3" json:"hint,omitempty"` + AdditionalAttributes bool `protobuf:"varint,14,opt,name=additional_attributes,json=additionalAttributes,proto3" json:"additional_attributes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RegistrationEntryMask) Reset() { @@ -663,9 +663,9 @@ func (x *RegistrationEntryMask) GetHint() bool { return false } -func (x *RegistrationEntryMask) GetCacheHintFlags() bool { +func (x *RegistrationEntryMask) GetAdditionalAttributes() bool { if x != nil { - return x.CacheHintFlags + return x.AdditionalAttributes } return false } @@ -1113,7 +1113,7 @@ func (x *AttestedNodeMask) GetAgentVersion() bool { // * Represents a set of flags informing the agent behaviour with respect to // pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort // spent on generating SVIDs of types, which are unlikely to be needed. -type RegistrationEntry_CacheHintFlags struct { +type RegistrationEntry_AdditionalAttributes struct { state protoimpl.MessageState `protogen:"open.v1"` // * Flag indicating whether the agent should prefetch and cache X509 SVID. // This can be set to `true` if the workload is unlikely to request an X509 SVID. @@ -1122,20 +1122,20 @@ type RegistrationEntry_CacheHintFlags struct { sizeCache protoimpl.SizeCache } -func (x *RegistrationEntry_CacheHintFlags) Reset() { - *x = RegistrationEntry_CacheHintFlags{} +func (x *RegistrationEntry_AdditionalAttributes) Reset() { + *x = RegistrationEntry_AdditionalAttributes{} mi := &file_spire_common_common_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *RegistrationEntry_CacheHintFlags) String() string { +func (x *RegistrationEntry_AdditionalAttributes) String() string { return protoimpl.X.MessageStringOf(x) } -func (*RegistrationEntry_CacheHintFlags) ProtoMessage() {} +func (*RegistrationEntry_AdditionalAttributes) ProtoMessage() {} -func (x *RegistrationEntry_CacheHintFlags) ProtoReflect() protoreflect.Message { +func (x *RegistrationEntry_AdditionalAttributes) ProtoReflect() protoreflect.Message { mi := &file_spire_common_common_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1147,12 +1147,12 @@ func (x *RegistrationEntry_CacheHintFlags) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use RegistrationEntry_CacheHintFlags.ProtoReflect.Descriptor instead. -func (*RegistrationEntry_CacheHintFlags) Descriptor() ([]byte, []int) { +// Deprecated: Use RegistrationEntry_AdditionalAttributes.ProtoReflect.Descriptor instead. +func (*RegistrationEntry_AdditionalAttributes) Descriptor() ([]byte, []int) { return file_spire_common_common_proto_rawDescGZIP(), []int{5, 0} } -func (x *RegistrationEntry_CacheHintFlags) GetDisableX509SvidPrefetch() bool { +func (x *RegistrationEntry_AdditionalAttributes) GetDisableX509SvidPrefetch() bool { if x != nil { return x.DisableX509SvidPrefetch } @@ -1182,7 +1182,7 @@ const file_spire_common_common_proto_rawDesc = "" + "\x12new_cert_not_after\x18\x06 \x01(\x03R\x0fnewCertNotAfter\x124\n" + "\tselectors\x18\a \x03(\v2\x16.spire.common.SelectorR\tselectors\x12!\n" + "\fcan_reattest\x18\b \x01(\bR\vcanReattest\x12#\n" + - "\ragent_version\x18\t \x01(\tR\fagentVersion\"\xbe\x05\n" + + "\ragent_version\x18\t \x01(\tR\fagentVersion\"\xda\x05\n" + "\x11RegistrationEntry\x124\n" + "\tselectors\x18\x01 \x03(\v2\x16.spire.common.SelectorR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\tR\bparentId\x12\x1b\n" + @@ -1204,11 +1204,11 @@ const file_spire_common_common_proto_rawDesc = "" + "jwtSvidTtl\x12\x12\n" + "\x04hint\x18\x0e \x01(\tR\x04hint\x12\x1d\n" + "\n" + - "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12]\n" + - "\x10cache_hint_flags\x18\x10 \x01(\v2..spire.common.RegistrationEntry.CacheHintFlagsH\x00R\x0ecacheHintFlags\x88\x01\x01\x1aM\n" + - "\x0eCacheHintFlags\x12;\n" + - "\x1adisable_x509_svid_prefetch\x18\x01 \x01(\bR\x17disableX509SvidPrefetchB\x13\n" + - "\x11_cache_hint_flags\"\xc9\x03\n" + + "created_at\x18\x0f \x01(\x03R\tcreatedAt\x12n\n" + + "\x15additional_attributes\x18\x10 \x01(\v24.spire.common.RegistrationEntry.AdditionalAttributesH\x00R\x14additionalAttributes\x88\x01\x01\x1aS\n" + + "\x14AdditionalAttributes\x12;\n" + + "\x1adisable_x509_svid_prefetch\x18\x01 \x01(\bR\x17disableX509SvidPrefetchB\x18\n" + + "\x16_additional_attributes\"\xd4\x03\n" + "\x15RegistrationEntryMask\x12\x1c\n" + "\tselectors\x18\x01 \x01(\bR\tselectors\x12\x1b\n" + "\tparent_id\x18\x02 \x01(\bR\bparentId\x12\x1b\n" + @@ -1227,8 +1227,8 @@ const file_spire_common_common_proto_rawDesc = "" + "store_svid\x18\v \x01(\bR\tstoreSvid\x12 \n" + "\fjwt_svid_ttl\x18\f \x01(\bR\n" + "jwtSvidTtl\x12\x12\n" + - "\x04hint\x18\r \x01(\bR\x04hint\x12(\n" + - "\x10cache_hint_flags\x18\x0e \x01(\bR\x0ecacheHintFlags\"P\n" + + "\x04hint\x18\r \x01(\bR\x04hint\x123\n" + + "\x15additional_attributes\x18\x0e \x01(\bR\x14additionalAttributes\"P\n" + "\x13RegistrationEntries\x129\n" + "\aentries\x18\x01 \x03(\v2\x1f.spire.common.RegistrationEntryR\aentries\"K\n" + "\vCertificate\x12\x1b\n" + @@ -1280,26 +1280,26 @@ func file_spire_common_common_proto_rawDescGZIP() []byte { var file_spire_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_spire_common_common_proto_goTypes = []any{ - (*Empty)(nil), // 0: spire.common.Empty - (*AttestationData)(nil), // 1: spire.common.AttestationData - (*Selector)(nil), // 2: spire.common.Selector - (*Selectors)(nil), // 3: spire.common.Selectors - (*AttestedNode)(nil), // 4: spire.common.AttestedNode - (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry - (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask - (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries - (*Certificate)(nil), // 8: spire.common.Certificate - (*PublicKey)(nil), // 9: spire.common.PublicKey - (*Bundle)(nil), // 10: spire.common.Bundle - (*BundleMask)(nil), // 11: spire.common.BundleMask - (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask - (*RegistrationEntry_CacheHintFlags)(nil), // 13: spire.common.RegistrationEntry.CacheHintFlags + (*Empty)(nil), // 0: spire.common.Empty + (*AttestationData)(nil), // 1: spire.common.AttestationData + (*Selector)(nil), // 2: spire.common.Selector + (*Selectors)(nil), // 3: spire.common.Selectors + (*AttestedNode)(nil), // 4: spire.common.AttestedNode + (*RegistrationEntry)(nil), // 5: spire.common.RegistrationEntry + (*RegistrationEntryMask)(nil), // 6: spire.common.RegistrationEntryMask + (*RegistrationEntries)(nil), // 7: spire.common.RegistrationEntries + (*Certificate)(nil), // 8: spire.common.Certificate + (*PublicKey)(nil), // 9: spire.common.PublicKey + (*Bundle)(nil), // 10: spire.common.Bundle + (*BundleMask)(nil), // 11: spire.common.BundleMask + (*AttestedNodeMask)(nil), // 12: spire.common.AttestedNodeMask + (*RegistrationEntry_AdditionalAttributes)(nil), // 13: spire.common.RegistrationEntry.AdditionalAttributes } var file_spire_common_common_proto_depIdxs = []int32{ 2, // 0: spire.common.Selectors.entries:type_name -> spire.common.Selector 2, // 1: spire.common.AttestedNode.selectors:type_name -> spire.common.Selector 2, // 2: spire.common.RegistrationEntry.selectors:type_name -> spire.common.Selector - 13, // 3: spire.common.RegistrationEntry.cache_hint_flags:type_name -> spire.common.RegistrationEntry.CacheHintFlags + 13, // 3: spire.common.RegistrationEntry.additional_attributes:type_name -> spire.common.RegistrationEntry.AdditionalAttributes 5, // 4: spire.common.RegistrationEntries.entries:type_name -> spire.common.RegistrationEntry 8, // 5: spire.common.Bundle.root_cas:type_name -> spire.common.Certificate 9, // 6: spire.common.Bundle.jwt_signing_keys:type_name -> spire.common.PublicKey diff --git a/proto/spire/common/common.proto b/proto/spire/common/common.proto index 0bee50c350..4caec88960 100644 --- a/proto/spire/common/common.proto +++ b/proto/spire/common/common.proto @@ -102,12 +102,12 @@ message RegistrationEntry { /** Represents a set of flags informing the agent behaviour with respect to pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort spent on generating SVIDs of types, which are unlikely to be needed. */ - message CacheHintFlags { + message AdditionalAttributes { /** Flag indicating whether the agent should prefetch and cache X509 SVID. This can be set to `true` if the workload is unlikely to request an X509 SVID. */ bool disable_x509_svid_prefetch = 1; } - optional CacheHintFlags cache_hint_flags = 16; + optional AdditionalAttributes additional_attributes = 16; } /** The RegistrationEntryMask is used to update only selected fields of the RegistrationEntry */ @@ -125,7 +125,7 @@ message RegistrationEntryMask { bool store_svid = 11; bool jwt_svid_ttl = 12; bool hint = 13; - bool cache_hint_flags = 14; + bool additional_attributes = 14; } From fac8325793047b14c5ed0fc10bddfbe4da0ad17a Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 25 Jan 2026 23:01:37 +0000 Subject: [PATCH 23/51] updated description of the message Signed-off-by: Valentin Fadeev --- proto/spire/common/common.pb.go | 13 +++++++++---- proto/spire/common/common.proto | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/proto/spire/common/common.pb.go b/proto/spire/common/common.pb.go index 11b23ff311..6e67c9217b 100644 --- a/proto/spire/common/common.pb.go +++ b/proto/spire/common/common.pb.go @@ -1110,13 +1110,18 @@ func (x *AttestedNodeMask) GetAgentVersion() bool { return false } -// * Represents a set of flags informing the agent behaviour with respect to -// pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort -// spent on generating SVIDs of types, which are unlikely to be needed. +// * This nested message is reserved to contain a number of optional fields +// controlling the various aspects of the agent's behaviour with respect to a +// given registration entry. It serves to enable introducing and testing out new +// tunables, without having to modify the datastore schema. Over time, some of +// the fields contained therein may be considered eligible for their dedicated +// attributes in the datastore. type RegistrationEntry_AdditionalAttributes struct { state protoimpl.MessageState `protogen:"open.v1"` // * Flag indicating whether the agent should prefetch and cache X509 SVID. - // This can be set to `true` if the workload is unlikely to request an X509 SVID. + // Can be set to `true` if the workload is unlikely to request an X509 SVID. + // This is meant to prevent unnecessary effort spent on generating SVIDs of types, + // which are unlikely to be needed. DisableX509SvidPrefetch bool `protobuf:"varint,1,opt,name=disable_x509_svid_prefetch,json=disableX509SvidPrefetch,proto3" json:"disable_x509_svid_prefetch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache diff --git a/proto/spire/common/common.proto b/proto/spire/common/common.proto index 4caec88960..f41b6ed413 100644 --- a/proto/spire/common/common.proto +++ b/proto/spire/common/common.proto @@ -99,12 +99,17 @@ message RegistrationEntry { string hint = 14; /** Time of creation, in seconds from epoch */ int64 created_at = 15; - /** Represents a set of flags informing the agent behaviour with respect to - pre-fecthing and caching SVIDs. This is meant to prevent unnecessary effort - spent on generating SVIDs of types, which are unlikely to be needed. */ + /** This nested message is reserved to contain a number of optional fields + controlling the various aspects of the agent's behaviour with respect to a + given registration entry. It serves to enable introducing and testing out new + tunables, without having to modify the datastore schema. Over time, some of + the fields contained therein may be considered eligible for their dedicated + attributes in the datastore. */ message AdditionalAttributes { /** Flag indicating whether the agent should prefetch and cache X509 SVID. - This can be set to `true` if the workload is unlikely to request an X509 SVID. */ + Can be set to `true` if the workload is unlikely to request an X509 SVID. + This is meant to prevent unnecessary effort spent on generating SVIDs of types, + which are unlikely to be needed.*/ bool disable_x509_svid_prefetch = 1; } optional AdditionalAttributes additional_attributes = 16; From 86e81c2c45d6821b3867f1e89eb2c2accd281084 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 26 Jan 2026 23:18:18 +0000 Subject: [PATCH 24/51] fixed assertion Signed-off-by: Valentin Fadeev --- pkg/agent/manager/cache/lru_cache_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/agent/manager/cache/lru_cache_test.go b/pkg/agent/manager/cache/lru_cache_test.go index a08c83802b..a76728be58 100644 --- a/pkg/agent/manager/cache/lru_cache_test.go +++ b/pkg/agent/manager/cache/lru_cache_test.go @@ -584,7 +584,7 @@ func TestLRUCacheGetStaleEntriesDisablePrefetch(t *testing.T) { {Entry: cache.records[bar.EntryId].entry}, {Entry: cache.records[qux.EntryId].entry}, } - assert.Equal(t, expectedEntries, cache.GetStaleEntries()) + assert.ElementsMatch(t, expectedEntries, cache.GetStaleEntries()) } func TestLRUCacheGetStaleEntries(t *testing.T) { From fb49792719397c7f316d56cc09458c98f9a105c5 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 30 Jan 2026 08:54:09 +0000 Subject: [PATCH 25/51] updated model field description Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/models.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/server/datastore/sqlstore/models.go b/pkg/server/datastore/sqlstore/models.go index 336347724a..cb38637bea 100644 --- a/pkg/server/datastore/sqlstore/models.go +++ b/pkg/server/datastore/sqlstore/models.go @@ -101,8 +101,9 @@ type RegisteredEntry struct { // TTL of JWT identities derived from this entry JWTSvidTTL int32 `gorm:"column:jwt_svid_ttl"` - // AdditionalAttributes contains a set of options and flags that inform the - // agent behaviour with respect to pre-fetching and refreshing X509 SVIDs + // AdditionalAttributes may contain a number of optional fields controlling + // the various aspects of the agent's behaviour with respect to a given + // registration entry AdditionalAttributes []byte `gorm:"size:65535,column:additional_attributes"` } From cd33b7e0662ed3cd6f7901a66fd2792a45236e2c Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 11 Feb 2026 17:36:13 +0000 Subject: [PATCH 26/51] added cli support for additional attributes Signed-off-by: Valentin Fadeev --- cmd/spire-server/cli/entry/create.go | 11 +- cmd/spire-server/cli/entry/create_test.go | 307 ++++++++++++------ cmd/spire-server/cli/entry/show_test.go | 68 ++-- cmd/spire-server/cli/entry/update.go | 10 + cmd/spire-server/cli/entry/update_test.go | 258 ++++++++------- cmd/spire-server/cli/entry/util.go | 38 ++- cmd/spire-server/cli/entry/util_posix_test.go | 4 + cmd/spire-server/cli/entry/util_test.go | 46 ++- .../cli/entry/util_windows_test.go | 4 + go.mod | 2 + go.sum | 4 +- pkg/common/protoutil/masks_test.go | 29 +- pkg/server/api/entry.go | 55 +++- pkg/server/api/entry_test.go | 21 +- .../fixture/registration/good-for-update.json | 9 +- test/fixture/registration/good.json | 25 +- 16 files changed, 567 insertions(+), 324 deletions(-) diff --git a/cmd/spire-server/cli/entry/create.go b/cmd/spire-server/cli/entry/create.go index 349fa344b2..e1d56b8eab 100644 --- a/cmd/spire-server/cli/entry/create.go +++ b/cmd/spire-server/cli/entry/create.go @@ -74,6 +74,10 @@ type createCommand struct { // storeSVID determines if the issued SVID must be stored through an SVIDStore plugin storeSVID bool + // disableX509SVIDPrefetch tells the agent not to prefetch and cache X509 SVID for + // the given entry + disableX509SVIDPrefetch bool + printer cliprinter.Printer env *commoncli.Env @@ -103,6 +107,7 @@ func (c *createCommand) AppendFlags(f *flag.FlagSet) { f.Int64Var(&c.entryExpiry, "entryExpiry", 0, "An expiry, from epoch in seconds, for the resulting registration entry to be pruned") f.Var(&c.dnsNames, "dns", "A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once") f.StringVar(&c.hint, "hint", "", "The entry hint, used to disambiguate entries with the same SPIFFE ID") + f.BoolVar(&c.disableX509SVIDPrefetch, "disableX509SVIDPrefetch", false, "A boolean value that, when set, disables prefetching X509 SVID for this entry") cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintCreate) } @@ -209,8 +214,12 @@ func (c *createCommand) parseConfig() ([]*types.Entry, error) { selectors = append(selectors, cs) } - e.Selectors = selectors + + additionalAttributes := &types.Entry_AdditionalAttributes{} + additionalAttributes.DisableX509SvidPrefetch = c.disableX509SVIDPrefetch + e.AdditionalAttributes = additionalAttributes + e.FederatesWith = c.federatesWith e.Admin = c.admin return []*types.Entry{e}, nil diff --git a/cmd/spire-server/cli/entry/create_test.go b/cmd/spire-server/cli/entry/create_test.go index eef1f73f00..314b142254 100644 --- a/cmd/spire-server/cli/entry/create_test.go +++ b/cmd/spire-server/cli/entry/create_test.go @@ -44,7 +44,10 @@ func TestCreate(t *testing.T) { DnsNames: []string{"unu1000", "ung1000"}, Downstream: true, StoreSvid: true, - CreatedAt: 1547583197, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, + CreatedAt: 1547583197, }, Status: &types.Status{ Code: int32(codes.OK), @@ -93,7 +96,10 @@ func TestCreate(t *testing.T) { X509SvidTtl: 200, JwtSvidTtl: 30, Admin: true, - CreatedAt: 1547583197, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: false, + }, + CreatedAt: 1547583197, }, Status: &types.Status{ Code: int32(codes.OK), @@ -109,7 +115,10 @@ func TestCreate(t *testing.T) { X509SvidTtl: 200, JwtSvidTtl: 30, Hint: "internal", - CreatedAt: 1547583197, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: false, + }, + CreatedAt: 1547583197, }, Status: &types.Status{ Code: int32(codes.OK), @@ -128,7 +137,29 @@ func TestCreate(t *testing.T) { StoreSvid: true, X509SvidTtl: 200, JwtSvidTtl: 30, - CreatedAt: 1547583197, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: false, + }, + CreatedAt: 1547583197, + }, + Status: &types.Status{ + Code: int32(codes.OK), + Message: "OK", + }, + }, + { + Entry: &types.Entry{ + Id: "entry-id-4", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/additionalattr"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Admin: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, + CreatedAt: 1547583197, }, Status: &types.Status{ Code: int32(codes.OK), @@ -235,6 +266,7 @@ func TestCreate(t *testing.T) { "-downstream", "-storeSVID", "-hint", "internal", + "-disableX509SVIDPrefetch", }, expReq: &entryv1.BatchCreateEntryRequest{ Entries: []*types.Entry{ @@ -254,27 +286,30 @@ func TestCreate(t *testing.T) { Downstream: true, StoreSvid: true, Hint: "internal", + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, }, }, }, fakeResp: fakeRespOKFromCmd, - expOutPretty: fmt.Sprintf(`Entry ID : entry-id -SPIFFE ID : spiffe://example.org/workload -Parent ID : spiffe://example.org/parent -Revision : 0 -Downstream : true -X509-SVID TTL : 60 -JWT-SVID TTL : 30 -Expiration time : %s -Selector : zebra:zebra:2000 -Selector : alpha:alpha:2000 -FederatesWith : spiffe://domaina.test -FederatesWith : spiffe://domainb.test -DNS name : unu1000 -DNS name : ung1000 -Admin : true -StoreSvid : true - + expOutPretty: fmt.Sprintf(`Entry ID : entry-id +SPIFFE ID : spiffe://example.org/workload +Parent ID : spiffe://example.org/parent +Revision : 0 +Downstream : true +X509-SVID TTL : 60 +JWT-SVID TTL : 30 +Expiration time : %s +Selector : zebra:zebra:2000 +Selector : alpha:alpha:2000 +FederatesWith : spiffe://domaina.test +FederatesWith : spiffe://domainb.test +DNS name : unu1000 +DNS name : ung1000 +Admin : true +StoreSvid : true +DisableX509SvidPrefetch : true `, time.Unix(1552410266, 0).UTC()), expOutJSON: `{ "results": [ @@ -319,7 +354,10 @@ StoreSvid : true ], "revision_number": "0", "store_svid": true, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": true + } } } ] @@ -354,33 +392,34 @@ StoreSvid : true {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - StoreSvid: true, + X509SvidTtl: 60, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + StoreSvid: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, }, }, fakeResp: fakeRespOKFromCmdWithoutJwtTtl, - expOutPretty: fmt.Sprintf(`Entry ID : entry-id -SPIFFE ID : spiffe://example.org/workload -Parent ID : spiffe://example.org/parent -Revision : 0 -Downstream : true -X509-SVID TTL : 60 -JWT-SVID TTL : default -Expiration time : %s -Selector : zebra:zebra:2000 -Selector : alpha:alpha:2000 -FederatesWith : spiffe://domaina.test -FederatesWith : spiffe://domainb.test -DNS name : unu1000 -DNS name : ung1000 -Admin : true -StoreSvid : true + expOutPretty: fmt.Sprintf(`Entry ID : entry-id +SPIFFE ID : spiffe://example.org/workload +Parent ID : spiffe://example.org/parent +Revision : 0 +Downstream : true +X509-SVID TTL : 60 +JWT-SVID TTL : default +Expiration time : %s +Selector : zebra:zebra:2000 +Selector : alpha:alpha:2000 +FederatesWith : spiffe://domaina.test +FederatesWith : spiffe://domainb.test +DNS name : unu1000 +DNS name : ung1000 +Admin : true +StoreSvid : true `, time.Unix(1552410266, 0).UTC()), expOutJSON: `{ @@ -440,20 +479,22 @@ StoreSvid : true expReq: &entryv1.BatchCreateEntryRequest{ Entries: []*types.Entry{ { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Admin: true, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Admin: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Hint: "internal", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Hint: "internal", + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, { SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, @@ -462,40 +503,62 @@ StoreSvid : true {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, + X509SvidTtl: 200, + JwtSvidTtl: 30, + StoreSvid: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + }, + { + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/additionalattr"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, X509SvidTtl: 200, JwtSvidTtl: 30, - StoreSvid: true, + Admin: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, }, }, }, fakeResp: fakeRespOKFromFile, - expOutPretty: `Entry ID : entry-id-1 -SPIFFE ID : spiffe://example.org/Blog -Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 30 -Selector : unix:uid:1111 -Admin : true + expOutPretty: `Entry ID : entry-id-1 +SPIFFE ID : spiffe://example.org/Blog +Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 30 +Selector : unix:uid:1111 +Admin : true + +Entry ID : entry-id-2 +SPIFFE ID : spiffe://example.org/Database +Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 30 +Selector : unix:uid:1111 +Hint : internal -Entry ID : entry-id-2 -SPIFFE ID : spiffe://example.org/Database -Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 30 -Selector : unix:uid:1111 -Hint : internal +Entry ID : entry-id-3 +SPIFFE ID : spiffe://example.org/storesvid +Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 30 +Selector : type:key1:value +Selector : type:key2:value +StoreSvid : true -Entry ID : entry-id-3 -SPIFFE ID : spiffe://example.org/storesvid -Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 30 -Selector : type:key1:value -Selector : type:key2:value -StoreSvid : true +Entry ID : entry-id-4 +SPIFFE ID : spiffe://example.org/additionalattr +Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 30 +Selector : unix:uid:1111 +Admin : true +DisableX509SvidPrefetch : true `, expOutJSON: `{ @@ -531,7 +594,10 @@ StoreSvid : true "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } } }, { @@ -565,7 +631,10 @@ StoreSvid : true "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } } }, { @@ -603,7 +672,47 @@ StoreSvid : true "dns_names": [], "revision_number": "0", "store_svid": true, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } + } + }, + { + "status": { + "code": 0, + "message": "OK" + }, + "entry": { + "id": "entry-id-4", + "spiffe_id": { + "trust_domain": "example.org", + "path": "/additionalattr" + }, + "parent_id": { + "trust_domain": "example.org", + "path": "/spire/agent/join_token/TokenBlog" + }, + "selectors": [ + { + "type": "unix", + "value": "uid:1111" + } + ], + "x509_svid_ttl": 200, + "federates_with": [], + "hint": "", + "admin": true, + "created_at": "1547583197", + "downstream": false, + "expires_at": "0", + "dns_names": [], + "revision_number": "0", + "store_svid": false, + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": true + } } } ] @@ -614,20 +723,21 @@ StoreSvid : true args: []string{"-spiffeID", "spiffe://example.org/already-exist", "-node", "-selector", "unix:uid:1"}, expReq: &entryv1.BatchCreateEntryRequest{Entries: []*types.Entry{ { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/already-exist"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/server"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/already-exist"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/server"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, }}, fakeResp: fakeRespErr, expErrPretty: `Failed to create the following entry (code: AlreadyExists, msg: "similar entry already exists"): -Entry ID : (none) -SPIFFE ID : spiffe://example.org/already-exist -Parent ID : spiffe://example.org/spire/server -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Selector : unix:uid:1 +Entry ID : (none) +SPIFFE ID : spiffe://example.org/already-exist +Parent ID : spiffe://example.org/spire/server +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Selector : unix:uid:1 Error: failed to create one or more entries `, @@ -664,7 +774,10 @@ Error: failed to create one or more entries "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 0 + "jwt_svid_ttl": 0, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } } } ] diff --git a/cmd/spire-server/cli/entry/show_test.go b/cmd/spire-server/cli/entry/show_test.go index 84cd90854d..4a1851c972 100644 --- a/cmd/spire-server/cli/entry/show_test.go +++ b/cmd/spire-server/cli/entry/show_test.go @@ -455,49 +455,49 @@ func getEntries(count int) []*types.Entry { func getPrettyPrintedEntry(idx int) string { switch idx { case 0: - return `Entry ID : 00000000-0000-0000-0000-000000000000 -SPIFFE ID : spiffe://example.org/son -Parent ID : spiffe://example.org/father -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Selector : foo:bar -Hint : internal + return `Entry ID : 00000000-0000-0000-0000-000000000000 +SPIFFE ID : spiffe://example.org/son +Parent ID : spiffe://example.org/father +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Selector : foo:bar +Hint : internal ` case 1: - return `Entry ID : 00000000-0000-0000-0000-000000000001 -SPIFFE ID : spiffe://example.org/daughter -Parent ID : spiffe://example.org/father -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Selector : bar:baz -Selector : foo:bar -Hint : external + return `Entry ID : 00000000-0000-0000-0000-000000000001 +SPIFFE ID : spiffe://example.org/daughter +Parent ID : spiffe://example.org/father +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Selector : bar:baz +Selector : foo:bar +Hint : external ` case 2: - return `Entry ID : 00000000-0000-0000-0000-000000000002 -SPIFFE ID : spiffe://example.org/daughter -Parent ID : spiffe://example.org/mother -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Selector : bar:baz -Selector : baz:bat -FederatesWith : spiffe://domain.test + return `Entry ID : 00000000-0000-0000-0000-000000000002 +SPIFFE ID : spiffe://example.org/daughter +Parent ID : spiffe://example.org/mother +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Selector : bar:baz +Selector : baz:bat +FederatesWith : spiffe://domain.test ` case 3: - return fmt.Sprintf(`Entry ID : 00000000-0000-0000-0000-000000000003 -SPIFFE ID : spiffe://example.org/son -Parent ID : spiffe://example.org/mother -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Expiration time : %s -Selector : baz:bat + return fmt.Sprintf(`Entry ID : 00000000-0000-0000-0000-000000000003 +SPIFFE ID : spiffe://example.org/son +Parent ID : spiffe://example.org/mother +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Expiration time : %s +Selector : baz:bat `, time.Unix(1552410266, 0).UTC()) default: diff --git a/cmd/spire-server/cli/entry/update.go b/cmd/spire-server/cli/entry/update.go index 369275d203..10bb50b2cf 100644 --- a/cmd/spire-server/cli/entry/update.go +++ b/cmd/spire-server/cli/entry/update.go @@ -67,6 +67,10 @@ type updateCommand struct { // storeSVID determines if the issued SVID must be stored through an SVIDStore plugin storeSVID bool + // disableX509SVIDPrefetch tells the agent not to prefetch and cache X509 SVID for + // the given entry + disableX509SVIDPrefetch bool + // Entry hint, used to disambiguate entries with the same SPIFFE ID hint string @@ -98,6 +102,7 @@ func (c *updateCommand) AppendFlags(f *flag.FlagSet) { f.Int64Var(&c.entryExpiry, "entryExpiry", 0, "An expiry, from epoch in seconds, for the resulting registration entry to be pruned") f.Var(&c.dnsNames, "dns", "A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once") f.StringVar(&c.hint, "hint", "", "The entry hint, used to disambiguate entries with the same SPIFFE ID") + f.BoolVar(&c.disableX509SVIDPrefetch, "disableX509SVIDPrefetch", false, "A boolean value that, when set, disables prefetching X509 SVID for this entry") cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintUpdate) } @@ -204,6 +209,11 @@ func (c *updateCommand) parseConfig() ([]*types.Entry, error) { } e.Selectors = selectors + + additionalAttributes := &types.Entry_AdditionalAttributes{} + additionalAttributes.DisableX509SvidPrefetch = c.disableX509SVIDPrefetch + e.AdditionalAttributes = additionalAttributes + e.FederatesWith = c.federatesWith e.Admin = c.admin e.StoreSvid = c.storeSVID diff --git a/cmd/spire-server/cli/entry/update_test.go b/cmd/spire-server/cli/entry/update_test.go index 0befd96851..dde8b63948 100644 --- a/cmd/spire-server/cli/entry/update_test.go +++ b/cmd/spire-server/cli/entry/update_test.go @@ -61,7 +61,10 @@ func TestUpdate(t *testing.T) { ], "revision_number": "0", "store_svid": true, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } }` entry0AdminJSON := `{ "id": "entry-id", @@ -99,7 +102,10 @@ func TestUpdate(t *testing.T) { ], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 30 + "jwt_svid_ttl": 30, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } }` entry1JSON := `{ "id": "entry-id-1", @@ -127,7 +133,10 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 300 + "jwt_svid_ttl": 300, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } } }` entry2JSON := `{ @@ -156,7 +165,10 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 300 + "jwt_svid_ttl": 300, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } } }` entry3JSON := `{ @@ -189,7 +201,10 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": true, - "jwt_svid_ttl": 300 + "jwt_svid_ttl": 300, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } }` nonExistentEntryJSON := `{ "id": "non-existent-id", @@ -217,7 +232,10 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "x509_svid_ttl": 0 + "x509_svid_ttl": 0, + "additional_attributes": { + "disable_x509_svid_prefetch": false + } }` entry1 := &types.Entry{ @@ -228,14 +246,15 @@ func TestUpdate(t *testing.T) { {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - Hint: "external", + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + Hint: "external", + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry0Admin := &types.Entry{ @@ -246,15 +265,16 @@ func TestUpdate(t *testing.T) { {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - Hint: "external", - CreatedAt: 1547583197, + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + Hint: "external", + CreatedAt: 1547583197, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entryStoreSVID := &types.Entry{ @@ -265,12 +285,13 @@ func TestUpdate(t *testing.T) { {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - StoreSvid: true, + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + StoreSvid: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entryStoreSVIDResp := proto.Clone(entryStoreSVID).(*types.Entry) @@ -289,23 +310,25 @@ func TestUpdate(t *testing.T) { } entry2 := &types.Entry{ - Id: "entry-id-1", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 300, - Admin: true, - Hint: "external", + Id: "entry-id-1", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 300, + Admin: true, + Hint: "external", + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry3 := &types.Entry{ - Id: "entry-id-2", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 300, + Id: "entry-id-2", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 300, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry4 := &types.Entry{ @@ -316,9 +339,10 @@ func TestUpdate(t *testing.T) { {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, - StoreSvid: true, - X509SvidTtl: 200, - JwtSvidTtl: 300, + StoreSvid: true, + X509SvidTtl: 200, + JwtSvidTtl: 300, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry2Resp := proto.Clone(entry2).(*types.Entry) @@ -403,10 +427,11 @@ func TestUpdate(t *testing.T) { args: []string{"-entryID", "entry-id", "-spiffeID", "spiffe://example.org/workload", "-parentID", "spiffe://example.org/parent", "-selector", "unix:uid:1"}, expReq: &entryv1.BatchUpdateEntryRequest{Entries: []*types.Entry{ { - Id: "entry-id", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + Id: "entry-id", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, }}, serverErr: errors.New("server-error"), @@ -436,22 +461,22 @@ func TestUpdate(t *testing.T) { Entries: []*types.Entry{entry1}, }, fakeResp: fakeRespOKFromCmd, - expOutPretty: fmt.Sprintf(`Entry ID : entry-id -SPIFFE ID : spiffe://example.org/workload -Parent ID : spiffe://example.org/parent -Revision : 0 -Downstream : true -X509-SVID TTL : 60 -JWT-SVID TTL : 30 -Expiration time : %s -Selector : zebra:zebra:2000 -Selector : alpha:alpha:2000 -FederatesWith : spiffe://domaina.test -FederatesWith : spiffe://domainb.test -DNS name : unu1000 -DNS name : ung1000 -Admin : true -Hint : external + expOutPretty: fmt.Sprintf(`Entry ID : entry-id +SPIFFE ID : spiffe://example.org/workload +Parent ID : spiffe://example.org/parent +Revision : 0 +Downstream : true +X509-SVID TTL : 60 +JWT-SVID TTL : 30 +Expiration time : %s +Selector : zebra:zebra:2000 +Selector : alpha:alpha:2000 +FederatesWith : spiffe://domaina.test +FederatesWith : spiffe://domainb.test +DNS name : unu1000 +DNS name : ung1000 +Admin : true +Hint : external `, time.Unix(1552410266, 0).UTC()), expOutJSON: fmt.Sprintf(`{ @@ -497,20 +522,20 @@ Hint : external }, }, }, - expOutPretty: fmt.Sprintf(`Entry ID : entry-id -SPIFFE ID : spiffe://example.org/workload -Parent ID : spiffe://example.org/parent -Revision : 0 -X509-SVID TTL : 60 -JWT-SVID TTL : 30 -Expiration time : %s -Selector : type:key1:value -Selector : type:key2:value -FederatesWith : spiffe://domaina.test -FederatesWith : spiffe://domainb.test -DNS name : unu1000 -DNS name : ung1000 -StoreSvid : true + expOutPretty: fmt.Sprintf(`Entry ID : entry-id +SPIFFE ID : spiffe://example.org/workload +Parent ID : spiffe://example.org/parent +Revision : 0 +X509-SVID TTL : 60 +JWT-SVID TTL : 30 +Expiration time : %s +Selector : type:key1:value +Selector : type:key2:value +FederatesWith : spiffe://domaina.test +FederatesWith : spiffe://domainb.test +DNS name : unu1000 +DNS name : ung1000 +StoreSvid : true `, time.Unix(1552410266, 0).UTC()), expOutJSON: fmt.Sprintf(`{ @@ -534,33 +559,33 @@ StoreSvid : true Entries: []*types.Entry{entry2, entry3, entry4}, }, fakeResp: fakeRespOKFromFile, - expOutPretty: `Entry ID : entry-id-1 -SPIFFE ID : spiffe://example.org/Blog -Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 300 -Selector : unix:uid:1111 -Admin : true -Hint : external + expOutPretty: `Entry ID : entry-id-1 +SPIFFE ID : spiffe://example.org/Blog +Parent ID : spiffe://example.org/spire/agent/join_token/TokenBlog +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 300 +Selector : unix:uid:1111 +Admin : true +Hint : external -Entry ID : entry-id-2 -SPIFFE ID : spiffe://example.org/Database -Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 300 -Selector : unix:uid:1111 +Entry ID : entry-id-2 +SPIFFE ID : spiffe://example.org/Database +Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 300 +Selector : unix:uid:1111 -Entry ID : entry-id-3 -SPIFFE ID : spiffe://example.org/Storesvid -Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase -Revision : 0 -X509-SVID TTL : 200 -JWT-SVID TTL : 300 -Selector : type:key1:value -Selector : type:key2:value -StoreSvid : true +Entry ID : entry-id-3 +SPIFFE ID : spiffe://example.org/Storesvid +Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 300 +Selector : type:key1:value +Selector : type:key2:value +StoreSvid : true `, expOutJSON: fmt.Sprintf(` @@ -593,21 +618,22 @@ StoreSvid : true args: []string{"-entryID", "non-existent-id", "-spiffeID", "spiffe://example.org/workload", "-parentID", "spiffe://example.org/parent", "-selector", "unix:uid:1"}, expReq: &entryv1.BatchUpdateEntryRequest{Entries: []*types.Entry{ { - Id: "non-existent-id", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + Id: "non-existent-id", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, }, }}, fakeResp: fakeRespErr, expErrPretty: `Failed to update the following entry (code: NotFound, msg: "failed to update entry: datastore-sql: record not found"): -Entry ID : non-existent-id -SPIFFE ID : spiffe://example.org/workload -Parent ID : spiffe://example.org/parent -Revision : 0 -X509-SVID TTL : default -JWT-SVID TTL : default -Selector : unix:uid:1 +Entry ID : non-existent-id +SPIFFE ID : spiffe://example.org/workload +Parent ID : spiffe://example.org/parent +Revision : 0 +X509-SVID TTL : default +JWT-SVID TTL : default +Selector : unix:uid:1 Error: failed to update one or more entries `, diff --git a/cmd/spire-server/cli/entry/util.go b/cmd/spire-server/cli/entry/util.go index d51208c646..f224329ea3 100644 --- a/cmd/spire-server/cli/entry/util.go +++ b/cmd/spire-server/cli/entry/util.go @@ -14,53 +14,59 @@ import ( ) func printEntry(e *types.Entry, printf func(string, ...any) error) { - _ = printf("Entry ID : %s\n", printableEntryID(e.Id)) - _ = printf("SPIFFE ID : %s\n", protoToIDString(e.SpiffeId)) - _ = printf("Parent ID : %s\n", protoToIDString(e.ParentId)) - _ = printf("Revision : %d\n", e.RevisionNumber) + _ = printf("Entry ID : %s\n", printableEntryID(e.Id)) + _ = printf("SPIFFE ID : %s\n", protoToIDString(e.SpiffeId)) + _ = printf("Parent ID : %s\n", protoToIDString(e.ParentId)) + _ = printf("Revision : %d\n", e.RevisionNumber) if e.Downstream { - _ = printf("Downstream : %t\n", e.Downstream) + _ = printf("Downstream : %t\n", e.Downstream) } if e.X509SvidTtl == 0 { - _ = printf("X509-SVID TTL : default\n") + _ = printf("X509-SVID TTL : default\n") } else { - _ = printf("X509-SVID TTL : %d\n", e.X509SvidTtl) + _ = printf("X509-SVID TTL : %d\n", e.X509SvidTtl) } if e.JwtSvidTtl == 0 { - _ = printf("JWT-SVID TTL : default\n") + _ = printf("JWT-SVID TTL : default\n") } else { - _ = printf("JWT-SVID TTL : %d\n", e.JwtSvidTtl) + _ = printf("JWT-SVID TTL : %d\n", e.JwtSvidTtl) } if e.ExpiresAt != 0 { - _ = printf("Expiration time : %s\n", time.Unix(e.ExpiresAt, 0).UTC()) + _ = printf("Expiration time : %s\n", time.Unix(e.ExpiresAt, 0).UTC()) } for _, s := range e.Selectors { - _ = printf("Selector : %s:%s\n", s.Type, s.Value) + _ = printf("Selector : %s:%s\n", s.Type, s.Value) } for _, id := range e.FederatesWith { - _ = printf("FederatesWith : %s\n", id) + _ = printf("FederatesWith : %s\n", id) } for _, dnsName := range e.DnsNames { - _ = printf("DNS name : %s\n", dnsName) + _ = printf("DNS name : %s\n", dnsName) } // admin is rare, so only show admin if true to keep // from muddying the output. if e.Admin { - _ = printf("Admin : %t\n", e.Admin) + _ = printf("Admin : %t\n", e.Admin) } if e.StoreSvid { - _ = printf("StoreSvid : %t\n", e.StoreSvid) + _ = printf("StoreSvid : %t\n", e.StoreSvid) } if e.Hint != "" { - _ = printf("Hint : %s\n", e.Hint) + _ = printf("Hint : %s\n", e.Hint) + } + + if e.AdditionalAttributes != nil { + if e.AdditionalAttributes.DisableX509SvidPrefetch { + _ = printf("DisableX509SvidPrefetch : %t\n", e.AdditionalAttributes.DisableX509SvidPrefetch) + } } _ = printf("\n") diff --git a/cmd/spire-server/cli/entry/util_posix_test.go b/cmd/spire-server/cli/entry/util_posix_test.go index 2ac9598873..ce8db79653 100644 --- a/cmd/spire-server/cli/entry/util_posix_test.go +++ b/cmd/spire-server/cli/entry/util_posix_test.go @@ -8,6 +8,8 @@ const ( If set, the SPIFFE ID in this entry will be granted access to the SPIRE Server's management APIs -data string Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin. + -disableX509SVIDPrefetch + A boolean value that, when set, disables prefetching X509 SVID for this entry -dns value A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once -downstream @@ -68,6 +70,8 @@ const ( If set, the SPIFFE ID in this entry will be granted access to the SPIRE Server's management APIs -data string Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin. + -disableX509SVIDPrefetch + A boolean value that, when set, disables prefetching X509 SVID for this entry -dns value A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once -downstream diff --git a/cmd/spire-server/cli/entry/util_test.go b/cmd/spire-server/cli/entry/util_test.go index 782b95afa7..9f70a1a5e6 100644 --- a/cmd/spire-server/cli/entry/util_test.go +++ b/cmd/spire-server/cli/entry/util_test.go @@ -70,11 +70,12 @@ func TestParseEntryJSON(t *testing.T) { Value: "uid:1111", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Admin: true, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Admin: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry2 := &types.Entry{ Selectors: []*types.Selector{ @@ -83,11 +84,12 @@ func TestParseEntryJSON(t *testing.T) { Value: "uid:1111", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Hint: "internal", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Hint: "internal", + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } entry3 := &types.Entry{ Selectors: []*types.Selector{ @@ -100,17 +102,35 @@ func TestParseEntryJSON(t *testing.T) { Value: "key2:value", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - StoreSvid: true, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + StoreSvid: true, + X509SvidTtl: 200, + JwtSvidTtl: 30, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + } + entry4 := &types.Entry{ + Selectors: []*types.Selector{ + { + Type: "unix", + Value: "uid:1111", + }, + }, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/additionalattr"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, X509SvidTtl: 200, JwtSvidTtl: 30, + Admin: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, } expectedEntries := []*types.Entry{ entry1, entry2, entry3, + entry4, } spiretest.RequireProtoListEqual(t, expectedEntries, entries) }) diff --git a/cmd/spire-server/cli/entry/util_windows_test.go b/cmd/spire-server/cli/entry/util_windows_test.go index 06fd7e4942..7c56388a04 100644 --- a/cmd/spire-server/cli/entry/util_windows_test.go +++ b/cmd/spire-server/cli/entry/util_windows_test.go @@ -8,6 +8,8 @@ const ( If set, the SPIFFE ID in this entry will be granted access to the SPIRE Server's management APIs -data string Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin. + -disableX509SVIDPrefetch + A boolean value that, when set, disables prefetching X509 SVID for this entry -dns value A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once -downstream @@ -68,6 +70,8 @@ const ( If set, the SPIFFE ID in this entry will be granted access to the SPIRE Server's management APIs -data string Path to a file containing registration JSON (optional). If set to '-', read the JSON from stdin. + -disableX509SVIDPrefetch + A boolean value that, when set, disables prefetching X509 SVID for this entry -dns value A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once -downstream diff --git a/go.mod b/go.mod index 706e2507bf..c8ed161092 100644 --- a/go.mod +++ b/go.mod @@ -330,3 +330,5 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) + +replace github.com/spiffe/spire-api-sdk v1.2.5-0.20260115194754-bcd1999bdd05 => github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99 diff --git a/go.sum b/go.sum index cdc7c82b04..576f42c7ed 100644 --- a/go.sum +++ b/go.sum @@ -95,6 +95,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99 h1:vRWsIC4uMgdmZ+n7uJbxBwYM2g0oWSN7j8DjqAeeY0o= +github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99/go.mod h1:9hXJcMzatM1KwAtBDO3s6HccDCic++/5c2yOc5Iln8Y= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -812,8 +814,6 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= -github.com/spiffe/spire-api-sdk v1.2.5-0.20260115194754-bcd1999bdd05 h1:wJPyPSXHtKEc9SuK83sr40OoRSaYUTf1+wncvWvogHE= -github.com/spiffe/spire-api-sdk v1.2.5-0.20260115194754-bcd1999bdd05/go.mod h1:9hXJcMzatM1KwAtBDO3s6HccDCic++/5c2yOc5Iln8Y= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20251120194148-791bbd274dc7 h1:OAvr7TNirmBpXnAp82cTosuB+JAus5cyFCRqXHE0WHs= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20251120194148-791bbd274dc7/go.mod h1:QvrRDiBlXiJ7kNd176ZHsF5eklxxeTRgJSu2CXe0MKw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/common/protoutil/masks_test.go b/pkg/common/protoutil/masks_test.go index 6615839acb..ca0483d565 100644 --- a/pkg/common/protoutil/masks_test.go +++ b/pkg/common/protoutil/masks_test.go @@ -29,20 +29,21 @@ func TestAllTrueMasks(t *testing.T) { }, protoutil.AllTrueBundleMask) spiretest.AssertProtoEqual(t, &types.EntryMask{ - SpiffeId: true, - ParentId: true, - Selectors: true, - X509SvidTtl: true, - JwtSvidTtl: true, - FederatesWith: true, - Admin: true, - CreatedAt: true, - Downstream: true, - ExpiresAt: true, - DnsNames: true, - RevisionNumber: true, - StoreSvid: true, - Hint: true, + SpiffeId: true, + ParentId: true, + Selectors: true, + X509SvidTtl: true, + JwtSvidTtl: true, + FederatesWith: true, + Admin: true, + CreatedAt: true, + Downstream: true, + ExpiresAt: true, + DnsNames: true, + RevisionNumber: true, + StoreSvid: true, + Hint: true, + AdditionalAttributes: true, }, protoutil.AllTrueEntryMask) spiretest.AssertProtoEqual(t, &common.BundleMask{ diff --git a/pkg/server/api/entry.go b/pkg/server/api/entry.go index f3cbc14dd0..d1f6f20ef5 100644 --- a/pkg/server/api/entry.go +++ b/pkg/server/api/entry.go @@ -59,6 +59,10 @@ func (e *ReadOnlyEntry) GetCreatedAt() int64 { return e.entry.CreatedAt } +func (e *ReadOnlyEntry) GetAdditionalAttributes() *types.Entry_AdditionalAttributes { + return e.entry.AdditionalAttributes +} + // Manually clone the entry instead of using the protobuf helpers // since those are two times slower. func (e *ReadOnlyEntry) Clone(mask *types.EntryMask) *types.Entry { @@ -132,6 +136,10 @@ func (e *ReadOnlyEntry) Clone(mask *types.EntryMask) *types.Entry { clone.CreatedAt = e.entry.CreatedAt } + if mask.AdditionalAttributes { + clone.AdditionalAttributes = e.entry.AdditionalAttributes + } + return clone } @@ -179,23 +187,36 @@ func RegistrationEntryToProto(e *common.RegistrationEntry) (*types.Entry, error) } } - return &types.Entry{ - Id: e.EntryId, - SpiffeId: ProtoFromID(spiffeID), - ParentId: ProtoFromID(parentID), - Selectors: ProtoFromSelectors(e.Selectors), - X509SvidTtl: e.X509SvidTtl, - FederatesWith: federatesWith, - Admin: e.Admin, - Downstream: e.Downstream, - ExpiresAt: e.EntryExpiry, - DnsNames: slices.Clone(e.DnsNames), - RevisionNumber: e.RevisionNumber, - StoreSvid: e.StoreSvid, - JwtSvidTtl: e.JwtSvidTtl, - Hint: e.Hint, - CreatedAt: e.CreatedAt, - }, nil + entry := &types.Entry{ + Id: e.EntryId, + SpiffeId: ProtoFromID(spiffeID), + ParentId: ProtoFromID(parentID), + Selectors: ProtoFromSelectors(e.Selectors), + X509SvidTtl: e.X509SvidTtl, + FederatesWith: federatesWith, + Admin: e.Admin, + Downstream: e.Downstream, + ExpiresAt: e.EntryExpiry, + DnsNames: slices.Clone(e.DnsNames), + RevisionNumber: e.RevisionNumber, + StoreSvid: e.StoreSvid, + JwtSvidTtl: e.JwtSvidTtl, + Hint: e.Hint, + CreatedAt: e.CreatedAt, + AdditionalAttributes: ProtoFromAdditionalAttributes(e.AdditionalAttributes), + } + + return entry, nil + +} + +func ProtoFromAdditionalAttributes(in *common.RegistrationEntry_AdditionalAttributes) *types.Entry_AdditionalAttributes { + if in != nil { + return &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: in.DisableX509SvidPrefetch, + } + } + return nil } // ProtoToRegistrationEntry converts and validate entry into common registration entry diff --git a/pkg/server/api/entry_test.go b/pkg/server/api/entry_test.go index 8254e20d45..c738c6caa4 100644 --- a/pkg/server/api/entry_test.go +++ b/pkg/server/api/entry_test.go @@ -703,14 +703,15 @@ func TestReadOnlyEntry(t *testing.T) { "domain1.com", "domain2.com", }, - Admin: true, - ExpiresAt: expiresAt, - DnsNames: []string{"dns1", "dns2"}, - Downstream: true, - RevisionNumber: 99, - Hint: "external", - CreatedAt: 1678731397, - StoreSvid: true, + Admin: true, + ExpiresAt: expiresAt, + DnsNames: []string{"dns1", "dns2"}, + Downstream: true, + RevisionNumber: 99, + Hint: "external", + CreatedAt: 1678731397, + StoreSvid: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{}, } // Verify that all getters return the expected value @@ -722,6 +723,7 @@ func TestReadOnlyEntry(t *testing.T) { require.Equal(t, readOnlyEntry.GetDnsNames(), entry.DnsNames) require.Equal(t, readOnlyEntry.GetRevisionNumber(), entry.RevisionNumber) require.Equal(t, readOnlyEntry.GetCreatedAt(), entry.CreatedAt) + require.Equal(t, readOnlyEntry.GetAdditionalAttributes(), entry.AdditionalAttributes) } func TestReadOnlyEntryClone(t *testing.T) { @@ -748,6 +750,9 @@ func TestReadOnlyEntryClone(t *testing.T) { Hint: "external", CreatedAt: 1678731397, StoreSvid: true, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: false, + }, } // Verify that we our test entry has all fields set to make sure diff --git a/test/fixture/registration/good-for-update.json b/test/fixture/registration/good-for-update.json index 4df150afc8..cd75b8bb52 100644 --- a/test/fixture/registration/good-for-update.json +++ b/test/fixture/registration/good-for-update.json @@ -13,7 +13,8 @@ "x509_svid_ttl": 200, "jwt_svid_ttl": 300, "admin": true, - "hint": "external" + "hint": "external", + "additional_attributes": {} }, { "entry_id": "entry-id-2", @@ -26,7 +27,8 @@ "spiffe_id": "spiffe://example.org/Database", "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, - "jwt_svid_ttl": 300 + "jwt_svid_ttl": 300, + "additional_attributes": {} }, { "entry_id": "entry-id-3", @@ -44,7 +46,8 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "store_svid": true, "x509_svid_ttl": 200, - "jwt_svid_ttl": 300 + "jwt_svid_ttl": 300, + "additional_attributes": {} } ] } diff --git a/test/fixture/registration/good.json b/test/fixture/registration/good.json index 42099149ff..488cabe6c8 100644 --- a/test/fixture/registration/good.json +++ b/test/fixture/registration/good.json @@ -11,7 +11,8 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenBlog", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "admin": true + "admin": true, + "additional_attributes": {} }, { "selectors": [ @@ -24,7 +25,8 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "hint": "internal" + "hint": "internal", + "additional_attributes": {} }, { "selectors": [ @@ -41,7 +43,24 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "store_svid": true + "store_svid": true, + "additional_attributes": {} + }, + { + "selectors": [ + { + "type": "unix", + "value": "uid:1111" + } + ], + "spiffe_id": "spiffe://example.org/additionalattr", + "parent_id": "spiffe://example.org/spire/agent/join_token/TokenBlog", + "x509_svid_ttl": 200, + "jwt_svid_ttl": 30, + "admin": true, + "additional_attributes": { + "disable_x509_svid_prefetch": true + } } ] } From d1db089adda40f04e1a112ce509d975ba1c00a44 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 11 Feb 2026 18:07:32 +0000 Subject: [PATCH 27/51] added disableX509SVIDPrefetch to server cli docs Signed-off-by: Valentin Fadeev --- doc/spire_server.md | 68 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/doc/spire_server.md b/doc/spire_server.md index 1b6f0a29ba..167d37da6b 100644 --- a/doc/spire_server.md +++ b/doc/spire_server.md @@ -379,44 +379,46 @@ human-readable registration entry name in addition to the token-based ID. Creates registration entries. -| Command | Action | Default | -|:-----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------| -| `-admin` | If set, the SPIFFE ID in this entry will be granted access to the Server APIs | | -| `-data` | Path to a file containing registration data in JSON format (optional, if specified, other flags related with entry information must be omitted). If set to '-', read the JSON from stdin. | | -| `-dns` | A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once | | -| `-downstream` | A boolean value that, when set, indicates that the entry describes a downstream SPIRE server | | -| `-entryExpiry` | An expiry, from epoch in seconds, for the resulting registration entry to be pruned from the datastore. Please note that this is a data management feature and not a security feature (optional). | | -| `-entryID` | A user-specified ID for the newly created registration entry (optional). If no entry ID is provided, one will be generated during creation | | -| `-federatesWith` | A list of trust domain SPIFFE IDs representing the trust domains this registration entry federates with. A bundle for that trust domain must already exist | | -| `-node` | If set, this entry will be applied to matching nodes rather than workloads | | -| `-parentID` | The SPIFFE ID of this record's parent. | | -| `-selector` | A colon-delimited type:value selector used for attestation. This parameter can be used more than once, to specify multiple selectors that must be satisfied. | | -| `-socketPath` | Path to the SPIRE Server API socket | /tmp/spire-server/private/api.sock | -| `-spiffeID` | The SPIFFE ID that this record represents and will be set to the SVID issued. | | -| `-x509SVIDTTL` | A TTL, in seconds, for any X509-SVID issued as a result of this record. | The TTL configured with `default_x509_svid_ttl` | -| `-jwtSVIDTTL` | A TTL, in seconds, for any JWT-SVID issued as a result of this record. | The TTL configured with `default_jwt_svid_ttl` | -| `-storeSVID` | A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin | | +| Command | Action | Default | +|:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------| +| `-admin` | If set, the SPIFFE ID in this entry will be granted access to the Server APIs | | +| `-data` | Path to a file containing registration data in JSON format (optional, if specified, other flags related with entry information must be omitted). If set to '-', read the JSON from stdin. | | +| `-disableX509SVIDPrefetch` | A boolean value that, when set, disables prefetching X509 SVID for this entry | `false` | +| `-dns` | A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once | | +| `-downstream` | A boolean value that, when set, indicates that the entry describes a downstream SPIRE server | | +| `-entryExpiry` | An expiry, from epoch in seconds, for the resulting registration entry to be pruned from the datastore. Please note that this is a data management feature and not a security feature (optional). | | +| `-entryID` | A user-specified ID for the newly created registration entry (optional). If no entry ID is provided, one will be generated during creation | | +| `-federatesWith` | A list of trust domain SPIFFE IDs representing the trust domains this registration entry federates with. A bundle for that trust domain must already exist | | +| `-node` | If set, this entry will be applied to matching nodes rather than workloads | | +| `-parentID` | The SPIFFE ID of this record's parent. | | +| `-selector` | A colon-delimited type:value selector used for attestation. This parameter can be used more than once, to specify multiple selectors that must be satisfied. | | +| `-socketPath` | Path to the SPIRE Server API socket | /tmp/spire-server/private/api.sock | +| `-spiffeID` | The SPIFFE ID that this record represents and will be set to the SVID issued. | | +| `-x509SVIDTTL` | A TTL, in seconds, for any X509-SVID issued as a result of this record. | The TTL configured with `default_x509_svid_ttl` | +| `-jwtSVIDTTL` | A TTL, in seconds, for any JWT-SVID issued as a result of this record. | The TTL configured with `default_jwt_svid_ttl` | +| `-storeSVID` | A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin | | ### `spire-server entry update` Updates registration entries. -| Command | Action | Default | -|:-----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------| -| `-admin` | If true, the SPIFFE ID in this entry will be granted access to the Server APIs | | -| `-data` | Path to a file containing registration data in JSON format (optional, if specified, other flags related with entry information must be omitted). If set to '-', read the JSON from stdin. | | -| `-dns` | A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once | | -| `-downstream` | A boolean value that, when set, indicates that the entry describes a downstream SPIRE server | | -| `-entryExpiry` | An expiry, from epoch in seconds, for the resulting registration entry to be pruned | | -| `-entryID` | The Registration Entry ID of the record to update | | -| `-federatesWith` | A list of trust domain SPIFFE IDs representing the trust domains this registration entry federates with. A bundle for that trust domain must already exist | | -| `-parentID` | The SPIFFE ID of this record's parent. | | -| `-selector` | A colon-delimited type:value selector used for attestation. This parameter can be used more than once, to specify multiple selectors that must be satisfied. | | -| `-socketPath` | Path to the SPIRE Server API socket | /tmp/spire-server/private/api.sock | -| `-spiffeID` | The SPIFFE ID that this record represents and will be set to the SVID issued. | | -| `-x509SVIDTTL` | A TTL, in seconds, for any X509-SVID issued as a result of this record. | The TTL configured with `default_x509_svid_ttl` | -| `-jwtSVIDTTL` | A TTL, in seconds, for any JWT-SVID issued as a result of this record. | The TTL configured with `default_jwt_svid_ttl` | -| `storeSVID` | A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin | | +| Command | Action | Default | +|:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------| +| `-admin` | If set, the SPIFFE ID in this entry will be granted access to the Server APIs | | +| `-data` | Path to a file containing registration data in JSON format (optional, if specified, other flags related with entry information must be omitted). If set to '-', read the JSON from stdin. | | +| `-disableX509SVIDPrefetch` | A boolean value that, when set, disables prefetching X509 SVID for this entry | `false` | +| `-dns` | A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once | | +| `-downstream` | A boolean value that, when set, indicates that the entry describes a downstream SPIRE server | | +| `-entryExpiry` | An expiry, from epoch in seconds, for the resulting registration entry to be pruned from the datastore. Please note that this is a data management feature and not a security feature (optional). | | +| `-entryID` | A user-specified ID for the newly created registration entry (optional). If no entry ID is provided, one will be generated during creation | | +| `-federatesWith` | A list of trust domain SPIFFE IDs representing the trust domains this registration entry federates with. A bundle for that trust domain must already exist | | +| `-parentID` | The SPIFFE ID of this record's parent. | | +| `-selector` | A colon-delimited type:value selector used for attestation. This parameter can be used more than once, to specify multiple selectors that must be satisfied. | | +| `-socketPath` | Path to the SPIRE Server API socket | /tmp/spire-server/private/api.sock | +| `-spiffeID` | The SPIFFE ID that this record represents and will be set to the SVID issued. | | +| `-x509SVIDTTL` | A TTL, in seconds, for any X509-SVID issued as a result of this record. | The TTL configured with `default_x509_svid_ttl` | +| `-jwtSVIDTTL` | A TTL, in seconds, for any JWT-SVID issued as a result of this record. | The TTL configured with `default_jwt_svid_ttl` | +| `-storeSVID` | A boolean value that, when set, indicates that the resulting issued SVID from this entry must be stored through an SVIDStore plugin | | ### `spire-server entry count` From 29b19e0d7c171ef67b359af51a4ea7a8d86f33e7 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 11 Feb 2026 18:37:22 +0000 Subject: [PATCH 28/51] addressed linter finding Signed-off-by: Valentin Fadeev --- pkg/server/api/entry.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/server/api/entry.go b/pkg/server/api/entry.go index d1f6f20ef5..d31b5f46f5 100644 --- a/pkg/server/api/entry.go +++ b/pkg/server/api/entry.go @@ -207,7 +207,6 @@ func RegistrationEntryToProto(e *common.RegistrationEntry) (*types.Entry, error) } return entry, nil - } func ProtoFromAdditionalAttributes(in *common.RegistrationEntry_AdditionalAttributes) *types.Entry_AdditionalAttributes { From 187da054cefa48b0af51e6a332d0008ead34f2dd Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Tue, 17 Feb 2026 16:39:07 +0000 Subject: [PATCH 29/51] create additional attributes conditionally Signed-off-by: Valentin Fadeev --- cmd/spire-server/cli/entry/create.go | 8 +- cmd/spire-server/cli/entry/create_test.go | 60 +++--- cmd/spire-server/cli/entry/update.go | 8 +- cmd/spire-server/cli/entry/update_test.go | 202 +++++++++++------- cmd/spire-server/cli/entry/util_test.go | 33 ++- pkg/server/api/entry.go | 49 +++-- pkg/server/api/entry_test.go | 17 +- .../fixture/registration/good-for-update.json | 27 ++- test/fixture/registration/good.json | 9 +- 9 files changed, 244 insertions(+), 169 deletions(-) diff --git a/cmd/spire-server/cli/entry/create.go b/cmd/spire-server/cli/entry/create.go index e1d56b8eab..ffaea76b00 100644 --- a/cmd/spire-server/cli/entry/create.go +++ b/cmd/spire-server/cli/entry/create.go @@ -216,9 +216,11 @@ func (c *createCommand) parseConfig() ([]*types.Entry, error) { } e.Selectors = selectors - additionalAttributes := &types.Entry_AdditionalAttributes{} - additionalAttributes.DisableX509SvidPrefetch = c.disableX509SVIDPrefetch - e.AdditionalAttributes = additionalAttributes + if c.disableX509SVIDPrefetch { + e.AdditionalAttributes = &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: c.disableX509SVIDPrefetch, + } + } e.FederatesWith = c.federatesWith e.Admin = c.admin diff --git a/cmd/spire-server/cli/entry/create_test.go b/cmd/spire-server/cli/entry/create_test.go index 314b142254..291edeed6c 100644 --- a/cmd/spire-server/cli/entry/create_test.go +++ b/cmd/spire-server/cli/entry/create_test.go @@ -392,14 +392,13 @@ DisableX509SvidPrefetch : true {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - StoreSvid: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + X509SvidTtl: 60, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + StoreSvid: true, }, }, }, @@ -479,22 +478,20 @@ StoreSvid : true expReq: &entryv1.BatchCreateEntryRequest{ Entries: []*types.Entry{ { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Admin: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Admin: true, }, { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Hint: "internal", - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Hint: "internal", }, { SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, @@ -503,10 +500,9 @@ StoreSvid : true {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, - X509SvidTtl: 200, - JwtSvidTtl: 30, - StoreSvid: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + StoreSvid: true, }, { SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/additionalattr"}, @@ -723,10 +719,9 @@ DisableX509SvidPrefetch : true args: []string{"-spiffeID", "spiffe://example.org/already-exist", "-node", "-selector", "unix:uid:1"}, expReq: &entryv1.BatchCreateEntryRequest{Entries: []*types.Entry{ { - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/already-exist"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/server"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/already-exist"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/server"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, }, }}, fakeResp: fakeRespErr, @@ -774,10 +769,7 @@ Error: failed to create one or more entries "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 0, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "jwt_svid_ttl": 0 } } ] diff --git a/cmd/spire-server/cli/entry/update.go b/cmd/spire-server/cli/entry/update.go index 10bb50b2cf..2e228a54a4 100644 --- a/cmd/spire-server/cli/entry/update.go +++ b/cmd/spire-server/cli/entry/update.go @@ -210,9 +210,11 @@ func (c *updateCommand) parseConfig() ([]*types.Entry, error) { e.Selectors = selectors - additionalAttributes := &types.Entry_AdditionalAttributes{} - additionalAttributes.DisableX509SvidPrefetch = c.disableX509SVIDPrefetch - e.AdditionalAttributes = additionalAttributes + if c.disableX509SVIDPrefetch { + e.AdditionalAttributes = &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: c.disableX509SVIDPrefetch, + } + } e.FederatesWith = c.federatesWith e.Admin = c.admin diff --git a/cmd/spire-server/cli/entry/update_test.go b/cmd/spire-server/cli/entry/update_test.go index dde8b63948..f8f6a926d6 100644 --- a/cmd/spire-server/cli/entry/update_test.go +++ b/cmd/spire-server/cli/entry/update_test.go @@ -61,10 +61,7 @@ func TestUpdate(t *testing.T) { ], "revision_number": "0", "store_svid": true, - "jwt_svid_ttl": 30, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "jwt_svid_ttl": 30 }` entry0AdminJSON := `{ "id": "entry-id", @@ -102,10 +99,7 @@ func TestUpdate(t *testing.T) { ], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 30, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "jwt_svid_ttl": 30 }` entry1JSON := `{ "id": "entry-id-1", @@ -133,10 +127,7 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 300, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "jwt_svid_ttl": 300 } }` entry2JSON := `{ @@ -165,10 +156,7 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "jwt_svid_ttl": 300, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "jwt_svid_ttl": 300 } }` entry3JSON := `{ @@ -201,11 +189,45 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": true, + "jwt_svid_ttl": 300 + } + }` + entry4JSON := `{ + "id": "entry-id-4", + "spiffe_id": { + "trust_domain": "example.org", + "path": "/DisableX509SvidPrefetch" + }, + "parent_id": { + "trust_domain": "example.org", + "path": "/spire/agent/join_token/TokenDatabase" + }, + "selectors": [ + { + "type": "type", + "value": "key1:value" + }, + { + "type": "type", + "value": "key2:value" + } + ], + "x509_svid_ttl": 200, + "federates_with": [], + "hint": "", + "admin": false, + "created_at": "1547583197", + "downstream": false, + "expires_at": "0", + "dns_names": [], + "revision_number": "0", "jwt_svid_ttl": 300, + "store_svid": false, "additional_attributes": { - "disable_x509_svid_prefetch": false - } - }` + "disable_x509_svid_prefetch": true + } + } + }` nonExistentEntryJSON := `{ "id": "non-existent-id", "spiffe_id": { @@ -232,10 +254,7 @@ func TestUpdate(t *testing.T) { "dns_names": [], "revision_number": "0", "store_svid": false, - "x509_svid_ttl": 0, - "additional_attributes": { - "disable_x509_svid_prefetch": false - } + "x509_svid_ttl": 0 }` entry1 := &types.Entry{ @@ -246,15 +265,14 @@ func TestUpdate(t *testing.T) { {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - Hint: "external", - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + Hint: "external", } entry0Admin := &types.Entry{ @@ -265,16 +283,15 @@ func TestUpdate(t *testing.T) { {Type: "zebra", Value: "zebra:2000"}, {Type: "alpha", Value: "alpha:2000"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - Admin: true, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - Downstream: true, - Hint: "external", - CreatedAt: 1547583197, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + Admin: true, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + Downstream: true, + Hint: "external", + CreatedAt: 1547583197, } entryStoreSVID := &types.Entry{ @@ -285,13 +302,12 @@ func TestUpdate(t *testing.T) { {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, - X509SvidTtl: 60, - JwtSvidTtl: 30, - FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, - ExpiresAt: 1552410266, - DnsNames: []string{"unu1000", "ung1000"}, - StoreSvid: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + X509SvidTtl: 60, + JwtSvidTtl: 30, + FederatesWith: []string{"spiffe://domaina.test", "spiffe://domainb.test"}, + ExpiresAt: 1552410266, + DnsNames: []string{"unu1000", "ung1000"}, + StoreSvid: true, } entryStoreSVIDResp := proto.Clone(entryStoreSVID).(*types.Entry) @@ -310,25 +326,23 @@ func TestUpdate(t *testing.T) { } entry2 := &types.Entry{ - Id: "entry-id-1", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 300, - Admin: true, - Hint: "external", - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + Id: "entry-id-1", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 300, + Admin: true, + Hint: "external", } entry3 := &types.Entry{ - Id: "entry-id-2", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, - X509SvidTtl: 200, - JwtSvidTtl: 300, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + Id: "entry-id-2", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1111"}}, + X509SvidTtl: 200, + JwtSvidTtl: 300, } entry4 := &types.Entry{ @@ -339,10 +353,24 @@ func TestUpdate(t *testing.T) { {Type: "type", Value: "key1:value"}, {Type: "type", Value: "key2:value"}, }, - StoreSvid: true, - X509SvidTtl: 200, - JwtSvidTtl: 300, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + StoreSvid: true, + X509SvidTtl: 200, + JwtSvidTtl: 300, + } + + entry5 := &types.Entry{ + Id: "entry-id-4", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/DisableX509SvidPrefetch"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + Selectors: []*types.Selector{ + {Type: "type", Value: "key1:value"}, + {Type: "type", Value: "key2:value"}, + }, + X509SvidTtl: 200, + JwtSvidTtl: 300, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, } entry2Resp := proto.Clone(entry2).(*types.Entry) @@ -351,6 +379,8 @@ func TestUpdate(t *testing.T) { entry3Resp.CreatedAt = 1547583197 entry4Resp := proto.Clone(entry4).(*types.Entry) entry4Resp.CreatedAt = 1547583197 + entry5Resp := proto.Clone(entry5).(*types.Entry) + entry5Resp.CreatedAt = 1547583197 fakeRespOKFromFile := &entryv1.BatchUpdateEntryResponse{ Results: []*entryv1.BatchUpdateEntryResponse_Result{ @@ -366,6 +396,10 @@ func TestUpdate(t *testing.T) { Entry: entry4Resp, Status: &types.Status{Code: int32(codes.OK), Message: "OK"}, }, + { + Entry: entry5Resp, + Status: &types.Status{Code: int32(codes.OK), Message: "OK"}, + }, }, } @@ -556,7 +590,7 @@ StoreSvid : true "-data", "../../../../test/fixture/registration/good-for-update.json", }, expReq: &entryv1.BatchUpdateEntryRequest{ - Entries: []*types.Entry{entry2, entry3, entry4}, + Entries: []*types.Entry{entry2, entry3, entry4, entry5}, }, fakeResp: fakeRespOKFromFile, expOutPretty: `Entry ID : entry-id-1 @@ -587,6 +621,16 @@ Selector : type:key1:value Selector : type:key2:value StoreSvid : true +Entry ID : entry-id-4 +SPIFFE ID : spiffe://example.org/DisableX509SvidPrefetch +Parent ID : spiffe://example.org/spire/agent/join_token/TokenDatabase +Revision : 0 +X509-SVID TTL : 200 +JWT-SVID TTL : 300 +Selector : type:key1:value +Selector : type:key2:value +DisableX509SvidPrefetch : true + `, expOutJSON: fmt.Sprintf(` { @@ -603,26 +647,30 @@ StoreSvid : true "message": "OK" }, "entry": %s, + { + "status": { + "code": 0, + "message": "OK" + }, + "entry": %s, { "status": { "code": 0, "message": "OK" }, "entry": %s - } ] -}`, entry1JSON, entry2JSON, entry3JSON), +}`, entry1JSON, entry2JSON, entry3JSON, entry4JSON), }, { name: "Entry not found", args: []string{"-entryID", "non-existent-id", "-spiffeID", "spiffe://example.org/workload", "-parentID", "spiffe://example.org/parent", "-selector", "unix:uid:1"}, expReq: &entryv1.BatchUpdateEntryRequest{Entries: []*types.Entry{ { - Id: "non-existent-id", - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, - Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + Id: "non-existent-id", + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/workload"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/parent"}, + Selectors: []*types.Selector{{Type: "unix", Value: "uid:1"}}, }, }}, fakeResp: fakeRespErr, diff --git a/cmd/spire-server/cli/entry/util_test.go b/cmd/spire-server/cli/entry/util_test.go index 9f70a1a5e6..191889aa08 100644 --- a/cmd/spire-server/cli/entry/util_test.go +++ b/cmd/spire-server/cli/entry/util_test.go @@ -70,12 +70,11 @@ func TestParseEntryJSON(t *testing.T) { Value: "uid:1111", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Admin: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Blog"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenBlog"}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Admin: true, } entry2 := &types.Entry{ Selectors: []*types.Selector{ @@ -84,12 +83,11 @@ func TestParseEntryJSON(t *testing.T) { Value: "uid:1111", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - X509SvidTtl: 200, - JwtSvidTtl: 30, - Hint: "internal", - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/Database"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + X509SvidTtl: 200, + JwtSvidTtl: 30, + Hint: "internal", } entry3 := &types.Entry{ Selectors: []*types.Selector{ @@ -102,12 +100,11 @@ func TestParseEntryJSON(t *testing.T) { Value: "key2:value", }, }, - SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, - ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, - StoreSvid: true, - X509SvidTtl: 200, - JwtSvidTtl: 30, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + SpiffeId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/storesvid"}, + ParentId: &types.SPIFFEID{TrustDomain: "example.org", Path: "/spire/agent/join_token/TokenDatabase"}, + StoreSvid: true, + X509SvidTtl: 200, + JwtSvidTtl: 30, } entry4 := &types.Entry{ Selectors: []*types.Selector{ diff --git a/pkg/server/api/entry.go b/pkg/server/api/entry.go index d31b5f46f5..3ee7d0c752 100644 --- a/pkg/server/api/entry.go +++ b/pkg/server/api/entry.go @@ -206,6 +206,11 @@ func RegistrationEntryToProto(e *common.RegistrationEntry) (*types.Entry, error) AdditionalAttributes: ProtoFromAdditionalAttributes(e.AdditionalAttributes), } + additionalAttributes := ProtoFromAdditionalAttributes(e.AdditionalAttributes) + if additionalAttributes != nil { + entry.AdditionalAttributes = additionalAttributes + } + return entry, nil } @@ -218,6 +223,15 @@ func ProtoFromAdditionalAttributes(in *common.RegistrationEntry_AdditionalAttrib return nil } +func AdditionalAttributesFromProto(in *types.Entry_AdditionalAttributes) *common.RegistrationEntry_AdditionalAttributes { + if in != nil { + return &common.RegistrationEntry_AdditionalAttributes{ + DisableX509SvidPrefetch: in.DisableX509SvidPrefetch, + } + } + return nil +} + // ProtoToRegistrationEntry converts and validate entry into common registration entry func ProtoToRegistrationEntry(ctx context.Context, td spiffeid.TrustDomain, e *types.Entry) (*common.RegistrationEntry, error) { return ProtoToRegistrationEntryWithMask(ctx, td, e, nil) @@ -329,20 +343,27 @@ func ProtoToRegistrationEntryWithMask(ctx context.Context, td spiffeid.TrustDoma } hint = e.Hint } + + var additionalAttributes *common.RegistrationEntry_AdditionalAttributes + if mask.AdditionalAttributes { + additionalAttributes = AdditionalAttributesFromProto(e.AdditionalAttributes) + } + return &common.RegistrationEntry{ - EntryId: e.Id, - ParentId: parentID.String(), - SpiffeId: spiffeID.String(), - Admin: admin, - DnsNames: dnsNames, - Downstream: downstream, - EntryExpiry: expiresAt, - FederatesWith: federatesWith, - Selectors: selectors, - RevisionNumber: revisionNumber, - StoreSvid: storeSVID, - X509SvidTtl: x509SvidTTL, - JwtSvidTtl: jwtSvidTTL, - Hint: hint, + EntryId: e.Id, + ParentId: parentID.String(), + SpiffeId: spiffeID.String(), + Admin: admin, + DnsNames: dnsNames, + Downstream: downstream, + EntryExpiry: expiresAt, + FederatesWith: federatesWith, + Selectors: selectors, + RevisionNumber: revisionNumber, + StoreSvid: storeSVID, + X509SvidTtl: x509SvidTTL, + JwtSvidTtl: jwtSvidTTL, + Hint: hint, + AdditionalAttributes: additionalAttributes, }, nil } diff --git a/pkg/server/api/entry_test.go b/pkg/server/api/entry_test.go index c738c6caa4..a410f025e2 100644 --- a/pkg/server/api/entry_test.go +++ b/pkg/server/api/entry_test.go @@ -703,15 +703,14 @@ func TestReadOnlyEntry(t *testing.T) { "domain1.com", "domain2.com", }, - Admin: true, - ExpiresAt: expiresAt, - DnsNames: []string{"dns1", "dns2"}, - Downstream: true, - RevisionNumber: 99, - Hint: "external", - CreatedAt: 1678731397, - StoreSvid: true, - AdditionalAttributes: &types.Entry_AdditionalAttributes{}, + Admin: true, + ExpiresAt: expiresAt, + DnsNames: []string{"dns1", "dns2"}, + Downstream: true, + RevisionNumber: 99, + Hint: "external", + CreatedAt: 1678731397, + StoreSvid: true, } // Verify that all getters return the expected value diff --git a/test/fixture/registration/good-for-update.json b/test/fixture/registration/good-for-update.json index cd75b8bb52..934dd2df01 100644 --- a/test/fixture/registration/good-for-update.json +++ b/test/fixture/registration/good-for-update.json @@ -13,8 +13,7 @@ "x509_svid_ttl": 200, "jwt_svid_ttl": 300, "admin": true, - "hint": "external", - "additional_attributes": {} + "hint": "external" }, { "entry_id": "entry-id-2", @@ -27,8 +26,7 @@ "spiffe_id": "spiffe://example.org/Database", "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, - "jwt_svid_ttl": 300, - "additional_attributes": {} + "jwt_svid_ttl": 300 }, { "entry_id": "entry-id-3", @@ -46,8 +44,27 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "store_svid": true, "x509_svid_ttl": 200, + "jwt_svid_ttl": 300 + }, + { + "entry_id": "entry-id-4", + "selectors": [ + { + "type": "type", + "value": "key1:value" + }, + { + "type": "type", + "value": "key2:value" + } + ], + "spiffe_id": "spiffe://example.org/DisableX509SvidPrefetch", + "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", + "x509_svid_ttl": 200, "jwt_svid_ttl": 300, - "additional_attributes": {} + "additional_attributes": { + "disable_x509_svid_prefetch": true + } } ] } diff --git a/test/fixture/registration/good.json b/test/fixture/registration/good.json index 488cabe6c8..2172365b82 100644 --- a/test/fixture/registration/good.json +++ b/test/fixture/registration/good.json @@ -11,8 +11,7 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenBlog", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "admin": true, - "additional_attributes": {} + "admin": true }, { "selectors": [ @@ -25,8 +24,7 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "hint": "internal", - "additional_attributes": {} + "hint": "internal" }, { "selectors": [ @@ -43,8 +41,7 @@ "parent_id": "spiffe://example.org/spire/agent/join_token/TokenDatabase", "x509_svid_ttl": 200, "jwt_svid_ttl": 30, - "store_svid": true, - "additional_attributes": {} + "store_svid": true }, { "selectors": [ From 3ae4eccc856c6f20dd67479aaeaa145a016f8241 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 18 Feb 2026 08:43:16 +0000 Subject: [PATCH 30/51] updated integration test to check cached svids Signed-off-by: Valentin Fadeev --- test/integration/common | 24 +++++++++++++++++-- .../suites/entries/02-start-agents | 13 ++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/test/integration/common b/test/integration/common index 2dd9777468..51201635b7 100644 --- a/test/integration/common +++ b/test/integration/common @@ -169,6 +169,26 @@ check-svid-count() { fi } +check-debug-info() { + MAXCHECKS=50 + CHECKINTERVAL=1 + + for ((i=1;i<=MAXCHECKS;i++)); do + log-info "check $2 on agent debug endpoint ($((i)) of $MAXCHECKS max)..." + COUNT=$(docker compose exec -T "$1" /opt/spire/conf/agent/debugclient -testCase "printDebugPage" | jq $(printf "'.%s'" "$2")) + log-info "$2: ${COUNT}" + if [ "$COUNT" -eq "$3" ]; then + log-info "SVID count of $COUNT matches the expected count of $3" + break + fi + sleep "${CHECKINTERVAL}" + done + + if (( i>MAXCHECKS )); then + fail-now "$2 validation failed" + fi +} + build-mashup-image() { ENVOY_VERSION=$1 ENVOY_IMAGE_TAG="${ENVOY_VERSION}-latest" @@ -253,7 +273,7 @@ download-kind() { elif [ "${ARCH}" = "aarch64" ]; then ARCH=arm64 fi - echo "Ensuring kind version $KINDVERSION is available..." + echo "Ensuring kind version $KINDVERSION is available..." KINDURL="https://github.com/kubernetes-sigs/kind/releases/download/$KINDVERSION/kind-$UNAME-$ARCH" local kind_link_or_path=$1 @@ -276,7 +296,7 @@ download-helm() { ARCH=arm64 fi - echo "Ensuring helm version $HELMVERSION is available..." + echo "Ensuring helm version $HELMVERSION is available..." HELMURL="https://get.helm.sh/helm-${HELMVERSION}-${UNAME}-${ARCH}.tar.gz" local helm_link_or_path=$1 diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index 3873648d7b..905ae51ac2 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -54,10 +54,23 @@ docker compose exec -T spire-server \ -selector "unix:uid:1003" \ -dns "example.org" +docker compose exec -T spire-server \ + /opt/spire/bin/spire-server entry create \ + -entryID prefetch-disabled \ + -parentID "spiffe://domain.test/cluster/test" \ + -spiffeID "spiffe://domain.test/workload-with-prefetch-disabled" \ + -selector "unix:uid:1004" \ + -disableX509SVIDPrefetch + check-synced-entry "spire-agent-1" "spiffe://domain.test/workload-agent-1" check-synced-entry "spire-agent-2" "spiffe://domain.test/workload-agent-2" for agent in spire-agent-1 spire-agent-2 spire-agent-3; do check-synced-entry ${agent} "spiffe://domain.test/workload-shared" check-synced-entry ${agent} "spiffe://domain.test/workload-with-dns" + check-synced-entry ${agent} "spiffe://domain.test/workload-with-prefetch-disabled" +done + +for agent in spire-agent-1 spire-agent-2 spire-agent-3; do + check-debug-info ${agent} "cachedX509SvidsCount" 2 done From d156b8f9ad0c61ba1e82ef1f392137a467621152 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 18 Feb 2026 10:51:56 +0000 Subject: [PATCH 31/51] created a separate migration for registration entries Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 15 ++++++- .../datastore/sqlstore/migration_test.go | 41 +++++++++++++++++++ .../datastore/sqlstore/sqlstore_test.go | 3 ++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 7abd5e6963..e984c710fe 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -273,7 +273,7 @@ import ( const ( // the latest schema version of the database in the code - latestSchemaVersion = 24 + latestSchemaVersion = 25 // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned @@ -506,6 +506,8 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi switch currVersion { case 23: err = migrateToV24(tx) + case 24: + err = migrateToV25(tx) default: err = newSQLError("no migration support for unknown schema version %d", currVersion) } @@ -517,7 +519,16 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi } func migrateToV24(tx *gorm.DB) error { - if err := tx.AutoMigrate(&AttestedNode{}, &RegisteredEntry{}).Error; err != nil { + // Add agent_version column to attested_node_entries table + if err := tx.AutoMigrate(&AttestedNode{}).Error; err != nil { + return newWrappedSQLError(err) + } + return nil +} + +func migrateToV25(tx *gorm.DB) error { + // Add additional_attributes column to registered_entries table + if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { return newWrappedSQLError(err) } return nil diff --git a/pkg/server/datastore/sqlstore/migration_test.go b/pkg/server/datastore/sqlstore/migration_test.go index 9c0b11f64d..0b3d2a7138 100644 --- a/pkg/server/datastore/sqlstore/migration_test.go +++ b/pkg/server/datastore/sqlstore/migration_test.go @@ -57,6 +57,47 @@ var ( CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; COMMIT; `, + 24: ` + PRAGMA foreign_keys=OFF; + BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS "federated_registration_entries" ("bundle_id" integer,"registered_entry_id" integer, PRIMARY KEY ("bundle_id","registered_entry_id")); + CREATE TABLE IF NOT EXISTS "bundles" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"data" blob ); + INSERT INTO bundles VALUES(1,'2026-02-18 10:42:12.139101+00:00','2026-02-18 10:42:12.140014+00:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712df030adc03308201d83082015ea0030201020214449db4c88cda977653f4d5e4770aec9b4b1e970c300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3233303531353032303530365a170d3238303531333032303530365a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b8104002203620004f57073b72f16fdec785ebd117735018227bfa2475a51385e485d0f42f540693b1768fd49ef2bf40e195ac38e48ec2bfd1cfdb51ce98cc48959d177aab0e97db0ce47e7b1c1416bb46c83577f0e2375e1dd079be4d57c8dc81410c5e5294b1867a35d305b301d0603551d0e04160414928ae360c6aaa7cf6aff8d1716b0046aa61c10ff300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040368003065023100e7843c85f844778a95c9cc1b2cdcce9bf1d0ae9d67d7e6b6c5cf3c894d37e8530f6a7711d4f2ea82c3833df5b2b6d75102300a2287548b879888c6bdf88dab55b8fc80ec490059f484b2c4177403997b463e9011b3da82f8a6e29254eee45a6293641a85010a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004c5f71bf758cacd8d14b8cf7feac452344ef4e6179e90a7c9827119ec56812d37cf5da87c41c6e2aade917438e6e1e85511d70785d9eacceb5c97d49b9d876a4f122069484c38587a6433556936664b42576f686252454e6a626b62715945557665771884d2dbcc062801'); + CREATE TABLE IF NOT EXISTS "attested_node_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"data_type" varchar(255),"serial_number" varchar(255),"expires_at" datetime,"new_serial_number" varchar(255),"new_expires_at" datetime,"can_reattest" bool,"agent_version" varchar(255) ); + CREATE TABLE IF NOT EXISTS "attested_node_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "node_resolver_map_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "registered_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255),"spiffe_id" varchar(255),"parent_id" varchar(255),"ttl" integer,"admin" bool,"downstream" bool,"expiry" bigint,"revision_number" bigint,"store_svid" bool,"hint" varchar(255),"jwt_svid_ttl" integer ); + CREATE TABLE IF NOT EXISTS "registered_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "join_tokens" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"token" varchar(255),"expiry" bigint ); + CREATE TABLE IF NOT EXISTS "selectors" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer,"code_version" varchar(255) ); + INSERT INTO migrations VALUES(1,'2026-02-18 10:42:12.131652+00:00','2026-02-18 10:42:12.131652+00:00',24,'1.14.2-dev-27d2083'); + CREATE TABLE IF NOT EXISTS "dns_names" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "federated_trust_domains" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"bundle_endpoint_url" varchar(255),"bundle_endpoint_profile" varchar(255),"endpoint_spiffe_id" varchar(255),"implicit" bool ); + CREATE TABLE IF NOT EXISTS "ca_journals" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"data" blob,"active_x509_authority_id" varchar(255),"active_jwt_authority_id" varchar(255) ); + INSERT INTO ca_journals VALUES(1,'2026-02-18 10:42:12.139336+00:00','2026-02-18 10:42:12.140316+00:00',X'0a99090a01411084afd6cc061a97043082021330820199a003020102021100edc054dc96e559d730b03d4ed8325d8d300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3236303231383130343230325a170d3236303231393130343231325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273235363631393333353330383839393338353431373438373938393233313537363633333937363059301306072a8648ce3d020106082a8648ce3d03010703420004259745e04850a4c2f6d6f3dd8414ae098d08c523f6de5d04ca2332aa12ae5bcf64c7f16fb4423c923eba1b7e88dfa21dee985e6643a8c45c2284b166919403f4a38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d15f6be56f1b49cdbbeb148b14dd9663caab881d301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023100e72005fbbd726232c3fde94c7da6fe7614377890e87a8391b6e499461be26825c1636d0c1a279be863d68e63b53b8d1f0230209e99243c0f6ed7db2c9e0b190e5abf1a86990761fa8459cee89f1c3ca79ce23c641fe066898b7818fdc1bd8abd027e2297043082021330820199a003020102021100edc054dc96e559d730b03d4ed8325d8d300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3236303231383130343230325a170d3236303231393130343231325a3050310b3009060355040613025553310f300d060355040a13065350494646453130302e060355040513273235363631393333353330383839393338353431373438373938393233313537363633333937363059301306072a8648ce3d020106082a8648ce3d03010703420004259745e04850a4c2f6d6f3dd8414ae098d08c523f6de5d04ca2332aa12ae5bcf64c7f16fb4423c923eba1b7e88dfa21dee985e6643a8c45c2284b166919403f4a38185308182300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e04160414d15f6be56f1b49cdbbeb148b14dd9663caab881d301f0603551d23041830168014928ae360c6aaa7cf6aff8d1716b0046aa61c10ff301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030368003065023100e72005fbbd726232c3fde94c7da6fe7614377890e87a8391b6e499461be26825c1636d0c1a279be863d68e63b53b8d1f0230209e99243c0f6ed7db2c9e0b190e5abf1a86990761fa8459cee89f1c3ca79ce23c641fe066898b7818fdc1bd8abd027e28033228643135663662653536663162343963646262656231343862313464643936363363616162383831643884d2dbcc0642283932386165333630633661616137636636616666386431373136623030343661613631633130666612b2010a01411084afd6cc061884d2dbcc06222069484c38587a6433556936664b42576f686252454e6a626b62715945557665772a5b3059301306072a8648ce3d020106082a8648ce3d03010703420004c5f71bf758cacd8d14b8cf7feac452344ef4e6179e90a7c9827119ec56812d37cf5da87c41c6e2aade917438e6e1e85511d70785d9eacceb5c97d49b9d876a4f30033a2069484c38587a6433556936664b42576f686252454e6a626b6271594555766577','d15f6be56f1b49cdbbeb148b14dd9663caab881d',''); + INSERT INTO sqlite_sequence VALUES('migrations',1); + INSERT INTO sqlite_sequence VALUES('bundles',1); + INSERT INTO sqlite_sequence VALUES('ca_journals',1); + CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ; + CREATE INDEX idx_attested_node_entries_expires_at ON "attested_node_entries"(expires_at) ; + CREATE UNIQUE INDEX uix_attested_node_entries_spiffe_id ON "attested_node_entries"(spiffe_id) ; + CREATE UNIQUE INDEX idx_node_resolver_map ON "node_resolver_map_entries"(spiffe_id, "type", "value") ; + CREATE INDEX idx_registered_entries_hint ON "registered_entries"("hint") ; + CREATE INDEX idx_registered_entries_spiffe_id ON "registered_entries"(spiffe_id) ; + CREATE INDEX idx_registered_entries_parent_id ON "registered_entries"(parent_id) ; + CREATE INDEX idx_registered_entries_expiry ON "registered_entries"("expiry") ; + CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ; + CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ; + CREATE INDEX idx_selectors_type_value ON "selectors"("type", "value") ; + CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ; + CREATE UNIQUE INDEX idx_dns_entry ON "dns_names"(registered_entry_id, "value") ; + CREATE UNIQUE INDEX uix_federated_trust_domains_trust_domain ON "federated_trust_domains"(trust_domain) ; + CREATE INDEX idx_ca_journals_active_x509_authority_id ON "ca_journals"(active_x509_authority_id) ; + CREATE INDEX idx_ca_journals_active_jwt_authority_id ON "ca_journals"(active_jwt_authority_id) ; + CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; + COMMIT; + `, } ) diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index cdbf3022d7..f36c923f96 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5241,6 +5241,9 @@ func (s *PluginSuite) TestMigration() { case 23: // Migration from v23 to v24 adds agent_version column prepareDB(true) + case 24: + // Migration from v24 to v25 adds additional_attributes column + prepareDB(true) default: t.Fatalf("no migration test added for schema version %d", schemaVersion) } From 6bff39a18980fda37ecd5885002370c1d40187cd Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Wed, 18 Feb 2026 15:10:53 +0000 Subject: [PATCH 32/51] fixed quoting in test fixture Signed-off-by: Valentin Fadeev --- test/integration/common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/common b/test/integration/common index 51201635b7..d9b8586168 100644 --- a/test/integration/common +++ b/test/integration/common @@ -175,7 +175,7 @@ check-debug-info() { for ((i=1;i<=MAXCHECKS;i++)); do log-info "check $2 on agent debug endpoint ($((i)) of $MAXCHECKS max)..." - COUNT=$(docker compose exec -T "$1" /opt/spire/conf/agent/debugclient -testCase "printDebugPage" | jq $(printf "'.%s'" "$2")) + COUNT=$(docker compose exec -T "$1" /opt/spire/conf/agent/debugclient -testCase "printDebugPage" | jq $(printf '.%s' "$2")) log-info "$2: ${COUNT}" if [ "$COUNT" -eq "$3" ]; then log-info "SVID count of $COUNT matches the expected count of $3" From 4f690fd6b3a89a20f854194d6e8059f4726a5c4e Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 19 Feb 2026 20:12:30 +0000 Subject: [PATCH 33/51] added new field to client api util Signed-off-by: Valentin Fadeev --- pkg/agent/client/util.go | 30 ++++++---- pkg/agent/manager/manager_test.go | 58 +++++++++++++++++-- .../registration/manager_test_entries.json | 20 ++++++- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/pkg/agent/client/util.go b/pkg/agent/client/util.go index 398a8807ea..ee5ad119cb 100644 --- a/pkg/agent/client/util.go +++ b/pkg/agent/client/util.go @@ -28,6 +28,15 @@ func spiffeIDFromProto(protoID *types.SPIFFEID) (string, error) { return id.String(), nil } +func additionalAttributesFromProto(in *types.Entry_AdditionalAttributes) *common.RegistrationEntry_AdditionalAttributes { + if in != nil { + return &common.RegistrationEntry_AdditionalAttributes{ + DisableX509SvidPrefetch: in.DisableX509SvidPrefetch, + } + } + return nil +} + func slicedEntryFromProto(e *types.Entry) (*common.RegistrationEntry, error) { if e == nil { return nil, errors.New("missing entry") @@ -72,15 +81,16 @@ func slicedEntryFromProto(e *types.Entry) (*common.RegistrationEntry, error) { } return &common.RegistrationEntry{ - EntryId: e.Id, - SpiffeId: spiffeID, - FederatesWith: federatesWith, - RevisionNumber: e.RevisionNumber, - Selectors: selectors, - StoreSvid: e.StoreSvid, - Admin: e.Admin, - Downstream: e.Downstream, - Hint: e.Hint, - CreatedAt: e.CreatedAt, + EntryId: e.Id, + SpiffeId: spiffeID, + FederatesWith: federatesWith, + RevisionNumber: e.RevisionNumber, + Selectors: selectors, + StoreSvid: e.StoreSvid, + Admin: e.Admin, + Downstream: e.Downstream, + Hint: e.Hint, + CreatedAt: e.CreatedAt, + AdditionalAttributes: additionalAttributesFromProto(e.AdditionalAttributes), }, nil } diff --git a/pkg/agent/manager/manager_test.go b/pkg/agent/manager/manager_test.go index 6fb5a5a30a..f22a03c80f 100644 --- a/pkg/agent/manager/manager_test.go +++ b/pkg/agent/manager/manager_test.go @@ -297,6 +297,51 @@ func TestHappyPathWithoutSyncNorRotation(t *testing.T) { }) } +func TestX509PrefetchDisabled(t *testing.T) { + dir := spiretest.TempDir(t) + km := fakeagentkeymanager.New(t, dir) + + clk := clock.NewMock(t) + api := newMockAPI(t, &mockAPIConfig{ + km: km, + getAuthorizedEntries: func(*mockAPI, int32, *entryv1.GetAuthorizedEntriesRequest) (*entryv1.GetAuthorizedEntriesResponse, error) { + return makeGetAuthorizedEntriesResponse(t, "resp6"), nil + }, + batchNewX509SVIDEntries: func(*mockAPI, int32) []*common.RegistrationEntry { + return makeBatchNewX509SVIDEntries("resp6") + }, + svidTTL: 200, + clk: clk, + }) + + baseSVID, baseSVIDKey := api.newSVID(joinTokenID, 1*time.Hour) + + cat := fakeagentcatalog.New() + cat.SetKeyManager(km) + + c := &Config{ + ServerAddr: api.addr, + SVID: baseSVID, + SVIDKey: baseSVIDKey, + Log: testLogger, + TrustDomain: trustDomain, + Storage: openStorage(t, dir), + WorkloadKeyType: workloadkey.ECP256, + Bundle: api.bundle, + Metrics: &telemetry.Blackhole{}, + Clk: clk, + Catalog: cat, + SVIDStoreCache: storecache.New(&storecache.Config{TrustDomain: trustDomain, Log: testLogger}), + RotationStrategy: rotationutil.NewRotationStrategy(0), + } + + m, closer := initializeAndRunNewManager(t, c) + defer closer() + + // Expect SVID not to be cached + require.Equal(t, 0, m.CountX509SVIDs()) +} + func TestRotationWithRSAKey(t *testing.T) { dir := spiretest.TempDir(t) km := fakeagentkeymanager.New(t, dir) @@ -1750,12 +1795,13 @@ func makeGetAuthorizedEntriesResponse(t *testing.T, respKeys ...string) *entryv1 spiffeID, err := spiffeid.FromString(regEntry.SpiffeId) require.NoError(t, err) entries = append(entries, &types.Entry{ - Id: regEntry.EntryId, - SpiffeId: api.ProtoFromID(spiffeID), - FederatesWith: regEntry.FederatesWith, - RevisionNumber: regEntry.RevisionNumber, - Selectors: api.ProtoFromSelectors(regEntry.Selectors), - StoreSvid: regEntry.StoreSvid, + Id: regEntry.EntryId, + SpiffeId: api.ProtoFromID(spiffeID), + FederatesWith: regEntry.FederatesWith, + RevisionNumber: regEntry.RevisionNumber, + Selectors: api.ProtoFromSelectors(regEntry.Selectors), + StoreSvid: regEntry.StoreSvid, + AdditionalAttributes: api.ProtoFromAdditionalAttributes(regEntry.AdditionalAttributes), }) } } diff --git a/test/fixture/registration/manager_test_entries.json b/test/fixture/registration/manager_test_entries.json index cff686b43d..fd3edb415f 100644 --- a/test/fixture/registration/manager_test_entries.json +++ b/test/fixture/registration/manager_test_entries.json @@ -95,7 +95,7 @@ "revision_number": 2 } ] - }, + }, "resp5" : { "entries": [ { @@ -124,5 +124,23 @@ "revision_number": 3 } ] + }, + "resp6" : { + "entries": [ + { + "selectors": [ + { + "type": "unix", + "value": "uid:1111" + } + ], + "entry_id": "0001", + "spiffe_id": "spiffe://example.org/spire/agent-1", + "revision_number": 1, + "additional_attributes": { + "disable_x509_svid_prefetch": true + } + } + ] } } From f7488418fb79a88746894a87e15b5bc30caa00cd Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 19 Feb 2026 20:30:31 +0000 Subject: [PATCH 34/51] validated expectation, accounting for agent entries Signed-off-by: Valentin Fadeev --- test/integration/suites/entries/02-start-agents | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index 905ae51ac2..da208e662b 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -59,8 +59,7 @@ docker compose exec -T spire-server \ -entryID prefetch-disabled \ -parentID "spiffe://domain.test/cluster/test" \ -spiffeID "spiffe://domain.test/workload-with-prefetch-disabled" \ - -selector "unix:uid:1004" \ - -disableX509SVIDPrefetch + -selector "unix:uid:1004" check-synced-entry "spire-agent-1" "spiffe://domain.test/workload-agent-1" check-synced-entry "spire-agent-2" "spiffe://domain.test/workload-agent-2" @@ -72,5 +71,5 @@ for agent in spire-agent-1 spire-agent-2 spire-agent-3; do done for agent in spire-agent-1 spire-agent-2 spire-agent-3; do - check-debug-info ${agent} "cachedX509SvidsCount" 2 + check-debug-info ${agent} "cachedX509SvidsCount" 5 done From 0102b5292b4dea72d5e9a403181818009835cae7 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Thu, 19 Feb 2026 20:58:04 +0000 Subject: [PATCH 35/51] updated actual expectation in the test Signed-off-by: Valentin Fadeev --- test/integration/common | 2 +- test/integration/suites/entries/02-start-agents | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration/common b/test/integration/common index d9b8586168..69efe0a4f6 100644 --- a/test/integration/common +++ b/test/integration/common @@ -174,7 +174,7 @@ check-debug-info() { CHECKINTERVAL=1 for ((i=1;i<=MAXCHECKS;i++)); do - log-info "check $2 on agent debug endpoint ($((i)) of $MAXCHECKS max)..." + log-info "check $2 on $1 debug endpoint ($((i)) of $MAXCHECKS max)..." COUNT=$(docker compose exec -T "$1" /opt/spire/conf/agent/debugclient -testCase "printDebugPage" | jq $(printf '.%s' "$2")) log-info "$2: ${COUNT}" if [ "$COUNT" -eq "$3" ]; then diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index da208e662b..3c64ef6686 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -59,7 +59,8 @@ docker compose exec -T spire-server \ -entryID prefetch-disabled \ -parentID "spiffe://domain.test/cluster/test" \ -spiffeID "spiffe://domain.test/workload-with-prefetch-disabled" \ - -selector "unix:uid:1004" + -selector "unix:uid:1004" \ + -disableX509SVIDPrefetch check-synced-entry "spire-agent-1" "spiffe://domain.test/workload-agent-1" check-synced-entry "spire-agent-2" "spiffe://domain.test/workload-agent-2" @@ -71,5 +72,5 @@ for agent in spire-agent-1 spire-agent-2 spire-agent-3; do done for agent in spire-agent-1 spire-agent-2 spire-agent-3; do - check-debug-info ${agent} "cachedX509SvidsCount" 5 + check-debug-info ${agent} "cachedX509SvidsCount" 4 done From 6f54da08c645f9b3097a76200f4ca81e4a89d5c5 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 20 Feb 2026 14:21:29 +0000 Subject: [PATCH 36/51] updated entry mask and tests Signed-off-by: Valentin Fadeev --- pkg/agent/client/client.go | 19 ++++++++++--------- pkg/agent/client/client_test.go | 19 ++++++++++--------- pkg/server/api/entry/v1/service.go | 4 ++++ pkg/server/api/entry/v1/service_test.go | 19 +++++++++++++++++-- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/pkg/agent/client/client.go b/pkg/agent/client/client.go index 5d3610c1cd..14af4fd7d2 100644 --- a/pkg/agent/client/client.go +++ b/pkg/agent/client/client.go @@ -39,15 +39,16 @@ var ( ErrUnableToGetStream = errors.New("unable to get a stream") entryOutputMask = &types.EntryMask{ - SpiffeId: true, - Selectors: true, - FederatesWith: true, - Admin: true, - Downstream: true, - RevisionNumber: true, - StoreSvid: true, - Hint: true, - CreatedAt: true, + SpiffeId: true, + Selectors: true, + FederatesWith: true, + Admin: true, + Downstream: true, + RevisionNumber: true, + StoreSvid: true, + Hint: true, + AdditionalAttributes: true, + CreatedAt: true, } // RPCTimeoutWithCacheHit can be more aggressive with timeouts in cases where a valid SVID diff --git a/pkg/agent/client/client_test.go b/pkg/agent/client/client_test.go index 958ed337c2..c1170da6ae 100644 --- a/pkg/agent/client/client_test.go +++ b/pkg/agent/client/client_test.go @@ -1199,15 +1199,16 @@ type testServer struct { func checkAuthorizedEntryOutputMask(outputMask *types.EntryMask) error { if diff := cmp.Diff(outputMask, &types.EntryMask{ - SpiffeId: true, - Selectors: true, - FederatesWith: true, - Admin: true, - Downstream: true, - RevisionNumber: true, - StoreSvid: true, - Hint: true, - CreatedAt: true, + SpiffeId: true, + Selectors: true, + FederatesWith: true, + Admin: true, + Downstream: true, + RevisionNumber: true, + StoreSvid: true, + Hint: true, + AdditionalAttributes: true, + CreatedAt: true, }, protocmp.Transform()); diff != "" { return status.Errorf(codes.InvalidArgument, "invalid output mask requested: %s", diff) } diff --git a/pkg/server/api/entry/v1/service.go b/pkg/server/api/entry/v1/service.go index a49966fcb3..bcc8070477 100644 --- a/pkg/server/api/entry/v1/service.go +++ b/pkg/server/api/entry/v1/service.go @@ -628,6 +628,10 @@ func applyMask(e *types.Entry, mask *types.EntryMask) { e.Hint = "" } + if !mask.AdditionalAttributes { + e.AdditionalAttributes = nil + } + if !mask.CreatedAt { e.CreatedAt = 0 } diff --git a/pkg/server/api/entry/v1/service_test.go b/pkg/server/api/entry/v1/service_test.go index a623dcd546..fa2c87c6ea 100644 --- a/pkg/server/api/entry/v1/service_test.go +++ b/pkg/server/api/entry/v1/service_test.go @@ -1244,6 +1244,9 @@ func TestGetEntry(t *testing.T) { DnsNames: []string{"dns1", "dns2"}, Downstream: true, Hint: "internal", + AdditionalAttributes: &common.RegistrationEntry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, }) require.NoError(t, err) @@ -1309,6 +1312,9 @@ func TestGetEntry(t *testing.T) { Downstream: true, ExpiresAt: expiresAt, Hint: "internal", + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, }, expectLogs: []spiretest.LogEntry{ { @@ -1508,6 +1514,9 @@ func TestBatchCreateEntry(t *testing.T) { X509SvidTtl: 45, JwtSvidTtl: 30, Hint: "external", + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, } // Registration entry for test entry testDSEntry := &common.RegistrationEntry{ @@ -1526,7 +1535,10 @@ func TestBatchCreateEntry(t *testing.T) { X509SvidTtl: 45, JwtSvidTtl: 30, Hint: "external", - CreatedAt: 1678731397, + AdditionalAttributes: &common.RegistrationEntry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, + CreatedAt: 1678731397, } for _, tt := range []struct { @@ -1846,7 +1858,10 @@ func TestBatchCreateEntry(t *testing.T) { JwtSvidTtl: 30, StoreSvid: false, Hint: "external", - CreatedAt: 1678731397, + AdditionalAttributes: &types.Entry_AdditionalAttributes{ + DisableX509SvidPrefetch: true, + }, + CreatedAt: 1678731397, }, }, }, From c66af51f80190258ed9f0a3390abe544b94bfb01 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 20 Feb 2026 17:44:59 +0000 Subject: [PATCH 37/51] run check on agents with debug socket Signed-off-by: Valentin Fadeev --- test/integration/suites/entries/02-start-agents | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index 3c64ef6686..d01954ade0 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -71,6 +71,6 @@ for agent in spire-agent-1 spire-agent-2 spire-agent-3; do check-synced-entry ${agent} "spiffe://domain.test/workload-with-prefetch-disabled" done -for agent in spire-agent-1 spire-agent-2 spire-agent-3; do +for agent in spire-agent-1 spire-agent-2; do check-debug-info ${agent} "cachedX509SvidsCount" 4 done From b7fb7602b39ed124165d4ec39c5167a5e949143a Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 20 Feb 2026 18:17:30 +0000 Subject: [PATCH 38/51] temporarily removed flag to validate test Signed-off-by: Valentin Fadeev --- test/integration/suites/entries/02-start-agents | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index d01954ade0..b8e6143138 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -59,8 +59,7 @@ docker compose exec -T spire-server \ -entryID prefetch-disabled \ -parentID "spiffe://domain.test/cluster/test" \ -spiffeID "spiffe://domain.test/workload-with-prefetch-disabled" \ - -selector "unix:uid:1004" \ - -disableX509SVIDPrefetch + -selector "unix:uid:1004" check-synced-entry "spire-agent-1" "spiffe://domain.test/workload-agent-1" check-synced-entry "spire-agent-2" "spiffe://domain.test/workload-agent-2" From 1ea04066c2b6175771f817e5b31ca7458efd3c03 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 20 Feb 2026 18:52:14 +0000 Subject: [PATCH 39/51] reinstated flag, updated expectation Signed-off-by: Valentin Fadeev --- test/integration/suites/entries/02-start-agents | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/suites/entries/02-start-agents b/test/integration/suites/entries/02-start-agents index b8e6143138..567984453a 100755 --- a/test/integration/suites/entries/02-start-agents +++ b/test/integration/suites/entries/02-start-agents @@ -59,7 +59,8 @@ docker compose exec -T spire-server \ -entryID prefetch-disabled \ -parentID "spiffe://domain.test/cluster/test" \ -spiffeID "spiffe://domain.test/workload-with-prefetch-disabled" \ - -selector "unix:uid:1004" + -selector "unix:uid:1004" \ + -disableX509SVIDPrefetch check-synced-entry "spire-agent-1" "spiffe://domain.test/workload-agent-1" check-synced-entry "spire-agent-2" "spiffe://domain.test/workload-agent-2" @@ -71,5 +72,5 @@ for agent in spire-agent-1 spire-agent-2 spire-agent-3; do done for agent in spire-agent-1 spire-agent-2; do - check-debug-info ${agent} "cachedX509SvidsCount" 4 + check-debug-info ${agent} "cachedX509SvidsCount" 3 done From fd54ce29130c8e58c5bbb0a85ebd89bc200e3b38 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 27 Feb 2026 18:21:37 +0000 Subject: [PATCH 40/51] cascade migrations Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index e984c710fe..8ba20058e5 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -506,6 +506,7 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi switch currVersion { case 23: err = migrateToV24(tx) + fallthrough case 24: err = migrateToV25(tx) default: From e9d52a138e9a74a676facecc1491632c2adc9c2f Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 27 Feb 2026 19:27:15 +0000 Subject: [PATCH 41/51] break on error Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 8ba20058e5..f35a8696b8 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -506,6 +506,9 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi switch currVersion { case 23: err = migrateToV24(tx) + if err != nil { + break + } fallthrough case 24: err = migrateToV25(tx) From 80f7b6cdafb50c34dcc89bb72aea8360b57cbc80 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 1 Mar 2026 22:33:17 +0000 Subject: [PATCH 42/51] removed duplicated assignment Signed-off-by: Valentin Fadeev --- pkg/server/api/entry.go | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/pkg/server/api/entry.go b/pkg/server/api/entry.go index b04dd4771b..34467ca39f 100644 --- a/pkg/server/api/entry.go +++ b/pkg/server/api/entry.go @@ -188,22 +188,21 @@ func RegistrationEntryToProto(e *common.RegistrationEntry) (*types.Entry, error) } entry := &types.Entry{ - Id: e.EntryId, - SpiffeId: ProtoFromID(spiffeID), - ParentId: ProtoFromID(parentID), - Selectors: ProtoFromSelectors(e.Selectors), - X509SvidTtl: e.X509SvidTtl, - FederatesWith: federatesWith, - Admin: e.Admin, - Downstream: e.Downstream, - ExpiresAt: e.EntryExpiry, - DnsNames: slices.Clone(e.DnsNames), - RevisionNumber: e.RevisionNumber, - StoreSvid: e.StoreSvid, - JwtSvidTtl: e.JwtSvidTtl, - Hint: e.Hint, - CreatedAt: e.CreatedAt, - AdditionalAttributes: ProtoFromAdditionalAttributes(e.AdditionalAttributes), + Id: e.EntryId, + SpiffeId: ProtoFromID(spiffeID), + ParentId: ProtoFromID(parentID), + Selectors: ProtoFromSelectors(e.Selectors), + X509SvidTtl: e.X509SvidTtl, + FederatesWith: federatesWith, + Admin: e.Admin, + Downstream: e.Downstream, + ExpiresAt: e.EntryExpiry, + DnsNames: slices.Clone(e.DnsNames), + RevisionNumber: e.RevisionNumber, + StoreSvid: e.StoreSvid, + JwtSvidTtl: e.JwtSvidTtl, + Hint: e.Hint, + CreatedAt: e.CreatedAt, } additionalAttributes := ProtoFromAdditionalAttributes(e.AdditionalAttributes) From 18d602ed43c0b62e5f744efb0612e542b8b46f48 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Fri, 20 Mar 2026 12:30:07 +0000 Subject: [PATCH 43/51] picked up changes from spire-api-sdk Signed-off-by: Valentin Fadeev --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index fec128462e..0dc6e65bfd 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/sirupsen/logrus v1.9.4 github.com/smallstep/pkcs7 v0.2.1 github.com/spiffe/go-spiffe/v2 v2.6.0 - github.com/spiffe/spire-api-sdk v1.2.5-0.20260115194754-bcd1999bdd05 + github.com/spiffe/spire-api-sdk v1.2.5-0.20260320104153-99d433165a01 github.com/spiffe/spire-plugin-sdk v1.4.4-0.20251120194148-791bbd274dc7 github.com/stretchr/testify v1.11.1 github.com/uber-go/tally/v4 v4.1.17 @@ -328,5 +328,3 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) - -replace github.com/spiffe/spire-api-sdk v1.2.5-0.20260115194754-bcd1999bdd05 => github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99 diff --git a/go.sum b/go.sum index 0c64e1fff4..bf6b5d203d 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,6 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99 h1:vRWsIC4uMgdmZ+n7uJbxBwYM2g0oWSN7j8DjqAeeY0o= -github.com/ValFadeev/spire-api-sdk v0.0.0-20260125231222-53d09c493d99/go.mod h1:9hXJcMzatM1KwAtBDO3s6HccDCic++/5c2yOc5Iln8Y= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -810,6 +808,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/spiffe/spire-api-sdk v1.2.5-0.20260320104153-99d433165a01 h1:FA0UhZ5fOca/d/ogmVHif1eDARpMo4FLWUnav8JzEts= +github.com/spiffe/spire-api-sdk v1.2.5-0.20260320104153-99d433165a01/go.mod h1:9hXJcMzatM1KwAtBDO3s6HccDCic++/5c2yOc5Iln8Y= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20251120194148-791bbd274dc7 h1:OAvr7TNirmBpXnAp82cTosuB+JAus5cyFCRqXHE0WHs= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20251120194148-791bbd274dc7/go.mod h1:QvrRDiBlXiJ7kNd176ZHsF5eklxxeTRgJSu2CXe0MKw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 097ac031b7961d2d8c4002ad49dee392b99e0b5f Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 22 Mar 2026 11:47:46 +0000 Subject: [PATCH 44/51] added flag to indicate if any of additional attrs were set Signed-off-by: Valentin Fadeev --- cmd/spire-server/cli/entry/update.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cmd/spire-server/cli/entry/update.go b/cmd/spire-server/cli/entry/update.go index 2e228a54a4..3aed41d4d9 100644 --- a/cmd/spire-server/cli/entry/update.go +++ b/cmd/spire-server/cli/entry/update.go @@ -67,6 +67,9 @@ type updateCommand struct { // storeSVID determines if the issued SVID must be stored through an SVIDStore plugin storeSVID bool + // additionalAttributesSet indicates whether any of the additional attributes if the requestration entry were set + additionalAttributesSet bool + // disableX509SVIDPrefetch tells the agent not to prefetch and cache X509 SVID for // the given entry disableX509SVIDPrefetch bool @@ -102,7 +105,12 @@ func (c *updateCommand) AppendFlags(f *flag.FlagSet) { f.Int64Var(&c.entryExpiry, "entryExpiry", 0, "An expiry, from epoch in seconds, for the resulting registration entry to be pruned") f.Var(&c.dnsNames, "dns", "A DNS name that will be included in SVIDs issued based on this entry, where appropriate. Can be used more than once") f.StringVar(&c.hint, "hint", "", "The entry hint, used to disambiguate entries with the same SPIFFE ID") - f.BoolVar(&c.disableX509SVIDPrefetch, "disableX509SVIDPrefetch", false, "A boolean value that, when set, disables prefetching X509 SVID for this entry") + f.BoolFunc("disableX509SVIDPrefetch", "A boolean value that, when set, disables prefetching X509 SVID for this entry", + func(_ string) error { + c.additionalAttributesSet = true + c.disableX509SVIDPrefetch = true + return nil + }) cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintUpdate) } @@ -210,10 +218,12 @@ func (c *updateCommand) parseConfig() ([]*types.Entry, error) { e.Selectors = selectors + if c.additionalAttributesSet { + e.AdditionalAttributes = &types.Entry_AdditionalAttributes{} + } + if c.disableX509SVIDPrefetch { - e.AdditionalAttributes = &types.Entry_AdditionalAttributes{ - DisableX509SvidPrefetch: c.disableX509SVIDPrefetch, - } + e.AdditionalAttributes.DisableX509SvidPrefetch = true } e.FederatesWith = c.federatesWith From be1f2e7117dbc8ad47b9d15ed657f2c48fb80616 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Sun, 22 Mar 2026 11:48:13 +0000 Subject: [PATCH 45/51] enforced additional attributes message size Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore.go | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index a45bd9c1c5..71ee3ff872 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -67,6 +67,9 @@ const ( // Maximum size for preallocation in a paginated request maxResultPreallocation = 1000 + + // Maximum size for additional attributes message in a registration entry + maxAdditionalAttributesSize = 65535 ) // Configuration for the sql datastore implementation. @@ -4529,6 +4532,22 @@ func modelToBundle(model *Bundle) (*common.Bundle, error) { return bundle, nil } +func validateAditionalAttributes(additionalAttributes *common.RegistrationEntry_AdditionalAttributes) error { + if additionalAttributes == nil { + return nil + } + + marshaledAdditionalAttributes, err := proto.Marshal(additionalAttributes) + if err != nil { + return newValidationError("invalid additional attributes: %s", err) + } + if len(marshaledAdditionalAttributes) > maxAdditionalAttributesSize { + return newValidationError("invalid registration entry: additional attributes size exceeds the maximum allowed size of %d bytes", maxAdditionalAttributesSize) + } + + return nil +} + func validateRegistrationEntry(entry *common.RegistrationEntry) error { if entry == nil { return newValidationError("invalid request: missing registered entry") @@ -4538,6 +4557,11 @@ func validateRegistrationEntry(entry *common.RegistrationEntry) error { return newValidationError("invalid registration entry: missing selector list") } + err := validateAditionalAttributes(entry.AdditionalAttributes) + if err != nil { + return err + } + // In case of StoreSvid is set, all entries 'must' be the same type, // it is done to avoid users to mix selectors from different platforms in // entries with storable SVIDs @@ -4617,6 +4641,12 @@ func validateRegistrationEntryForUpdate(entry *common.RegistrationEntry, mask *c return newValidationError("invalid registration entry: JwtSvidTtl is not set") } + if mask != nil && mask.AdditionalAttributes { + if err := validateAditionalAttributes(entry.AdditionalAttributes); err != nil { + return err + } + } + return nil } From 9fd6df2f783360fb3a91952e9f77b62d9155af5f Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 13:54:46 +0100 Subject: [PATCH 46/51] validate attributes when creating/updating Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/sqlstore.go | 25 +++++++---------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/pkg/server/datastore/sqlstore/sqlstore.go b/pkg/server/datastore/sqlstore/sqlstore.go index 71ee3ff872..f3c9d8abb7 100644 --- a/pkg/server/datastore/sqlstore/sqlstore.go +++ b/pkg/server/datastore/sqlstore/sqlstore.go @@ -2553,7 +2553,7 @@ func createRegistrationEntry(tx *gorm.DB, entry *common.RegistrationEntry) (*com return nil, err } - AdditionalAttributes, err := proto.Marshal(entry.AdditionalAttributes) + AdditionalAttributes, err := marshalAndValidateAdditionalAttributes(entry.AdditionalAttributes) if err != nil { return nil, err } @@ -4086,7 +4086,7 @@ func updateRegistrationEntry(tx *gorm.DB, e *common.RegistrationEntry, mask *com entry.Hint = e.Hint } if mask == nil || mask.AdditionalAttributes { - AdditionalAttributes, err := proto.Marshal(e.AdditionalAttributes) + AdditionalAttributes, err := marshalAndValidateAdditionalAttributes(e.AdditionalAttributes) if err != nil { return nil, err } @@ -4532,20 +4532,20 @@ func modelToBundle(model *Bundle) (*common.Bundle, error) { return bundle, nil } -func validateAditionalAttributes(additionalAttributes *common.RegistrationEntry_AdditionalAttributes) error { +func marshalAndValidateAdditionalAttributes(additionalAttributes *common.RegistrationEntry_AdditionalAttributes) ([]byte, error) { if additionalAttributes == nil { - return nil + return nil, nil } marshaledAdditionalAttributes, err := proto.Marshal(additionalAttributes) if err != nil { - return newValidationError("invalid additional attributes: %s", err) + return nil, newValidationError("invalid additional attributes: %s", err) } if len(marshaledAdditionalAttributes) > maxAdditionalAttributesSize { - return newValidationError("invalid registration entry: additional attributes size exceeds the maximum allowed size of %d bytes", maxAdditionalAttributesSize) + return nil, newValidationError("invalid registration entry: additional attributes size exceeds the maximum allowed size of %d bytes", maxAdditionalAttributesSize) } - return nil + return marshaledAdditionalAttributes, nil } func validateRegistrationEntry(entry *common.RegistrationEntry) error { @@ -4557,11 +4557,6 @@ func validateRegistrationEntry(entry *common.RegistrationEntry) error { return newValidationError("invalid registration entry: missing selector list") } - err := validateAditionalAttributes(entry.AdditionalAttributes) - if err != nil { - return err - } - // In case of StoreSvid is set, all entries 'must' be the same type, // it is done to avoid users to mix selectors from different platforms in // entries with storable SVIDs @@ -4641,12 +4636,6 @@ func validateRegistrationEntryForUpdate(entry *common.RegistrationEntry, mask *c return newValidationError("invalid registration entry: JwtSvidTtl is not set") } - if mask != nil && mask.AdditionalAttributes { - if err := validateAditionalAttributes(entry.AdditionalAttributes); err != nil { - return err - } - } - return nil } From dc387242ed6d3fe7d7c7160a2d6723d8d18adb61 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 13:55:32 +0100 Subject: [PATCH 47/51] updated base schema version for migration Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 8 +--- .../datastore/sqlstore/migration_test.go | 40 ------------------- .../datastore/sqlstore/sqlstore_test.go | 5 +-- 3 files changed, 2 insertions(+), 51 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 64d5c099f0..fdee696ce7 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -281,7 +281,7 @@ const ( // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 23 + lastMinorReleaseSchemaVersion = 24 ) // the current code version @@ -507,12 +507,6 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { - case 23: - err = migrateToV24(tx) - if err != nil { - break - } - fallthrough case 24: err = migrateToV25(tx) default: diff --git a/pkg/server/datastore/sqlstore/migration_test.go b/pkg/server/datastore/sqlstore/migration_test.go index 0b3d2a7138..fe2e548165 100644 --- a/pkg/server/datastore/sqlstore/migration_test.go +++ b/pkg/server/datastore/sqlstore/migration_test.go @@ -17,46 +17,6 @@ var ( // pristine database created by a SPIRE release that runs that schema // version. migrationDumps = map[int]string{ - 23: ` - PRAGMA foreign_keys=OFF; - BEGIN TRANSACTION; - CREATE TABLE IF NOT EXISTS "federated_registration_entries" ("bundle_id" integer,"registered_entry_id" integer, PRIMARY KEY ("bundle_id","registered_entry_id")); - CREATE TABLE IF NOT EXISTS "bundles" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"data" blob ); - INSERT INTO bundles VALUES(1,'2023-08-29 13:15:25.103258-03:00','2023-08-29 13:15:25.201436-03:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712df030adc03308201d83082015ea0030201020214449db4c88cda977653f4d5e4770aec9b4b1e970c300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3233303531353032303530365a170d3238303531333032303530365a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b8104002203620004f57073b72f16fdec785ebd117735018227bfa2475a51385e485d0f42f540693b1768fd49ef2bf40e195ac38e48ec2bfd1cfdb51ce98cc48959d177aab0e97db0ce47e7b1c1416bb46c83577f0e2375e1dd079be4d57c8dc81410c5e5294b1867a35d305b301d0603551d0e04160414928ae360c6aaa7cf6aff8d1716b0046aa61c10ff300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040368003065023100e7843c85f844778a95c9cc1b2cdcce9bf1d0ae9d67d7e6b6c5cf3c894d37e8530f6a7711d4f2ea82c3833df5b2b6d75102300a2287548b879888c6bdf88dab55b8fc80ec490059f484b2c4177403997b463e9011b3da82f8a6e29254eee45a6293641a85010a5b3059301306072a8648ce3d020106082a8648ce3d030107034200045cdd2166a5ae9e1c95695558c35dabc43c44c196abbd364aff4ffaac924811d7ab4601485f61efd5422ffe67b46f9d7c0b3963f90a41183d410bd3520c7434e5122054314a6772794c4746774f516c354e6b44386e4f7051695a43436430626b7a49189dd6bda7062801'); - CREATE TABLE IF NOT EXISTS "attested_node_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"data_type" varchar(255),"serial_number" varchar(255),"expires_at" datetime,"new_serial_number" varchar(255),"new_expires_at" datetime,"can_reattest" bool ); - CREATE TABLE IF NOT EXISTS "attested_node_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255) ); - CREATE TABLE IF NOT EXISTS "node_resolver_map_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"type" varchar(255),"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "registered_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255),"spiffe_id" varchar(255),"parent_id" varchar(255),"ttl" integer,"admin" bool,"downstream" bool,"expiry" bigint,"revision_number" bigint,"store_svid" bool,"hint" varchar(255),"jwt_svid_ttl" integer ); - CREATE TABLE IF NOT EXISTS "registered_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255) ); - CREATE TABLE IF NOT EXISTS "join_tokens" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"token" varchar(255),"expiry" bigint ); - CREATE TABLE IF NOT EXISTS "selectors" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"type" varchar(255),"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer,"code_version" varchar(255) ); - INSERT INTO migrations VALUES(1,'2023-08-29 13:15:25.080937-03:00','2023-08-29 13:15:25.080937-03:00',23,'1.8.0-dev-unk'); - CREATE TABLE IF NOT EXISTS "dns_names" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"value" varchar(255) ); - CREATE TABLE IF NOT EXISTS "federated_trust_domains" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"bundle_endpoint_url" varchar(255),"bundle_endpoint_profile" varchar(255),"endpoint_spiffe_id" varchar(255),"implicit" bool ); - CREATE TABLE IF NOT EXISTS "ca_journals" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"data" blob,"active_x509_authority_id" varchar(255),"active_jwt_authority_id" varchar(255) ); - DELETE FROM sqlite_sequence; - INSERT INTO sqlite_sequence VALUES('migrations',1); - INSERT INTO sqlite_sequence VALUES('bundles',1); - CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ; - CREATE INDEX idx_attested_node_entries_expires_at ON "attested_node_entries"(expires_at) ; - CREATE UNIQUE INDEX uix_attested_node_entries_spiffe_id ON "attested_node_entries"(spiffe_id) ; - CREATE UNIQUE INDEX idx_node_resolver_map ON "node_resolver_map_entries"(spiffe_id, "type", "value") ; - CREATE INDEX idx_registered_entries_spiffe_id ON "registered_entries"(spiffe_id) ; - CREATE INDEX idx_registered_entries_parent_id ON "registered_entries"(parent_id) ; - CREATE INDEX idx_registered_entries_expiry ON "registered_entries"("expiry") ; - CREATE INDEX idx_registered_entries_hint ON "registered_entries"("hint") ; - CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ; - CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ; - CREATE INDEX idx_selectors_type_value ON "selectors"("type", "value") ; - CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ; - CREATE UNIQUE INDEX idx_dns_entry ON "dns_names"(registered_entry_id, "value") ; - CREATE UNIQUE INDEX uix_federated_trust_domains_trust_domain ON "federated_trust_domains"(trust_domain) ; - CREATE INDEX idx_ca_journals_active_x509_authority_id ON "ca_journals"(active_x509_authority_id) ; - CREATE INDEX idx_ca_journals_active_jwt_authority_id ON "ca_journals"(active_jwt_authority_id) ; - CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; - COMMIT; - `, 24: ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index f36c923f96..61f51de5b8 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5236,11 +5236,8 @@ func (s *PluginSuite) TestMigration() { switch schemaVersion { // All of these schema versions were migrated by previous versions // of SPIRE server and no longer have migration code. - case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22: + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23: prepareDB(false) - case 23: - // Migration from v23 to v24 adds agent_version column - prepareDB(true) case 24: // Migration from v24 to v25 adds additional_attributes column prepareDB(true) From c16d67b8b4911cb91c9f5e94032842819f587af9 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 15:18:46 +0100 Subject: [PATCH 48/51] removed unused helper Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index fdee696ce7..a1620f3b1f 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -519,14 +519,6 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi return nextVersion, nil } -func migrateToV24(tx *gorm.DB) error { - // Add agent_version column to attested_node_entries table - if err := tx.AutoMigrate(&AttestedNode{}).Error; err != nil { - return newWrappedSQLError(err) - } - return nil -} - func migrateToV25(tx *gorm.DB) error { // Add additional_attributes column to registered_entries table if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { From cadd6b7c5184f2edd2c3a6adcddd12dd70ba8464 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 15:58:14 +0100 Subject: [PATCH 49/51] Revert "removed unused helper" This reverts commit c16d67b8b4911cb91c9f5e94032842819f587af9. Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index a1620f3b1f..fdee696ce7 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -519,6 +519,14 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi return nextVersion, nil } +func migrateToV24(tx *gorm.DB) error { + // Add agent_version column to attested_node_entries table + if err := tx.AutoMigrate(&AttestedNode{}).Error; err != nil { + return newWrappedSQLError(err) + } + return nil +} + func migrateToV25(tx *gorm.DB) error { // Add additional_attributes column to registered_entries table if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil { From c4b63eae2d65590b9074d393bb81f44d2209bef6 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 15:59:02 +0100 Subject: [PATCH 50/51] Revert "updated base schema version for migration" This reverts commit dc387242ed6d3fe7d7c7160a2d6723d8d18adb61. Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 8 +++- .../datastore/sqlstore/migration_test.go | 40 +++++++++++++++++++ .../datastore/sqlstore/sqlstore_test.go | 5 ++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index fdee696ce7..64d5c099f0 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -281,7 +281,7 @@ const ( // lastMinorReleaseSchemaVersion is the schema version supported by the // last minor release. When the migrations are opportunistically pruned // from the code after a minor release, this number should be updated. - lastMinorReleaseSchemaVersion = 24 + lastMinorReleaseSchemaVersion = 23 ) // the current code version @@ -507,6 +507,12 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi // } // switch currVersion { + case 23: + err = migrateToV24(tx) + if err != nil { + break + } + fallthrough case 24: err = migrateToV25(tx) default: diff --git a/pkg/server/datastore/sqlstore/migration_test.go b/pkg/server/datastore/sqlstore/migration_test.go index fe2e548165..0b3d2a7138 100644 --- a/pkg/server/datastore/sqlstore/migration_test.go +++ b/pkg/server/datastore/sqlstore/migration_test.go @@ -17,6 +17,46 @@ var ( // pristine database created by a SPIRE release that runs that schema // version. migrationDumps = map[int]string{ + 23: ` + PRAGMA foreign_keys=OFF; + BEGIN TRANSACTION; + CREATE TABLE IF NOT EXISTS "federated_registration_entries" ("bundle_id" integer,"registered_entry_id" integer, PRIMARY KEY ("bundle_id","registered_entry_id")); + CREATE TABLE IF NOT EXISTS "bundles" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"data" blob ); + INSERT INTO bundles VALUES(1,'2023-08-29 13:15:25.103258-03:00','2023-08-29 13:15:25.201436-03:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712df030adc03308201d83082015ea0030201020214449db4c88cda977653f4d5e4770aec9b4b1e970c300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3233303531353032303530365a170d3238303531333032303530365a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b8104002203620004f57073b72f16fdec785ebd117735018227bfa2475a51385e485d0f42f540693b1768fd49ef2bf40e195ac38e48ec2bfd1cfdb51ce98cc48959d177aab0e97db0ce47e7b1c1416bb46c83577f0e2375e1dd079be4d57c8dc81410c5e5294b1867a35d305b301d0603551d0e04160414928ae360c6aaa7cf6aff8d1716b0046aa61c10ff300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040368003065023100e7843c85f844778a95c9cc1b2cdcce9bf1d0ae9d67d7e6b6c5cf3c894d37e8530f6a7711d4f2ea82c3833df5b2b6d75102300a2287548b879888c6bdf88dab55b8fc80ec490059f484b2c4177403997b463e9011b3da82f8a6e29254eee45a6293641a85010a5b3059301306072a8648ce3d020106082a8648ce3d030107034200045cdd2166a5ae9e1c95695558c35dabc43c44c196abbd364aff4ffaac924811d7ab4601485f61efd5422ffe67b46f9d7c0b3963f90a41183d410bd3520c7434e5122054314a6772794c4746774f516c354e6b44386e4f7051695a43436430626b7a49189dd6bda7062801'); + CREATE TABLE IF NOT EXISTS "attested_node_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"data_type" varchar(255),"serial_number" varchar(255),"expires_at" datetime,"new_serial_number" varchar(255),"new_expires_at" datetime,"can_reattest" bool ); + CREATE TABLE IF NOT EXISTS "attested_node_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "node_resolver_map_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"spiffe_id" varchar(255),"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "registered_entries" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255),"spiffe_id" varchar(255),"parent_id" varchar(255),"ttl" integer,"admin" bool,"downstream" bool,"expiry" bigint,"revision_number" bigint,"store_svid" bool,"hint" varchar(255),"jwt_svid_ttl" integer ); + CREATE TABLE IF NOT EXISTS "registered_entries_events" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"entry_id" varchar(255) ); + CREATE TABLE IF NOT EXISTS "join_tokens" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"token" varchar(255),"expiry" bigint ); + CREATE TABLE IF NOT EXISTS "selectors" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"type" varchar(255),"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer,"code_version" varchar(255) ); + INSERT INTO migrations VALUES(1,'2023-08-29 13:15:25.080937-03:00','2023-08-29 13:15:25.080937-03:00',23,'1.8.0-dev-unk'); + CREATE TABLE IF NOT EXISTS "dns_names" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"registered_entry_id" integer,"value" varchar(255) ); + CREATE TABLE IF NOT EXISTS "federated_trust_domains" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"trust_domain" varchar(255) NOT NULL,"bundle_endpoint_url" varchar(255),"bundle_endpoint_profile" varchar(255),"endpoint_spiffe_id" varchar(255),"implicit" bool ); + CREATE TABLE IF NOT EXISTS "ca_journals" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"data" blob,"active_x509_authority_id" varchar(255),"active_jwt_authority_id" varchar(255) ); + DELETE FROM sqlite_sequence; + INSERT INTO sqlite_sequence VALUES('migrations',1); + INSERT INTO sqlite_sequence VALUES('bundles',1); + CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ; + CREATE INDEX idx_attested_node_entries_expires_at ON "attested_node_entries"(expires_at) ; + CREATE UNIQUE INDEX uix_attested_node_entries_spiffe_id ON "attested_node_entries"(spiffe_id) ; + CREATE UNIQUE INDEX idx_node_resolver_map ON "node_resolver_map_entries"(spiffe_id, "type", "value") ; + CREATE INDEX idx_registered_entries_spiffe_id ON "registered_entries"(spiffe_id) ; + CREATE INDEX idx_registered_entries_parent_id ON "registered_entries"(parent_id) ; + CREATE INDEX idx_registered_entries_expiry ON "registered_entries"("expiry") ; + CREATE INDEX idx_registered_entries_hint ON "registered_entries"("hint") ; + CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ; + CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ; + CREATE INDEX idx_selectors_type_value ON "selectors"("type", "value") ; + CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ; + CREATE UNIQUE INDEX idx_dns_entry ON "dns_names"(registered_entry_id, "value") ; + CREATE UNIQUE INDEX uix_federated_trust_domains_trust_domain ON "federated_trust_domains"(trust_domain) ; + CREATE INDEX idx_ca_journals_active_x509_authority_id ON "ca_journals"(active_x509_authority_id) ; + CREATE INDEX idx_ca_journals_active_jwt_authority_id ON "ca_journals"(active_jwt_authority_id) ; + CREATE INDEX idx_federated_registration_entries_registered_entry_id ON "federated_registration_entries"(registered_entry_id) ; + COMMIT; + `, 24: ` PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; diff --git a/pkg/server/datastore/sqlstore/sqlstore_test.go b/pkg/server/datastore/sqlstore/sqlstore_test.go index 61f51de5b8..f36c923f96 100644 --- a/pkg/server/datastore/sqlstore/sqlstore_test.go +++ b/pkg/server/datastore/sqlstore/sqlstore_test.go @@ -5236,8 +5236,11 @@ func (s *PluginSuite) TestMigration() { switch schemaVersion { // All of these schema versions were migrated by previous versions // of SPIRE server and no longer have migration code. - case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23: + case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22: prepareDB(false) + case 23: + // Migration from v23 to v24 adds agent_version column + prepareDB(true) case 24: // Migration from v24 to v25 adds additional_attributes column prepareDB(true) From 951f8fc96b7c3007121412d54d647d4658a31cd6 Mon Sep 17 00:00:00 2001 From: Valentin Fadeev Date: Mon, 30 Mar 2026 16:00:05 +0100 Subject: [PATCH 51/51] removed fallthrough Signed-off-by: Valentin Fadeev --- pkg/server/datastore/sqlstore/migration.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/server/datastore/sqlstore/migration.go b/pkg/server/datastore/sqlstore/migration.go index 64d5c099f0..635b9caf01 100644 --- a/pkg/server/datastore/sqlstore/migration.go +++ b/pkg/server/datastore/sqlstore/migration.go @@ -509,10 +509,6 @@ func migrateVersion(tx *gorm.DB, currVersion int, log logrus.FieldLogger) (versi switch currVersion { case 23: err = migrateToV24(tx) - if err != nil { - break - } - fallthrough case 24: err = migrateToV25(tx) default: