Skip to content

Commit

Permalink
Merge pull request #776 from amoore877/T2582353_registration_entry_ex…
Browse files Browse the repository at this point in the history
…piry

add expiry time to registration entry and pruning API
  • Loading branch information
azdagron committed Mar 7, 2019
2 parents b0b3244 + c3ba63d commit 8eb77cb
Show file tree
Hide file tree
Showing 21 changed files with 611 additions and 216 deletions.
30 changes: 29 additions & 1 deletion pkg/server/plugin/datastore/sql/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

const (
// version of the database in the code
codeVersion = 6
codeVersion = 7
)

func migrateDB(db *gorm.DB) (err error) {
Expand Down Expand Up @@ -112,6 +112,8 @@ func migrateVersion(tx *gorm.DB, version int) (versionOut int, err error) {
err = migrateToV5(tx)
case 5:
err = migrateToV6(tx)
case 6:
err = migrateToV7(tx)
default:
err = sqlError.New("no migration support for version %d", version)
}
Expand Down Expand Up @@ -280,6 +282,13 @@ func migrateToV5(tx *gorm.DB) error {
}

func migrateToV6(tx *gorm.DB) error {
if err := tx.AutoMigrate(&V6RegisteredEntry{}).Error; err != nil {
return sqlError.Wrap(err)
}
return nil
}

func migrateToV7(tx *gorm.DB) error {
if err := tx.AutoMigrate(&RegisteredEntry{}).Error; err != nil {
return sqlError.Wrap(err)
}
Expand Down Expand Up @@ -350,3 +359,22 @@ type V5RegisteredEntry struct {
func (V5RegisteredEntry) TableName() string {
return "registered_entries"
}

// V6RegisteredEntry holds a version 6 registered entry
type V6RegisteredEntry struct {
Model

EntryID string `gorm:"unique_index"`
SpiffeID string
ParentID string
TTL int32
Selectors []Selector
FederatesWith []Bundle `gorm:"many2many:federated_registration_entries;"`
Admin bool
Downstream bool
}

// TableName gets table name for v6 registered entry
func (V6RegisteredEntry) TableName() string {
return "registered_entries"
}
29 changes: 29 additions & 0 deletions pkg/server/plugin/datastore/sql/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,35 @@ CREATE UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entr
CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ;
CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ;
COMMIT;
`,
// v6 database
`
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,'2018-12-19 14:26:32.340488-07:00','2018-12-19 14:26:32.340488-07:00','spiffe://example.org',X'0a147370696666653a2f2f6578616d706c652e6f726712f6030af303308201ef30820174a003020102020101300a06082a8648ce3d040303301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3138313231393231323632325a170d3138313231393232323633325a301e310b3009060355040613025553310f300d060355040a13065350494646453076301006072a8648ce3d020106052b8104002203620004c941f4fdc386a57aa74807d64a05fdedac4d3c9cd0841beac744db4163ae6ba46e883551c683cf11781c8958ebb11ae9a4bbeb3bbf751aaa9e645e65ab6ee3c5b681621d538929956f37e182c8f955614bef67e7921b3371571b87a0065e0f8da38185308182300e0603551d0f0101ff040403020186300f0603551d130101ff040530030101ff301d0603551d0e04160414bb9e6ee33abb3b2d2587b5c67f66f74851487739301f0603551d2304183016801487a5f357a2f035acc0f864c454e76ed3ba39c8e8301f0603551d110418301686147370696666653a2f2f6578616d706c652e6f7267300a06082a8648ce3d0403030369003066023100813cc8650728e10cdfd5230d484dd4353ec7513dc2543cb51c1115dfb62d5d1ca92dd586137d273b4ad6a78a53dedc6c023100d16f9478064213f3e6fbe9cd3a96dd730caa413464fadaf634337e810d5e6be7da15d7c142d309cb76fd0f6f5cf111e112d3030ad003308201cc30820153a00302010202090093380e1447d2f9ae300a06082a8648ce3d040304301e310b3009060355040613025553310f300d060355040a0c06535049464645301e170d3138303531333139333334375a170d3233303531323139333334375a301e310b3009060355040613025553310f300d060355040a0c065350494646453076301006072a8648ce3d020106052b81040022036200045a307e9d2192c48622ce76fce31bb95860d98fcd272fb5b5737cdfe3c5a1cb499aed8ee60812b37d092b80382e2388f467ed3fb431ffafc82d3ad2cbac8a6e330587a1ee2f6d5045b5ed6f8fa5ede96784f255f0702bcbb3f99c9af3ea54af63a35d305b301d0603551d0e0416041487a5f357a2f035acc0f864c454e76ed3ba39c8e8300f0603551d130101ff040530030101ff300e0603551d0f0101ff04040302010630190603551d1104123010860e7370696666653a2f2f6c6f63616c300a06082a8648ce3d0403040367003064023013831ed77a8c0bd8ba164c74876eb2d3d41921bb91a80f69b8b83d01e780032a39b41cd197560bd0a344a74d9529260902305d789bea8c9f705b9e4e1a3d494300c50fb91678407aa0c9703db23fe61118ddacc98b5e88d2e375252613496192a9671a85010a5b3059301306072a8648ce3d020106082a8648ce3d030107034200041db49815c4dc0a343e25ba73a2f6add69a034f968f9319c34eb6ef89c2674c92a310ebcef9d393fb478c7f00ce4a1dd0926b54cf6bbae5544968cd933b1372f61220486558424e674565324b6d744b563143384738674b5450766c59536c4156675318988bebe005');
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 );
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);
INSERT INTO registered_entries VALUES(1,'2018-12-19 14:26:58.227869-07:00','2018-12-19 14:26:58.227869-07:00','f0373f87-a0f3-4c94-aa6a-a2f948bfc15a','spiffe://example.org/admin','spiffe://example.org/spire/agent/x509pop/e81aef2e9178db3db836a1a85d362ca5b2241631',3600, 0, 0);
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) );
INSERT INTO selectors VALUES(1,'2018-12-19 14:26:58.228067-07:00','2018-12-19 14:26:58.228067-07:00',1,'unix','uid:501');
CREATE TABLE IF NOT EXISTS "migrations" ("id" integer primary key autoincrement,"created_at" datetime,"updated_at" datetime,"version" integer );
INSERT INTO migrations VALUES(1,'2018-12-19 14:26:32.297244-07:00','2018-12-19 14:26:32.297244-07:00',4);
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('migrations',1);
INSERT INTO sqlite_sequence VALUES('bundles',1);
INSERT INTO sqlite_sequence VALUES('registered_entries',1);
INSERT INTO sqlite_sequence VALUES('selectors',1);
CREATE UNIQUE INDEX uix_bundles_trust_domain ON "bundles"(trust_domain) ;
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 UNIQUE INDEX uix_registered_entries_entry_id ON "registered_entries"(entry_id) ;
CREATE UNIQUE INDEX uix_join_tokens_token ON "join_tokens"("token") ;
CREATE UNIQUE INDEX idx_selector_entry ON "selectors"(registered_entry_id, "type", "value") ;
COMMIT;
`,
}
)
Expand Down
9 changes: 6 additions & 3 deletions pkg/server/plugin/datastore/sql/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,17 @@ func (NodeSelector) TableName() string {
type RegisteredEntry struct {
Model

EntryID string `gorm:"unique_index"`
SpiffeID string
ParentID string
EntryID string `gorm:"unique_index"`
SpiffeID string
ParentID string
// TTL of identities derived from this entry
TTL int32
Selectors []Selector
FederatesWith []Bundle `gorm:"many2many:federated_registration_entries;"`
Admin bool
Downstream bool
// (optional) expiry of this entry
Expiry int64
}

// JoinToken holds a join token
Expand Down
51 changes: 45 additions & 6 deletions pkg/server/plugin/datastore/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,18 @@ func (ds *sqlPlugin) DeleteRegistrationEntry(ctx context.Context,
return resp, nil
}

// PruneRegistrationEntries takes a registration entry message, and deletes all entries which have expired
// before the date in the message
func (ds *sqlPlugin) PruneRegistrationEntries(ctx context.Context, req *datastore.PruneRegistrationEntriesRequest) (resp *datastore.PruneRegistrationEntriesResponse, err error) {
if err := ds.withWriteTx(ctx, func(tx *gorm.DB) (err error) {
resp, err = pruneRegistrationEntries(tx, req)
return err
}); err != nil {
return nil, err
}
return resp, nil
}

// CreateJoinToken takes a Token message and stores it
func (ds *sqlPlugin) CreateJoinToken(ctx context.Context, req *datastore.CreateJoinTokenRequest) (resp *datastore.CreateJoinTokenResponse, err error) {
if req.JoinToken == nil || req.JoinToken.Token == "" || req.JoinToken.Expiry == 0 {
Expand Down Expand Up @@ -802,6 +814,7 @@ func createRegistrationEntry(tx *gorm.DB,
TTL: req.Entry.Ttl,
Admin: req.Entry.Admin,
Downstream: req.Entry.Downstream,
Expiry: req.Entry.Expiry,
}

if err := tx.Create(&newRegisteredEntry).Error; err != nil {
Expand Down Expand Up @@ -1047,6 +1060,7 @@ func updateRegistrationEntry(tx *gorm.DB,
entry.Selectors = selectors
entry.Admin = req.Entry.Admin
entry.Downstream = req.Entry.Downstream
entry.Expiry = req.Entry.Expiry
if err := tx.Save(&entry).Error; err != nil {
return nil, sqlError.Wrap(err)
}
Expand Down Expand Up @@ -1079,19 +1093,43 @@ func deleteRegistrationEntry(tx *gorm.DB,
return nil, err
}

if err := tx.Model(&entry).Association("FederatesWith").Clear().Error; err != nil {
err = deleteRegistrationEntrySupport(tx, entry)
if err != nil {
return nil, err
}

if err := tx.Delete(&entry).Error; err != nil {
return nil, sqlError.Wrap(err)
}

return &datastore.DeleteRegistrationEntryResponse{
Entry: respEntry,
}, nil
}

func deleteRegistrationEntrySupport(tx *gorm.DB, entry RegisteredEntry) error {
if err := tx.Model(&entry).Association("FederatesWith").Clear().Error; err != nil {
return err
}

if err := tx.Delete(&entry).Error; err != nil {
return sqlError.Wrap(err)
}

return nil
}

func pruneRegistrationEntries(tx *gorm.DB, req *datastore.PruneRegistrationEntriesRequest) (*datastore.PruneRegistrationEntriesResponse, error) {
var registrationEntries []RegisteredEntry
if err := tx.Where("expiry != 0").Where("expiry < ?", req.ExpiresBefore).Find(&registrationEntries).Error; err != nil {
return nil, err
}

for _, entry := range registrationEntries {
if err := deleteRegistrationEntrySupport(tx, entry); err != nil {
return nil, err
}
}

return &datastore.PruneRegistrationEntriesResponse{}, nil
}

func createJoinToken(tx *gorm.DB, req *datastore.CreateJoinTokenRequest) (*datastore.CreateJoinTokenResponse, error) {
t := JoinToken{
Token: req.JoinToken.Token,
Expand Down Expand Up @@ -1137,7 +1175,7 @@ func deleteJoinToken(tx *gorm.DB, req *datastore.DeleteJoinTokenRequest) (*datas
}

func pruneJoinTokens(tx *gorm.DB, req *datastore.PruneJoinTokensRequest) (*datastore.PruneJoinTokensResponse, error) {
if err := tx.Where("expiry <= ?", req.ExpiresBefore).Delete(&JoinToken{}).Error; err != nil {
if err := tx.Where("expiry < ?", req.ExpiresBefore).Delete(&JoinToken{}).Error; err != nil {
return nil, sqlError.Wrap(err)
}

Expand Down Expand Up @@ -1250,6 +1288,7 @@ func modelToEntry(tx *gorm.DB, model RegisteredEntry) (*common.RegistrationEntry
FederatesWith: federatesWith,
Admin: model.Admin,
Downstream: model.Downstream,
Expiry: model.Expiry,
}, nil
}

Expand Down
76 changes: 76 additions & 0 deletions pkg/server/plugin/datastore/sql/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,54 @@ func (s *PluginSuite) TestFetchRegistrationEntry() {
s.Equal(createdEntry, fetchRegistrationEntryResponse.Entry)
}

func (s *PluginSuite) TestPruneRegistrationEntries() {
now := time.Now().Unix()
registeredEntry := &datastore.RegistrationEntry{
Selectors: []*common.Selector{
{Type: "Type1", Value: "Value1"},
{Type: "Type2", Value: "Value2"},
{Type: "Type3", Value: "Value3"},
},
SpiffeId: "SpiffeId",
ParentId: "ParentId",
Ttl: 1,
Expiry: now,
}
createRegistrationEntryResponse, err := s.ds.CreateRegistrationEntry(ctx, &datastore.CreateRegistrationEntryRequest{Entry: registeredEntry})
s.Require().NoError(err)
s.Require().NotNil(createRegistrationEntryResponse)
createdEntry := createRegistrationEntryResponse.Entry

// Ensure we don't prune valid entries, wind clock back 10s
_, err = s.ds.PruneRegistrationEntries(ctx, &datastore.PruneRegistrationEntriesRequest{
ExpiresBefore: now - 10,
})
s.Require().NoError(err)
fetchRegistrationEntryResponse, err := s.ds.FetchRegistrationEntry(ctx, &datastore.FetchRegistrationEntryRequest{EntryId: createdEntry.EntryId})
s.Require().NoError(err)
s.Require().NotNil(fetchRegistrationEntryResponse)
s.Equal(createdEntry, fetchRegistrationEntryResponse.Entry)

// Ensure we don't prune on the exact ExpiresBefore
_, err = s.ds.PruneRegistrationEntries(ctx, &datastore.PruneRegistrationEntriesRequest{
ExpiresBefore: now,
})
s.Require().NoError(err)
fetchRegistrationEntryResponse, err = s.ds.FetchRegistrationEntry(ctx, &datastore.FetchRegistrationEntryRequest{EntryId: createdEntry.EntryId})
s.Require().NoError(err)
s.Require().NotNil(fetchRegistrationEntryResponse)
s.Equal(createdEntry, fetchRegistrationEntryResponse.Entry)

// Ensure we prune old entries
_, err = s.ds.PruneRegistrationEntries(ctx, &datastore.PruneRegistrationEntriesRequest{
ExpiresBefore: now + 10,
})
s.Require().NoError(err)
fetchRegistrationEntryResponse, err = s.ds.FetchRegistrationEntry(ctx, &datastore.FetchRegistrationEntryRequest{EntryId: createdEntry.EntryId})
s.Require().NoError(err)
s.Nil(fetchRegistrationEntryResponse.Entry)
}

func (s *PluginSuite) TestFetchInexistentRegistrationEntry() {
fetchRegistrationEntryResponse, err := s.ds.FetchRegistrationEntry(ctx, &datastore.FetchRegistrationEntryRequest{EntryId: "INEXISTENT"})
s.Require().NoError(err)
Expand Down Expand Up @@ -1196,6 +1244,17 @@ func (s *PluginSuite) TestPruneJoinTokens() {
s.Require().NoError(err)
s.Equal("foobar", resp.JoinToken.Token)

// Ensure we don't prune on the exact ExpiresBefore
_, err = s.ds.PruneJoinTokens(ctx, &datastore.PruneJoinTokensRequest{
ExpiresBefore: now,
})
s.Require().NoError(err)
resp, err = s.ds.FetchJoinToken(ctx, &datastore.FetchJoinTokenRequest{
Token: joinToken.Token,
})
s.Require().NoError(err)
s.Equal("foobar", resp.JoinToken.Token)

// Ensure we prune old tokens
joinToken.Expiry = (now + 10)
_, err = s.ds.PruneJoinTokens(ctx, &datastore.PruneJoinTokensRequest{
Expand Down Expand Up @@ -1324,6 +1383,23 @@ func (s *PluginSuite) TestMigration() {
s.Require().NoError(err)
s.Require().Len(resp.Entries, 1)
s.Require().True(resp.Entries[0].Downstream)
case 6:
resp, err := s.ds.ListRegistrationEntries(context.Background(), &datastore.ListRegistrationEntriesRequest{})
s.Require().NoError(err)
s.Require().Len(resp.Entries, 1)
s.Require().Zero(resp.Entries[0].Expiry)

expiryVal := time.Now().Unix()
resp.Entries[0].Expiry = expiryVal
_, err = s.ds.UpdateRegistrationEntry(context.Background(), &datastore.UpdateRegistrationEntryRequest{
Entry: resp.Entries[0],
})
s.Require().NoError(err)

resp, err = s.ds.ListRegistrationEntries(context.Background(), &datastore.ListRegistrationEntriesRequest{})
s.Require().NoError(err)
s.Require().Len(resp.Entries, 1)
s.Require().Equal(expiryVal, resp.Entries[0].Expiry)
default:
s.T().Fatalf("no migration test added for version %d", i)
}
Expand Down
1 change: 1 addition & 0 deletions proto/agent/nodeattestor/README_pb.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ manage the various registered nodes and workloads that are controlled by it.
| entry_id | [string](#string) | | Entry ID |
| admin | [bool](#bool) | | Whether or not the workload is an admin workload. Admin workloads can use their SVID&#39;s to authenticate with the Registration API, for example. |
| downstream | [bool](#bool) | | To enable signing CA CSR in upstream spire server |
| expiry | [int64](#int64) | | Expiration of this entry |



Expand Down
1 change: 1 addition & 0 deletions proto/agent/workloadattestor/README_pb.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ manage the various registered nodes and workloads that are controlled by it.
| entry_id | [string](#string) | | Entry ID |
| admin | [bool](#bool) | | Whether or not the workload is an admin workload. Admin workloads can use their SVID&#39;s to authenticate with the Registration API, for example. |
| downstream | [bool](#bool) | | To enable signing CA CSR in upstream spire server |
| expiry | [int64](#int64) | | Expiration of this entry |



Expand Down
1 change: 1 addition & 0 deletions proto/api/node/README_pb.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ manage the various registered nodes and workloads that are controlled by it.
| entry_id | [string](#string) | | Entry ID |
| admin | [bool](#bool) | | Whether or not the workload is an admin workload. Admin workloads can use their SVID&#39;s to authenticate with the Registration API, for example. |
| downstream | [bool](#bool) | | To enable signing CA CSR in upstream spire server |
| expiry | [int64](#int64) | | Expiration of this entry |



Expand Down
1 change: 1 addition & 0 deletions proto/api/registration/README_pb.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ manage the various registered nodes and workloads that are controlled by it.
| entry_id | [string](#string) | | Entry ID |
| admin | [bool](#bool) | | Whether or not the workload is an admin workload. Admin workloads can use their SVID&#39;s to authenticate with the Registration API, for example. |
| downstream | [bool](#bool) | | To enable signing CA CSR in upstream spire server |
| expiry | [int64](#int64) | | Expiration of this entry |



Expand Down
1 change: 1 addition & 0 deletions proto/common/README_pb.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ manage the various registered nodes and workloads that are controlled by it.
| entry_id | [string](#string) | | Entry ID |
| admin | [bool](#bool) | | Whether or not the workload is an admin workload. Admin workloads can use their SVID&#39;s to authenticate with the Registration API, for example. |
| downstream | [bool](#bool) | | To enable signing CA CSR in upstream spire server |
| expiry | [int64](#int64) | | Expiration of this entry |



Expand Down
Loading

0 comments on commit 8eb77cb

Please sign in to comment.