diff --git a/pkg/agent/attestor/node/node.go b/pkg/agent/attestor/node/node.go index c47f92e39d..1db6cc1a02 100644 --- a/pkg/agent/attestor/node/node.go +++ b/pkg/agent/attestor/node/node.go @@ -307,6 +307,9 @@ func (a *attestor) serverCredFunc(bundle []*x509.Certificate) func() (credential } func (a *attestor) parseAttestationResponse(id string, r *node.AttestResponse) (*x509.Certificate, []*x509.Certificate, error) { + if r.SvidUpdate == nil { + return nil, nil, errors.New("response missing svid update") + } if len(r.SvidUpdate.Svids) < 1 { return nil, nil, errors.New("no svid received") } @@ -321,12 +324,21 @@ func (a *attestor) parseAttestationResponse(id string, r *node.AttestResponse) ( return nil, nil, fmt.Errorf("invalid svid: %v", err) } - bundle, err := x509.ParseCertificates(r.SvidUpdate.Bundle) + if r.SvidUpdate.Bundles == nil { + return nil, nil, errors.New("missing bundles") + } + + bundle := r.SvidUpdate.Bundles[a.c.TrustDomain.String()] + if bundle == nil { + return nil, nil, errors.New("missing bundle") + } + + bundleCerts, err := x509.ParseCertificates(bundle.CaCerts) if err != nil { return nil, nil, fmt.Errorf("invalid bundle: %v", bundle) } - return svid, bundle, nil + return svid, bundleCerts, nil } func (a *attestor) serverID() *url.URL { diff --git a/pkg/agent/attestor/node/node_test.go b/pkg/agent/attestor/node/node_test.go index 73791827d6..9563f800a4 100644 --- a/pkg/agent/attestor/node/node_test.go +++ b/pkg/agent/attestor/node/node_test.go @@ -237,6 +237,9 @@ func (s *NodeAttestorTestSuite) setAttestResponse(challenges []challengeResponse svid, _, err := util.LoadSVIDFixture() s.Require().NoError(err) + bundle, err := util.LoadBundleFixture() + s.Require().NoError(err) + stream := mock_node.NewMockNode_AttestClient(s.ctrl) stream.EXPECT().Send(gomock.Any()) for _, challenge := range challenges { @@ -251,7 +254,14 @@ func (s *NodeAttestorTestSuite) setAttestResponse(challenges []challengeResponse "spiffe://example.com/spire/agent/join_token/foobar": &node.X509SVID{ Cert: svid.Raw, ExpiresAt: svid.NotAfter.Unix(), - }}, + }, + }, + Bundles: map[string]*node.Bundle{ + "spiffe://example.com": &node.Bundle{ + Id: "spiffe://example.com", + CaCerts: bundle[0].Raw, + }, + }, }}, nil) stream.EXPECT().CloseSend() stream.EXPECT().Recv().Return(nil, io.EOF) diff --git a/pkg/agent/client/client.go b/pkg/agent/client/client.go index 466b15a2dd..0bb3cfa01c 100644 --- a/pkg/agent/client/client.go +++ b/pkg/agent/client/client.go @@ -120,7 +120,7 @@ func (c *client) FetchUpdates(ctx context.Context, req *node.FetchX509SVIDReques regEntries := map[string]*common.RegistrationEntry{} svids := map[string]*node.X509SVID{} - var lastBundle []byte + bundles := map[string]*node.Bundle{} // Read all the server responses from the stream. for { resp, err := stream.Recv() @@ -129,7 +129,13 @@ func (c *client) FetchUpdates(ctx context.Context, req *node.FetchX509SVIDReques } if err != nil { // There was an error receiving a response, exit loop to return what we have. - return &Update{regEntries, svids, lastBundle}, err + logrus.Errorf("failed to consume entire SVID update stream: %v", err) + return nil, err + } + + if resp.SvidUpdate == nil { + logrus.Warn("empty update in SVID update stream") + continue } for _, re := range resp.SvidUpdate.RegistrationEntries { @@ -138,12 +144,14 @@ func (c *client) FetchUpdates(ctx context.Context, req *node.FetchX509SVIDReques for spiffeid, svid := range resp.SvidUpdate.Svids { svids[spiffeid] = svid } - lastBundle = resp.SvidUpdate.Bundle + for spiffeid, bundle := range resp.SvidUpdate.Bundles { + bundles[spiffeid] = bundle + } } return &Update{ Entries: regEntries, SVIDs: svids, - Bundle: lastBundle, + Bundles: bundles, }, nil } diff --git a/pkg/agent/client/client_test.go b/pkg/agent/client/client_test.go index 1790e65c43..a5385befc5 100644 --- a/pkg/agent/client/client_test.go +++ b/pkg/agent/client/client_test.go @@ -36,7 +36,6 @@ func TestFetchUpdates(t *testing.T) { } res := &node.FetchX509SVIDResponse{ SvidUpdate: &node.X509SVIDUpdate{ - Bundle: []byte{10, 20, 30, 40}, RegistrationEntries: []*common.RegistrationEntry{{ EntryId: "1", }}, @@ -45,6 +44,12 @@ func TestFetchUpdates(t *testing.T) { Cert: []byte{11, 22, 33}, }, }, + Bundles: map[string]*node.Bundle{ + "spiffe://example.org": { + Id: "spiffe://example.org", + CaCerts: []byte{10, 20, 30, 40}, + }, + }, }, } @@ -57,7 +62,7 @@ func TestFetchUpdates(t *testing.T) { update, err := client.FetchUpdates(context.Background(), req) require.Nil(t, err) - assert.Equal(t, res.SvidUpdate.Bundle, update.Bundle) + assert.Equal(t, res.SvidUpdate.Bundles, update.Bundles) assert.Equal(t, res.SvidUpdate.Svids, update.SVIDs) for _, entry := range res.SvidUpdate.RegistrationEntries { assert.Equal(t, entry, update.Entries[entry.EntryId]) diff --git a/pkg/agent/client/update.go b/pkg/agent/client/update.go index 69b8af86b2..d7a7b54541 100644 --- a/pkg/agent/client/update.go +++ b/pkg/agent/client/update.go @@ -11,7 +11,7 @@ import ( type Update struct { Entries map[string]*common.RegistrationEntry SVIDs map[string]*node.X509SVID - Bundle []byte + Bundles map[string]*node.Bundle } func (u *Update) String() string { @@ -38,12 +38,11 @@ func (u *Update) String() string { } buffer.WriteString(" ") } - buffer.WriteString("], Bundle: ") - if u.Bundle != nil && len(u.Bundle) > 0 { - buffer.WriteString("bytes") - } else { - buffer.WriteString("none") + buffer.WriteString("], Bundles: [") + for spiffeid := range u.Bundles { + buffer.WriteString(spiffeid) + buffer.WriteString(" ") } - buffer.WriteString("}") + buffer.WriteString("]}") return buffer.String() } diff --git a/pkg/agent/client/update_test.go b/pkg/agent/client/update_test.go index f93040ccf8..b7f40a4bf6 100644 --- a/pkg/agent/client/update_test.go +++ b/pkg/agent/client/update_test.go @@ -15,7 +15,6 @@ var ( func TestString(t *testing.T) { entries := regEntriesMap["resp1"] u := &Update{ - Bundle: []byte{1, 2, 3}, Entries: map[string]*common.RegistrationEntry{entries[0].EntryId: entries[0]}, SVIDs: map[string]*node.X509SVID{ "spiffe://example.org": { @@ -23,9 +22,15 @@ func TestString(t *testing.T) { ExpiresAt: 5, }, }, + Bundles: map[string]*node.Bundle{ + "spiffe://example.org": { + Id: "spiffe://example.org", + CaCerts: []byte{1, 2, 3}, + }, + }, } - expected := "{ Entries: [{ spiffeID: spiffe://example.org/spire/agent, parentID: spiffe://example.org/spire/agent/join_token/abcd, selectors: [type:\"spiffe_id\" value:\"spiffe://example.org/spire/agent/join_token/abcd\" ]}], SVIDs: [spiffe://example.org: cert:\"\\004\\005\" expires_at:5 ], Bundle: bytes}" + expected := "{ Entries: [{ spiffeID: spiffe://example.org/spire/agent, parentID: spiffe://example.org/spire/agent/join_token/abcd, selectors: [type:\"spiffe_id\" value:\"spiffe://example.org/spire/agent/join_token/abcd\" ]}], SVIDs: [spiffe://example.org: cert:\"\\004\\005\" expires_at:5 ], Bundles: [spiffe://example.org ]}" if u.String() != expected { t.Errorf("expected: %s, got: %s", expected, u.String()) } diff --git a/pkg/agent/endpoints/workload/handler.go b/pkg/agent/endpoints/workload/handler.go index cff6a32e21..07d3ed06b1 100644 --- a/pkg/agent/endpoints/workload/handler.go +++ b/pkg/agent/endpoints/workload/handler.go @@ -100,10 +100,12 @@ func (h *Handler) sendResponse(update *cache.WorkloadUpdate, stream workload.Spi func (h *Handler) composeResponse(update *cache.WorkloadUpdate) (*workload.X509SVIDResponse, error) { resp := new(workload.X509SVIDResponse) resp.Svids = []*workload.X509SVID{} + resp.FederatedBundles = make(map[string][]byte) - bundle := []byte{} - for _, c := range update.Bundle { - bundle = append(bundle, c.Raw...) + bundle := marshalBundle(update.Bundle) + + for id, federatedBundle := range update.FederatedBundles { + resp.FederatedBundles[id] = marshalBundle(federatedBundle) } for _, e := range update.Entries { @@ -115,10 +117,11 @@ func (h *Handler) composeResponse(update *cache.WorkloadUpdate) (*workload.X509S } svid := &workload.X509SVID{ - SpiffeId: id, - X509Svid: e.SVID.Raw, - X509SvidKey: keyData, - Bundle: bundle, + SpiffeId: id, + X509Svid: e.SVID.Raw, + X509SvidKey: keyData, + Bundle: bundle, + FederatesWith: e.RegistrationEntry.FederatesWith, } resp.Svids = append(resp.Svids, svid) @@ -148,3 +151,11 @@ func (h *Handler) callerPID(ctx context.Context) (pid int32, err error) { return info.PID, nil } + +func marshalBundle(certs []*x509.Certificate) []byte { + bundle := []byte{} + for _, c := range certs { + bundle = append(bundle, c.Raw...) + } + return bundle +} diff --git a/pkg/agent/endpoints/workload/handler_test.go b/pkg/agent/endpoints/workload/handler_test.go index 931c7bce1d..b6af31a3ab 100644 --- a/pkg/agent/endpoints/workload/handler_test.go +++ b/pkg/agent/endpoints/workload/handler_test.go @@ -146,13 +146,17 @@ func (s *HandlerTestSuite) TestComposeResponse() { s.Require().NoError(err) svidMsg := &workload.X509SVID{ - SpiffeId: "spiffe://example.org/foo", - X509Svid: update.Entries[0].SVID.Raw, - X509SvidKey: keyData, - Bundle: update.Bundle[0].Raw, + SpiffeId: "spiffe://example.org/foo", + X509Svid: update.Entries[0].SVID.Raw, + X509SvidKey: keyData, + Bundle: update.Bundle[0].Raw, + FederatesWith: []string{"spiffe://otherdomain.test"}, } apiMsg := &workload.X509SVIDResponse{ Svids: []*workload.X509SVID{svidMsg}, + FederatedBundles: map[string][]byte{ + "spiffe://otherdomain.test": update.Bundle[0].Raw, + }, } resp, err := s.h.composeResponse(s.workloadUpdate()) @@ -200,12 +204,16 @@ func (s *HandlerTestSuite) workloadUpdate() *cache.WorkloadUpdate { SVID: svid, PrivateKey: key, RegistrationEntry: &common.RegistrationEntry{ - SpiffeId: "spiffe://example.org/foo", + SpiffeId: "spiffe://example.org/foo", + FederatesWith: []string{"spiffe://otherdomain.test"}, }, } update := &cache.WorkloadUpdate{ Entries: []*cache.Entry{&entry}, Bundle: []*x509.Certificate{ca}, + FederatedBundles: map[string][]*x509.Certificate{ + "spiffe://otherdomain.test": {ca}, + }, } return update diff --git a/pkg/agent/manager/cache/cache.go b/pkg/agent/manager/cache/cache.go index f10e80474b..99bdd7c7b7 100644 --- a/pkg/agent/manager/cache/cache.go +++ b/pkg/agent/manager/cache/cache.go @@ -18,11 +18,57 @@ type Entry struct { RegistrationEntry *common.RegistrationEntry SVID *x509.Certificate PrivateKey *ecdsa.PrivateKey +} + +// Wraps an observer stream to provide a type safe interface +type BundleStream struct { + stream observer.Stream +} + +func NewBundleStream(stream observer.Stream) *BundleStream { + return &BundleStream{ + stream: stream, + } +} + +// Value returns the current value for this stream. +func (b *BundleStream) Value() map[string][]*x509.Certificate { + value, _ := b.stream.Value().(map[string][]*x509.Certificate) + return value +} + +// Changes returns the channel that is closed when a new value is available. +func (b *BundleStream) Changes() chan struct{} { + return b.stream.Changes() +} + +// Next advances this stream to the next state. +// You should never call this unless Changes channel is closed. +func (b *BundleStream) Next() map[string][]*x509.Certificate { + value, _ := b.stream.Next().(map[string][]*x509.Certificate) + return value +} - // Bundles stores the ID => Bundle map for - // federated bundles. The registration entry - // only stores references to the keys here. - Bundles map[string][]byte +// HasNext checks whether there is a new value available. +func (b *BundleStream) HasNext() bool { + return b.stream.HasNext() +} + +// WaitNext waits for Changes to be closed, advances the stream and returns +// the current value. +func (b *BundleStream) WaitNext() map[string][]*x509.Certificate { + value, _ := b.stream.WaitNext().(map[string][]*x509.Certificate) + return value +} + +// Clone creates a new independent stream from this one but sharing the same +// Property. Updates to the property will be reflected in both streams but +// they may have different values depending on when they advance the stream +// with Next. +func (b *BundleStream) Clone() *BundleStream { + return &BundleStream{ + stream: b.stream.Clone(), + } } type Cache interface { @@ -35,17 +81,15 @@ type Cache interface { DeleteEntry(regEntry *common.RegistrationEntry) bool // Entries returns all the in force cached entries. Entries() []*Entry - // IsEmpty returns true if this cache doesn't have any entry. - IsEmpty() bool // Registers and returns a Subscriber, and then sends latest WorkloadUpdate on its channel Subscribe(selectors Selectors) Subscriber - // Set the bundle - SetBundle([]*x509.Certificate) - // Retrieve the bundle + // Set the bundles + SetBundles(map[string][]*x509.Certificate) + // Retrieve the bundle for the trust domain Bundle() []*x509.Certificate - // SubscribeToBundleChanges returns a new observer.Stream of []*x509.Certificate instances. Each - // time the bundle is updated, a new instance is streamed. - SubscribeToBundleChanges() observer.Stream + // SubscribeToBundleChanges returns a bundle stream. Each + // time bundles are updated, a new bundle mapping is streamed. + SubscribeToBundleChanges() *BundleStream } type cacheImpl struct { @@ -54,32 +98,68 @@ type cacheImpl struct { log logrus.FieldLogger m sync.Mutex subscribers *subscribers - bundle observer.Property + trustDomain string + bundles observer.Property notifyMutex sync.Mutex } // New creates a new Cache. -func New(log logrus.FieldLogger, bundle []*x509.Certificate) *cacheImpl { +func New(log logrus.FieldLogger, trustDomain string, bundle []*x509.Certificate) *cacheImpl { + bundles := map[string][]*x509.Certificate{ + trustDomain: bundle, + } return &cacheImpl{ cache: make(map[string]*Entry), log: log.WithField("subsystem_name", "cache"), - bundle: observer.NewProperty(bundle), + trustDomain: trustDomain, + bundles: observer.NewProperty(bundles), subscribers: NewSubscribers(), } } -func (c *cacheImpl) SetBundle(bundle []*x509.Certificate) { - c.bundle.Update(bundle) - subs := c.subscribers.getAll() - c.notifySubscribers(subs) +func (c *cacheImpl) SetBundles(newBundles map[string][]*x509.Certificate) { + // SetBundles() and Bundle()/Bundles() can be called concurrently since + // the "property" is atomic. Before the following code can merge in changes + // it needs to make a copy of the map to mutate so it doesn't modify + // the bundle map out from underneath readers. SetBundles() is not intended + // to be called by more than one goroutine at a time. + + // copy the map + bundles := make(map[string][]*x509.Certificate) + for k, v := range c.Bundles() { + bundles[k] = v + } + + // merge in changes + changed := false + for id, newBundle := range newBundles { + bundle, ok := bundles[id] + if !ok || !certsEqual(bundle, newBundle) { + bundles[id] = newBundle + changed = true + } + } + + // notify subscribers + // TODO: be more selective about which subscribers get updated to reduce + // unnecessary workload updates. + if changed { + c.bundles.Update(bundles) + subs := c.subscribers.getAll() + c.notifySubscribers(subs) + } } func (c *cacheImpl) Bundle() []*x509.Certificate { - return c.bundle.Value().([]*x509.Certificate) + return c.Bundles()[c.trustDomain] } -func (c *cacheImpl) SubscribeToBundleChanges() observer.Stream { - return c.bundle.Observe() +func (c *cacheImpl) Bundles() map[string][]*x509.Certificate { + return c.bundles.Value().(map[string][]*x509.Certificate) +} + +func (c *cacheImpl) SubscribeToBundleChanges() *BundleStream { + return NewBundleStream(c.bundles.Observe()) } func (c *cacheImpl) Entries() []*Entry { @@ -132,7 +212,10 @@ func (c *cacheImpl) notifySubscribers(subs []*subscriber) { defer c.notifyMutex.Unlock() entries := c.Entries() - bundle := c.Bundle() + bundles := c.Bundles() + + bundle := bundles[c.trustDomain] + for _, sub := range subs { sub.m.Lock() // If subscriber is not active any more, remove it. @@ -150,7 +233,24 @@ func (c *cacheImpl) notifySubscribers(subs []*subscriber) { } subEntries := subscriberEntries(sub, entries) - sub.c <- &WorkloadUpdate{Entries: subEntries, Bundle: bundle} + + federatedBundles := make(map[string][]*x509.Certificate) + for _, subEntry := range subEntries { + for _, federatesWith := range subEntry.RegistrationEntry.FederatesWith { + federatedBundle := bundles[federatesWith] + if len(federatedBundle) > 0 { + federatedBundles[federatesWith] = federatedBundle + } + } + } + + update := &WorkloadUpdate{ + Entries: subEntries, + Bundle: bundle, + FederatedBundles: federatedBundles, + } + + sub.c <- update sub.m.Unlock() } } @@ -171,12 +271,6 @@ func (c *cacheImpl) DeleteEntry(regEntry *common.RegistrationEntry) (deleted boo return } -func (c *cacheImpl) IsEmpty() bool { - c.m.Lock() - defer c.m.Unlock() - return len(c.cache) == 0 -} - func subscriberEntries(sub *subscriber, entries []*Entry) (subentries []*Entry) { for _, e := range entries { regEntrySelectors := selector.NewSetFromRaw(e.RegistrationEntry.Selectors) @@ -186,3 +280,17 @@ func subscriberEntries(sub *subscriber, entries []*Entry) (subentries []*Entry) } return } + +func certsEqual(a, b []*x509.Certificate) bool { + if len(a) != len(b) { + return false + } + + for i, cert := range a { + if !cert.Equal(b[i]) { + return false + } + } + + return true +} diff --git a/pkg/agent/manager/cache/cache_test.go b/pkg/agent/manager/cache/cache_test.go index 30e58ee3d3..24e41c34a5 100644 --- a/pkg/agent/manager/cache/cache_test.go +++ b/pkg/agent/manager/cache/cache_test.go @@ -32,7 +32,7 @@ func init() { } func TestCacheImpl_Valid(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) tests := []struct { name string ce *Entry @@ -70,7 +70,7 @@ func TestCacheImpl_Valid(t *testing.T) { } func TestCacheImpl_Invalid(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) tests := []struct { name string ce *Entry @@ -112,7 +112,7 @@ func TestCacheImpl_Invalid(t *testing.T) { } func TestCacheImpl_DeleteEntry(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) tests := []struct { name string ce *Entry @@ -153,7 +153,15 @@ func TestCacheImpl_DeleteEntry(t *testing.T) { } func TestNotifySubscribersDoesntBlockOnSubscriberWrite(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) + + exampleCerts := []*x509.Certificate{{Raw: []byte("EXAMPLE.ORG")}} + otherDomainCerts := []*x509.Certificate{{Raw: []byte("OTHERDOMAIN.TEST")}} + + cache.SetBundles(map[string][]*x509.Certificate{ + "spiffe://example.org": exampleCerts, + "spiffe://otherdomain.test": otherDomainCerts, + }) e1 := &Entry{ RegistrationEntry: &common.RegistrationEntry{ @@ -174,9 +182,10 @@ func TestNotifySubscribersDoesntBlockOnSubscriberWrite(t *testing.T) { Selectors: Selectors{ &common.Selector{Type: "unix", Value: "uid:1111"}, }, - ParentId: "spiffe:parent2", - SpiffeId: "spiffe:test2", - EntryId: "00000000-0000-0000-0000-000000000002", + ParentId: "spiffe:parent2", + SpiffeId: "spiffe:test2", + EntryId: "00000000-0000-0000-0000-000000000002", + FederatesWith: []string{"spiffe://otherdomain.test"}, }, SVID: &x509.Certificate{}, PrivateKey: privateKey, @@ -195,17 +204,24 @@ func TestNotifySubscribersDoesntBlockOnSubscriberWrite(t *testing.T) { wu := <-sub2.Updates() assert.Equal(t, 1, len(wu.Entries)) assert.Equal(t, e1, wu.Entries[0]) + assert.Equal(t, exampleCerts, wu.Bundle) }) + // The second registration entry federates with otherdomain.test. The + // WorkloadUpdate should include that bundle. util.RunWithTimeout(t, 5*time.Second, func() { wu := <-sub1.Updates() assert.Equal(t, 1, len(wu.Entries)) assert.Equal(t, e2, wu.Entries[0]) + assert.Equal(t, exampleCerts, wu.Bundle) + assert.Equal(t, map[string][]*x509.Certificate{ + "spiffe://otherdomain.test": otherDomainCerts, + }, wu.FederatedBundles) }) } func TestNotifySubscribersDoesntPileUpGoroutines(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) e2 := &Entry{ RegistrationEntry: &common.RegistrationEntry{ @@ -234,7 +250,7 @@ func TestNotifySubscribersDoesntPileUpGoroutines(t *testing.T) { } func TestNotifySubscribersNotifiesLatestUpdatesToSlowSubscriber(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) sub := cache.Subscribe(Selectors{&common.Selector{Type: "unix", Value: "uid:1111"}}) @@ -298,7 +314,7 @@ func TestNotifySubscribersNotifiesLatestUpdatesToSlowSubscriber(t *testing.T) { } func TestSubscriberFinish(t *testing.T) { - cache := New(logger, nil) + cache := New(logger, "spiffe://example.org", nil) sub := cache.Subscribe(Selectors{&common.Selector{Type: "unix", Value: "uid:1111"}}) diff --git a/pkg/agent/manager/cache/subscriber.go b/pkg/agent/manager/cache/subscriber.go index fdd0f5434e..75c1a420ab 100644 --- a/pkg/agent/manager/cache/subscriber.go +++ b/pkg/agent/manager/cache/subscriber.go @@ -14,8 +14,9 @@ type Subscriber interface { } type WorkloadUpdate struct { - Entries []*Entry - Bundle []*x509.Certificate + Entries []*Entry + Bundle []*x509.Certificate + FederatedBundles map[string][]*x509.Certificate } type subscriber struct { diff --git a/pkg/agent/manager/config.go b/pkg/agent/manager/config.go index a46e40fb6a..684b0e5e88 100644 --- a/pkg/agent/manager/config.go +++ b/pkg/agent/manager/config.go @@ -47,7 +47,7 @@ func New(c *Config) (*manager, error) { c.RotationInterval = 60 * time.Second } - cache := cache.New(c.Log, c.Bundle) + cache := cache.New(c.Log, c.TrustDomain.String(), c.Bundle) rotCfg := &svid.RotatorConfig{ Log: c.Log, diff --git a/pkg/agent/manager/manager.go b/pkg/agent/manager/manager.go index 3334b5eef3..def4dc70cc 100644 --- a/pkg/agent/manager/manager.go +++ b/pkg/agent/manager/manager.go @@ -37,9 +37,10 @@ type Manager interface { // each time an SVID rotation finishes. SubscribeToSVIDChanges() observer.Stream - // SubscribeToBundleChanges returns a new observer.Stream on which []*x509.Certificate instances are - // received each time the bundle changes. - SubscribeToBundleChanges() observer.Stream + // SubscribeToBundleChanges returns a new bundle stream on which + // map[string][]*x509.Certificate instances are received each time the + // bundle changes. + SubscribeToBundleChanges() *cache.BundleStream // MatchingEntries takes a slice of selectors, and iterates over all the in force entries // in order to find matching cache entries. A cache entry is matched when its RegistrationEntry's @@ -96,7 +97,7 @@ func (m *manager) SubscribeToSVIDChanges() observer.Stream { return m.svid.Subscribe() } -func (m *manager) SubscribeToBundleChanges() observer.Stream { +func (m *manager) SubscribeToBundleChanges() *cache.BundleStream { return m.cache.SubscribeToBundleChanges() } @@ -148,8 +149,8 @@ func (m *manager) runBundleObserver(ctx context.Context) error { case <-ctx.Done(): return nil case <-bundleStream.Changes(): - b := bundleStream.Next().([]*x509.Certificate) - m.storeBundle(b) + b := bundleStream.Next() + m.storeBundle(b[m.c.TrustDomain.String()]) } } } diff --git a/pkg/agent/manager/manager_test.go b/pkg/agent/manager/manager_test.go index c00e6576c3..d4db0c651f 100644 --- a/pkg/agent/manager/manager_test.go +++ b/pkg/agent/manager/manager_test.go @@ -23,6 +23,7 @@ import ( "github.com/spiffe/spire/proto/api/node" "github.com/spiffe/spire/proto/common" "github.com/spiffe/spire/test/util" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -31,6 +32,11 @@ import ( const ( tmpSubdirName = "manager-test" + trustDomain = "example.org" +) + +var ( + trustDomainID = url.URL{Scheme: "spiffe", Host: "example.org"} ) var ( @@ -39,7 +45,6 @@ var ( ) func TestInitializationFailure(t *testing.T) { - trustDomain := "somedomain.com" ca, cakey := createCA(t, trustDomain) baseSVID, baseSVIDKey := createSVID(t, ca, cakey, "spiffe://"+trustDomain+"/agent", 1*time.Hour) @@ -47,7 +52,7 @@ func TestInitializationFailure(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, } m, err := New(c) if err != nil { @@ -64,7 +69,6 @@ func TestStoreBundleOnStartup(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "somedomain.com" ca, cakey := createCA(t, trustDomain) baseSVID, baseSVIDKey := createSVID(t, ca, cakey, "spiffe://"+trustDomain+"/agent", 1*time.Hour) @@ -72,7 +76,7 @@ func TestStoreBundleOnStartup(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: []*x509.Certificate{ca}, @@ -82,9 +86,13 @@ func TestStoreBundleOnStartup(t *testing.T) { t.Fatal(err) } - if !m.bundleAlreadyCached([]*x509.Certificate{ca}) { - t.Fatal("bundle should have been cached in memory") - } + util.RunWithTimeout(t, time.Second, func() { + sub := m.SubscribeToBundleChanges() + bundles := sub.Value() + require.NotNil(t, bundles) + bundleCerts := bundles[trustDomainID.String()] + require.Equal(t, bundleCerts, []*x509.Certificate{ca}) + }) err = m.Initialize(context.Background()) if err == nil { @@ -107,7 +115,6 @@ func TestStoreSVIDOnStartup(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "somedomain.com" ca, cakey := createCA(t, trustDomain) baseSVID, baseSVIDKey := createSVID(t, ca, cakey, "spiffe://"+trustDomain+"/agent", 1*time.Hour) @@ -115,7 +122,7 @@ func TestStoreSVIDOnStartup(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), } @@ -150,8 +157,6 @@ func TestHappyPathWithoutSyncNorRotation(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -175,7 +180,7 @@ func TestHappyPathWithoutSyncNorRotation(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: apiHandler.bundle, @@ -230,8 +235,6 @@ func TestSVIDRotation(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -256,7 +259,7 @@ func TestSVIDRotation(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: apiHandler.bundle, @@ -302,8 +305,6 @@ func TestSynchronization(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -327,7 +328,7 @@ func TestSynchronization(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: apiHandler.bundle, @@ -429,8 +430,6 @@ func TestSynchronizationClearsStaleCacheEntries(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -454,7 +453,7 @@ func TestSynchronizationClearsStaleCacheEntries(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: apiHandler.bundle, @@ -488,8 +487,6 @@ func TestSubscribersGetUpToDateBundle(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -513,7 +510,7 @@ func TestSubscribersGetUpToDateBundle(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: []*x509.Certificate{apiHandler.bundle[0]}, @@ -547,8 +544,6 @@ func TestSurvivesCARotation(t *testing.T) { dir := createTempDir(t) defer removeTempDir(dir) - trustDomain := "example.org" - l, err := net.Listen("tcp", "localhost:") if err != nil { t.Fatal(err) @@ -574,7 +569,7 @@ func TestSurvivesCARotation(t *testing.T) { SVID: baseSVID, SVIDKey: baseSVIDKey, Log: testLogger, - TrustDomain: url.URL{Host: trustDomain}, + TrustDomain: trustDomainID, SVIDCachePath: path.Join(dir, "svid.der"), BundleCachePath: path.Join(dir, "bundle.der"), Bundle: []*x509.Certificate{apiHandler.bundle[0]}, @@ -725,7 +720,12 @@ func newFetchX509SVIDResponse(regEntriesKeys []string, svids svidMap, bundle []* SvidUpdate: &node.X509SVIDUpdate{ RegistrationEntries: regEntries, Svids: svids, - Bundle: bundleBytes.Bytes(), + Bundles: map[string]*node.Bundle{ + trustDomainID.String(): { + Id: trustDomainID.String(), + CaCerts: bundleBytes.Bytes(), + }, + }, }, } } @@ -872,11 +872,6 @@ func (h *mockNodeAPIHandler) FetchJWTSVID(ctx context.Context, req *node.FetchJW return nil, errors.New("oh noes") } -func (h *mockNodeAPIHandler) FetchFederatedBundle(context.Context, *node.FetchFederatedBundleRequest) (*node.FetchFederatedBundleResponse, error) { - h.c.t.Fatalf("unexpected call to FetchFederatedBundle") - return nil, nil -} - func (h *mockNodeAPIHandler) start() { s := grpc.NewServer(h.creds) node.RegisterNodeServer(s, h) diff --git a/pkg/agent/manager/sync.go b/pkg/agent/manager/sync.go index 847f21cfcc..cbf17cd133 100644 --- a/pkg/agent/manager/sync.go +++ b/pkg/agent/manager/sync.go @@ -60,15 +60,12 @@ func (m *manager) fetchUpdates(ctx context.Context, entryRequests map[string]*en return nil, nil, err } - if update.Bundle != nil { - bundle, err := x509.ParseCertificates(update.Bundle) + if update.Bundles != nil { + bundles, err := parseBundles(update.Bundles) if err != nil { return nil, nil, err } - - if !m.bundleAlreadyCached(bundle) { - m.cache.SetBundle(bundle) - } + m.cache.SetBundles(bundles) } return update.Entries, update.SVIDs, nil @@ -131,12 +128,10 @@ func (m *manager) checkExpiredCacheEntries(cEntryRequests entryRequests) error { return err } - bundles := make(map[string][]byte) //TODO: Populate Bundles cacheEntry := &cache.Entry{ RegistrationEntry: entry.RegistrationEntry, SVID: nil, PrivateKey: privateKey, - Bundles: bundles, } cEntryRequests.add(&entryRequest{csr, cacheEntry}) } @@ -154,12 +149,10 @@ func (m *manager) checkForNewCacheEntries(regEntries map[string]*proto.Registrat return err } - bundles := make(map[string][]byte) //TODO: Populate Bundles cacheEntry := &cache.Entry{ RegistrationEntry: regEntry, SVID: nil, PrivateKey: privateKey, - Bundles: bundles, } cEntryRequests.add(&entryRequest{csr, cacheEntry}) } @@ -180,26 +173,6 @@ func (m *manager) newCSR(spiffeID string) (pk *ecdsa.PrivateKey, csr []byte, err return } -func (m *manager) bundleAlreadyCached(bundle []*x509.Certificate) bool { - currentBundle := m.cache.Bundle() - - if currentBundle == nil { - return bundle == nil - } - - if len(bundle) != len(currentBundle) { - return false - } - - for i, cert := range currentBundle { - if !cert.Equal(bundle[i]) { - return false - } - } - - return true -} - // entryRequest holds a CSR and a pre-built cache entry for the RegistrationEntry // contained in the entry field. type entryRequest struct { @@ -215,3 +188,15 @@ func (er entryRequests) add(e *entryRequest) { entryID := e.entry.RegistrationEntry.EntryId er[entryID] = e } + +func parseBundles(bundles map[string]*node.Bundle) (map[string][]*x509.Certificate, error) { + out := make(map[string][]*x509.Certificate) + for _, bundle := range bundles { + certs, err := x509.ParseCertificates(bundle.CaCerts) + if err != nil { + return nil, err + } + out[bundle.Id] = certs + } + return out, nil +} diff --git a/pkg/agent/svid/rotator_config.go b/pkg/agent/svid/rotator_config.go index aa1b4abb87..b5ba99030a 100644 --- a/pkg/agent/svid/rotator_config.go +++ b/pkg/agent/svid/rotator_config.go @@ -8,6 +8,7 @@ import ( "time" "github.com/spiffe/spire/pkg/agent/client" + "github.com/spiffe/spire/pkg/agent/manager/cache" "github.com/imkira/go-observer" "github.com/sirupsen/logrus" @@ -21,7 +22,7 @@ type RotatorConfig struct { SVID *x509.Certificate SVIDKey *ecdsa.PrivateKey - BundleStream observer.Stream + BundleStream *cache.BundleStream SpiffeID string @@ -48,8 +49,8 @@ func NewRotator(c *RotatorConfig) (*rotator, client.Client) { s := state.Value().(State) bsm.RLock() defer bsm.RUnlock() - bundle := c.BundleStream.Value().([]*x509.Certificate) - return s.SVID, s.Key, bundle + bundles := c.BundleStream.Value() + return s.SVID, s.Key, bundles[c.TrustDomain.String()] }, } client := client.New(cfg) diff --git a/pkg/agent/svid/rotator_test.go b/pkg/agent/svid/rotator_test.go index a6b9b97b85..5e5f6bab30 100644 --- a/pkg/agent/svid/rotator_test.go +++ b/pkg/agent/svid/rotator_test.go @@ -11,6 +11,7 @@ import ( "github.com/imkira/go-observer" "github.com/sirupsen/logrus/hooks/test" "github.com/spiffe/spire/pkg/agent/client" + "github.com/spiffe/spire/pkg/agent/manager/cache" "github.com/spiffe/spire/proto/api/node" "github.com/spiffe/spire/test/mock/agent/client" "github.com/spiffe/spire/test/util" @@ -52,7 +53,7 @@ func (s *RotatorTestSuite) SetupTest() { Log: log, TrustDomain: td, SpiffeID: "spiffe://example.org/spire/agent/1234", - BundleStream: s.bundle.Observe(), + BundleStream: cache.NewBundleStream(s.bundle.Observe()), } s.r, _ = NewRotator(c) s.r.client = s.client diff --git a/pkg/server/endpoints/node/handler.go b/pkg/server/endpoints/node/handler.go index 6fb2cf52c4..33181eb7ef 100644 --- a/pkg/server/endpoints/node/handler.go +++ b/pkg/server/endpoints/node/handler.go @@ -198,17 +198,18 @@ func (h *Handler) FetchX509SVID(server node.Node_FetchX509SVIDServer) (err error return errors.New("Error trying sign CSRs") } - bundle, err := h.getBundle(ctx) + ourBundle, bundles, err := h.getBundlesForEntries(ctx, regEntries) if err != nil { - h.c.Log.Errorf("Error retrieving bundle from datastore: %v", err) - return fmt.Errorf("Error retrieving bundle") + h.c.Log.Error(err) + return err } err = server.Send(&node.FetchX509SVIDResponse{ SvidUpdate: &node.X509SVIDUpdate{ Svids: svids, - Bundle: bundle, + DEPRECATEDBundle: ourBundle.CaCerts, RegistrationEntries: regEntries, + Bundles: bundles, }, }) if err != nil { @@ -272,21 +273,20 @@ func (h *Handler) FetchJWTSVID(ctx context.Context, req *node.FetchJWTSVIDReques return nil, err } + _, bundles, err := h.getBundlesForEntries(ctx, regEntries) + if err != nil { + return nil, err + } + return &node.FetchJWTSVIDResponse{ Svid: &node.JWTSVID{ Token: token, ExpiresAt: expiresAt.Unix(), }, + Bundles: bundles, }, nil } -//TODO -func (h *Handler) FetchFederatedBundle( - ctx context.Context, request *node.FetchFederatedBundleRequest) ( - response *node.FetchFederatedBundleResponse, err error) { - return response, nil -} - func (h *Handler) isAttested(ctx context.Context, baseSpiffeID string) (bool, error) { dataStore := h.c.Catalog.DataStores()[0] @@ -376,7 +376,7 @@ func (h *Handler) attestToken(ctx context.Context, return nil, err } if resp.JoinToken == nil { - return nil, errors.New("response missing token") + return nil, errors.New("no such token") } t := resp.JoinToken @@ -508,15 +508,16 @@ func (h *Handler) getAttestResponse(ctx context.Context, return nil, err } - bundle, err := h.getBundle(ctx) + ourBundle, bundles, err := h.getBundlesForEntries(ctx, regEntries) if err != nil { return nil, err } svidUpdate := &node.X509SVIDUpdate{ Svids: svids, - Bundle: bundle, + DEPRECATEDBundle: ourBundle.CaCerts, RegistrationEntries: regEntries, + Bundles: bundles, } return &node.AttestResponse{SvidUpdate: svidUpdate}, nil } @@ -629,12 +630,36 @@ func (h *Handler) buildBaseSVID(ctx context.Context, csr []byte) (*node.X509SVID return makeX509SVID(cert), cert, nil } -// getBundle fetches the current CA bundle from the datastore. -func (h *Handler) getBundle(ctx context.Context) ([]byte, error) { +func (h *Handler) getBundlesForEntries(ctx context.Context, regEntries []*common.RegistrationEntry) (*node.Bundle, map[string]*node.Bundle, error) { + bundles := make(map[string]*node.Bundle) + + ourBundle, err := h.getBundle(ctx, h.c.TrustDomain.String()) + if err != nil { + return nil, nil, err + } + bundles[ourBundle.Id] = ourBundle + + for _, entry := range regEntries { + for _, id := range entry.FederatesWith { + if bundles[id] != nil { + continue + } + bundle, err := h.getBundle(ctx, id) + if err != nil { + return nil, nil, err + } + bundles[id] = bundle + } + } + return ourBundle, bundles, nil +} + +// getBundle fetches a bundle from the datastore, by trust domain +func (h *Handler) getBundle(ctx context.Context, trustDomain string) (*node.Bundle, error) { ds := h.c.Catalog.DataStores()[0] resp, err := ds.FetchBundle(ctx, &datastore.FetchBundleRequest{ - TrustDomain: h.c.TrustDomain.String(), + TrustDomain: trustDomain, }) if err != nil { return nil, fmt.Errorf("get bundle from datastore: %v", err) @@ -643,7 +668,10 @@ func (h *Handler) getBundle(ctx context.Context) ([]byte, error) { return nil, errors.New("response missing bundle") } - return resp.Bundle.CaCerts, nil + return &node.Bundle{ + Id: trustDomain, + CaCerts: resp.Bundle.CaCerts, + }, nil } func getSpiffeIDFromCSR(csrBytes []byte) (spiffeID string, err error) { diff --git a/pkg/server/endpoints/node/handler_test.go b/pkg/server/endpoints/node/handler_test.go index f3f31939c0..7ce7e6ac52 100644 --- a/pkg/server/endpoints/node/handler_test.go +++ b/pkg/server/endpoints/node/handler_test.go @@ -249,13 +249,16 @@ func getAttestTestData() *fetchBaseSVIDData { {Type: "foo", Value: "bar"}, }, ParentId: "spiffe://example.org/path", - SpiffeId: "spiffe://test1"}, + SpiffeId: "spiffe://test1", + }, { Selectors: []*common.Selector{ {Type: "foo", Value: "bar"}, }, ParentId: "spiffe://example.org/path", - SpiffeId: "spiffe://repeated"}} + SpiffeId: "spiffe://repeated", + }, + } data.regEntrySelectorList = []*common.RegistrationEntry{ { @@ -263,7 +266,8 @@ func getAttestTestData() *fetchBaseSVIDData { {Type: "foo", Value: "bar"}, }, ParentId: "spiffe://example.org/path", - SpiffeId: "spiffe://repeated"}, + SpiffeId: "spiffe://repeated", + }, { Selectors: []*common.Selector{ {Type: "foo", Value: "bar"}, @@ -473,8 +477,14 @@ func getExpectedAttest(suite *HandlerTestSuite, baseSpiffeID string, cert *x509. caCert, _, _ := util.LoadCAFixture() svidUpdate := &node.X509SVIDUpdate{ Svids: svids, - Bundle: caCert.Raw, + DEPRECATEDBundle: caCert.Raw, RegistrationEntries: expectedRegEntries, + Bundles: map[string]*node.Bundle{ + testTrustDomain.String(): { + Id: testTrustDomain.String(), + CaCerts: caCert.Raw, + }, + }, } return svidUpdate @@ -525,7 +535,7 @@ func getFetchX509SVIDTestData() *fetchSVIDData { data.nodeSelectors = []*common.Selector{data.selector} data.bySelectorsEntries = []*common.RegistrationEntry{ - {SpiffeId: data.baseSpiffeID, Ttl: 1111}, + {SpiffeId: data.baseSpiffeID, Ttl: 1111, FederatesWith: []string{"spiffe://otherdomain.test"}}, } data.byParentIDEntries = []*common.RegistrationEntry{ @@ -613,6 +623,17 @@ func setFetchX509SVIDExpectations( }, }, nil) + suite.mockDataStore.EXPECT(). + FetchBundle(gomock.Any(), &datastore.FetchBundleRequest{ + TrustDomain: "spiffe://otherdomain.test", + }). + Return(&datastore.FetchBundleResponse{ + Bundle: &datastore.Bundle{ + TrustDomain: "spiffe://otherdomain.test", + CaCerts: caCert.Raw, + }, + }, nil) + suite.mockServerCA.EXPECT().SignX509SVID(gomock.Any(), data.request.Csrs[0], durationFromTTL(data.byParentIDEntries[2].Ttl)).Return(data.generatedCerts[0], nil) @@ -650,8 +671,18 @@ func getExpectedFetchX509SVID(data *fetchSVIDData) *node.X509SVIDUpdate { caCert, _, _ := util.LoadCAFixture() svidUpdate := &node.X509SVIDUpdate{ Svids: svids, - Bundle: caCert.Raw, + DEPRECATEDBundle: caCert.Raw, RegistrationEntries: registrationEntries, + Bundles: map[string]*node.Bundle{ + testTrustDomain.String(): { + Id: testTrustDomain.String(), + CaCerts: caCert.Raw, + }, + "spiffe://otherdomain.test": { + Id: "spiffe://otherdomain.test", + CaCerts: caCert.Raw, + }, + }, } return svidUpdate @@ -681,11 +712,24 @@ func TestFetchJWTSVID(t *testing.T) { log, _ := test.NewNullLogger() dataStore := fakedatastore.New() + dataStore.CreateBundle(ctx, &datastore.CreateBundleRequest{ + Bundle: &datastore.Bundle{ + TrustDomain: "spiffe://example.org", + CaCerts: []byte("EXAMPLE-CERTS"), + }, + }) + dataStore.CreateBundle(ctx, &datastore.CreateBundleRequest{ + Bundle: &datastore.Bundle{ + TrustDomain: "spiffe://otherdomain.test", + CaCerts: []byte("OTHERDOMAIN-CERTS"), + }, + }) dataStore.CreateRegistrationEntry(ctx, &datastore.CreateRegistrationEntryRequest{ Entry: &node.RegistrationEntry{ - ParentId: "spiffe://example.org/spire/agent/join_token/token", - SpiffeId: "spiffe://example.org/blog", - Ttl: 1, + ParentId: "spiffe://example.org/spire/agent/join_token/token", + SpiffeId: "spiffe://example.org/blog", + Ttl: 1, + FederatesWith: []string{"spiffe://otherdomain.test"}, }, }) @@ -697,9 +741,10 @@ func TestFetchJWTSVID(t *testing.T) { catalog.SetDataStores(dataStore) handler := NewHandler(HandlerConfig{ - Catalog: catalog, - ServerCA: serverCA, - Log: log, + Catalog: catalog, + ServerCA: serverCA, + Log: log, + TrustDomain: testTrustDomain, }) // no peer certificate on context @@ -749,6 +794,16 @@ func TestFetchJWTSVID(t *testing.T) { require.NotNil(t, resp) require.NotEmpty(t, resp.Svid.Token) require.NotEqual(t, 0, resp.Svid.ExpiresAt) + require.Equal(t, map[string]*node.Bundle{ + "spiffe://example.org": { + Id: "spiffe://example.org", + CaCerts: []byte("EXAMPLE-CERTS"), + }, + "spiffe://otherdomain.test": { + Id: "spiffe://otherdomain.test", + CaCerts: []byte("OTHERDOMAIN-CERTS"), + }, + }, resp.Bundles) // authorized against a registration entry resp, err = handler.FetchJWTSVID(ctx, &node.FetchJWTSVIDRequest{ @@ -761,4 +816,14 @@ func TestFetchJWTSVID(t *testing.T) { require.NotNil(t, resp) require.NotEmpty(t, resp.Svid.Token) require.NotEqual(t, 0, resp.Svid.ExpiresAt) + require.Equal(t, map[string]*node.Bundle{ + "spiffe://example.org": { + Id: "spiffe://example.org", + CaCerts: []byte("EXAMPLE-CERTS"), + }, + "spiffe://otherdomain.test": { + Id: "spiffe://otherdomain.test", + CaCerts: []byte("OTHERDOMAIN-CERTS"), + }, + }, resp.Bundles) } diff --git a/proto/api/node/README_pb.md b/proto/api/node/README_pb.md index 51341ff9c5..0f5fff2710 100644 --- a/proto/api/node/README_pb.md +++ b/proto/api/node/README_pb.md @@ -18,17 +18,17 @@ - [node.proto](#node.proto) - [AttestRequest](#spire.api.node.AttestRequest) - [AttestResponse](#spire.api.node.AttestResponse) - - [FetchFederatedBundleRequest](#spire.api.node.FetchFederatedBundleRequest) - - [FetchFederatedBundleResponse](#spire.api.node.FetchFederatedBundleResponse) - - [FetchFederatedBundleResponse.FederatedBundlesEntry](#spire.api.node.FetchFederatedBundleResponse.FederatedBundlesEntry) + - [Bundle](#spire.api.node.Bundle) - [FetchJWTSVIDRequest](#spire.api.node.FetchJWTSVIDRequest) - [FetchJWTSVIDResponse](#spire.api.node.FetchJWTSVIDResponse) + - [FetchJWTSVIDResponse.BundlesEntry](#spire.api.node.FetchJWTSVIDResponse.BundlesEntry) - [FetchX509SVIDRequest](#spire.api.node.FetchX509SVIDRequest) - [FetchX509SVIDResponse](#spire.api.node.FetchX509SVIDResponse) - [JSR](#spire.api.node.JSR) - [JWTSVID](#spire.api.node.JWTSVID) - [X509SVID](#spire.api.node.X509SVID) - [X509SVIDUpdate](#spire.api.node.X509SVIDUpdate) + - [X509SVIDUpdate.BundlesEntry](#spire.api.node.X509SVIDUpdate.BundlesEntry) - [X509SVIDUpdate.SvidsEntry](#spire.api.node.X509SVIDUpdate.SvidsEntry) @@ -191,76 +191,63 @@ all current Registration Entries which are relevant to the caller SPIFFE ID - + -### FetchFederatedBundleRequest -Represents a request with an array of SPIFFE Ids. +### Bundle +Trust domain bundle | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| spiffe_id | [string](#string) | repeated | An array of SPIFFE Ids. | +| id | [string](#string) | | bundle identifier, i.e. the SPIFFE ID for the trust domain | +| ca_certs | [bytes](#bytes) | | bundle data (ASN.1 encoded X.509 certificates) | - - -### FetchFederatedBundleResponse -Represents a response with a map of SPIFFE Id, Federated CA Bundle. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| federated_bundles | [FetchFederatedBundleResponse.FederatedBundlesEntry](#spire.api.node.FetchFederatedBundleResponse.FederatedBundlesEntry) | repeated | Map [ SPIFFE ID ] => Federated CA Bundle | - - - - - - - + -### FetchFederatedBundleResponse.FederatedBundlesEntry +### FetchJWTSVIDRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| key | [string](#string) | | | -| value | [bytes](#bytes) | | | +| jsr | [JSR](#spire.api.node.JSR) | | The JWT signing request | - + -### FetchJWTSVIDRequest +### FetchJWTSVIDResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| jsr | [JSR](#spire.api.node.JSR) | | The JWT signing request | +| svid | [JWTSVID](#spire.api.node.JWTSVID) | | The signed JWT-SVID | +| bundles | [FetchJWTSVIDResponse.BundlesEntry](#spire.api.node.FetchJWTSVIDResponse.BundlesEntry) | repeated | Trust bundles associated with the SVID, keyed by trust domain SPIFFE ID. Bundles included are the trust bundle for the server trust domain and any federated trust domain bundles applicable to the SVID. | - + -### FetchJWTSVIDResponse +### FetchJWTSVIDResponse.BundlesEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| svid | [JWTSVID](#spire.api.node.JWTSVID) | | The signed JWT-SVID | +| key | [string](#string) | | | +| value | [Bundle](#spire.api.node.Bundle) | | | @@ -358,8 +345,25 @@ a list of all current Registration Entries which are relevant to the caller SPIF | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | svids | [X509SVIDUpdate.SvidsEntry](#spire.api.node.X509SVIDUpdate.SvidsEntry) | repeated | A map containing SVID values and corresponding SPIFFE IDs as the keys. Map[SPIFFE_ID] => SVID. | -| bundle | [bytes](#bytes) | | Latest SPIRE Server bundle | +| DEPRECATED_bundle | [bytes](#bytes) | | DEPRECATED. Latest SPIRE Server bundle. | | registration_entries | [.spire.common.RegistrationEntry](#spire.api.node..spire.common.RegistrationEntry) | repeated | A type representing a curated record that the Spire Server uses to set up and manage the various registered nodes and workloads that are controlled by it. | +| bundles | [X509SVIDUpdate.BundlesEntry](#spire.api.node.X509SVIDUpdate.BundlesEntry) | repeated | Trust bundles associated with the SVIDs, keyed by trust domain SPIFFE ID. Bundles included are the trust bundle for the server trust domain and any federated trust domain bundles applicable to the SVIDs. Supersedes the deprecated `bundle` field. | + + + + + + + + +### X509SVIDUpdate.BundlesEntry + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| key | [string](#string) | | | +| value | [Bundle](#spire.api.node.Bundle) | | | @@ -398,7 +402,6 @@ a list of all current Registration Entries which are relevant to the caller SPIF | Attest | [AttestRequest](#spire.api.node.AttestRequest) | [AttestResponse](#spire.api.node.AttestRequest) | Attest the node, get base node SVID. | | FetchX509SVID | [FetchX509SVIDRequest](#spire.api.node.FetchX509SVIDRequest) | [FetchX509SVIDResponse](#spire.api.node.FetchX509SVIDRequest) | Get Workload, Node Agent certs and CA trust bundles. Also used for rotation Base Node SVID or the Registered Node SVID used for this call) List can be empty to allow Node Agent cache refresh). | | FetchJWTSVID | [FetchJWTSVIDRequest](#spire.api.node.FetchJWTSVIDRequest) | [FetchJWTSVIDResponse](#spire.api.node.FetchJWTSVIDRequest) | Fetches a signed JWT-SVID for a workload intended for a specific audience. | -| FetchFederatedBundle | [FetchFederatedBundleRequest](#spire.api.node.FetchFederatedBundleRequest) | [FetchFederatedBundleResponse](#spire.api.node.FetchFederatedBundleRequest) | Called by the Node Agent to fetch the named Federated CA Bundle. Used in the event that authorized workloads reference a Federated Bundle. | diff --git a/proto/api/node/node.pb.go b/proto/api/node/node.pb.go index 86da4635d2..0b4df6ed86 100644 --- a/proto/api/node/node.pb.go +++ b/proto/api/node/node.pb.go @@ -42,6 +42,55 @@ type RegistrationEntry = common.RegistrationEntry // RegistrationEntries from public import github.com/spiffe/spire/proto/common/common.proto type RegistrationEntries = common.RegistrationEntries +// * Trust domain bundle +type Bundle struct { + // bundle identifier, i.e. the SPIFFE ID for the trust domain + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // bundle data (ASN.1 encoded X.509 certificates) + CaCerts []byte `protobuf:"bytes,2,opt,name=ca_certs,json=caCerts,proto3" json:"ca_certs,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Bundle) Reset() { *m = Bundle{} } +func (m *Bundle) String() string { return proto.CompactTextString(m) } +func (*Bundle) ProtoMessage() {} +func (*Bundle) Descriptor() ([]byte, []int) { + return fileDescriptor_node_2016e66bdd7f3eaa, []int{0} +} +func (m *Bundle) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Bundle.Unmarshal(m, b) +} +func (m *Bundle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Bundle.Marshal(b, m, deterministic) +} +func (dst *Bundle) XXX_Merge(src proto.Message) { + xxx_messageInfo_Bundle.Merge(dst, src) +} +func (m *Bundle) XXX_Size() int { + return xxx_messageInfo_Bundle.Size(m) +} +func (m *Bundle) XXX_DiscardUnknown() { + xxx_messageInfo_Bundle.DiscardUnknown(m) +} + +var xxx_messageInfo_Bundle proto.InternalMessageInfo + +func (m *Bundle) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Bundle) GetCaCerts() []byte { + if m != nil { + return m.CaCerts + } + return nil +} + // A type which contains the "Spiffe Verifiable Identity Document" and // a TTL indicating when the SVID expires. type X509SVID struct { @@ -58,7 +107,7 @@ func (m *X509SVID) Reset() { *m = X509SVID{} } func (m *X509SVID) String() string { return proto.CompactTextString(m) } func (*X509SVID) ProtoMessage() {} func (*X509SVID) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{0} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{1} } func (m *X509SVID) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_X509SVID.Unmarshal(m, b) @@ -98,21 +147,26 @@ type X509SVIDUpdate struct { // A map containing SVID values and corresponding SPIFFE IDs as the // keys. Map[SPIFFE_ID] => SVID. Svids map[string]*X509SVID `protobuf:"bytes,1,rep,name=svids,proto3" json:"svids,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // Latest SPIRE Server bundle - Bundle []byte `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + // DEPRECATED. Latest SPIRE Server bundle. + DEPRECATEDBundle []byte `protobuf:"bytes,2,opt,name=DEPRECATED_bundle,json=DEPRECATEDBundle,proto3" json:"DEPRECATED_bundle,omitempty"` // A type representing a curated record that the Spire Server uses to set up // and manage the various registered nodes and workloads that are controlled by it. - RegistrationEntries []*common.RegistrationEntry `protobuf:"bytes,3,rep,name=registration_entries,json=registrationEntries,proto3" json:"registration_entries,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + RegistrationEntries []*common.RegistrationEntry `protobuf:"bytes,3,rep,name=registration_entries,json=registrationEntries,proto3" json:"registration_entries,omitempty"` + // Trust bundles associated with the SVIDs, keyed by trust domain SPIFFE + // ID. Bundles included are the trust bundle for the server trust domain + // and any federated trust domain bundles applicable to the SVIDs. + // Supersedes the deprecated `bundle` field. + Bundles map[string]*Bundle `protobuf:"bytes,4,rep,name=bundles,proto3" json:"bundles,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *X509SVIDUpdate) Reset() { *m = X509SVIDUpdate{} } func (m *X509SVIDUpdate) String() string { return proto.CompactTextString(m) } func (*X509SVIDUpdate) ProtoMessage() {} func (*X509SVIDUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{1} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{2} } func (m *X509SVIDUpdate) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_X509SVIDUpdate.Unmarshal(m, b) @@ -139,9 +193,9 @@ func (m *X509SVIDUpdate) GetSvids() map[string]*X509SVID { return nil } -func (m *X509SVIDUpdate) GetBundle() []byte { +func (m *X509SVIDUpdate) GetDEPRECATEDBundle() []byte { if m != nil { - return m.Bundle + return m.DEPRECATEDBundle } return nil } @@ -153,6 +207,13 @@ func (m *X509SVIDUpdate) GetRegistrationEntries() []*common.RegistrationEntry { return nil } +func (m *X509SVIDUpdate) GetBundles() map[string]*Bundle { + if m != nil { + return m.Bundles + } + return nil +} + // JSR is a JWT SVID signing request. type JSR struct { // SPIFFE ID of the workload @@ -171,7 +232,7 @@ func (m *JSR) Reset() { *m = JSR{} } func (m *JSR) String() string { return proto.CompactTextString(m) } func (*JSR) ProtoMessage() {} func (*JSR) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{2} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{3} } func (m *JSR) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_JSR.Unmarshal(m, b) @@ -227,7 +288,7 @@ func (m *JWTSVID) Reset() { *m = JWTSVID{} } func (m *JWTSVID) String() string { return proto.CompactTextString(m) } func (*JWTSVID) ProtoMessage() {} func (*JWTSVID) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{3} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{4} } func (m *JWTSVID) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_JWTSVID.Unmarshal(m, b) @@ -278,7 +339,7 @@ func (m *AttestRequest) Reset() { *m = AttestRequest{} } func (m *AttestRequest) String() string { return proto.CompactTextString(m) } func (*AttestRequest) ProtoMessage() {} func (*AttestRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{4} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{5} } func (m *AttestRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttestRequest.Unmarshal(m, b) @@ -338,7 +399,7 @@ func (m *AttestResponse) Reset() { *m = AttestResponse{} } func (m *AttestResponse) String() string { return proto.CompactTextString(m) } func (*AttestResponse) ProtoMessage() {} func (*AttestResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{5} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{6} } func (m *AttestResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttestResponse.Unmarshal(m, b) @@ -385,7 +446,7 @@ func (m *FetchX509SVIDRequest) Reset() { *m = FetchX509SVIDRequest{} } func (m *FetchX509SVIDRequest) String() string { return proto.CompactTextString(m) } func (*FetchX509SVIDRequest) ProtoMessage() {} func (*FetchX509SVIDRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{6} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{7} } func (m *FetchX509SVIDRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchX509SVIDRequest.Unmarshal(m, b) @@ -427,7 +488,7 @@ func (m *FetchX509SVIDResponse) Reset() { *m = FetchX509SVIDResponse{} } func (m *FetchX509SVIDResponse) String() string { return proto.CompactTextString(m) } func (*FetchX509SVIDResponse) ProtoMessage() {} func (*FetchX509SVIDResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{7} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{8} } func (m *FetchX509SVIDResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchX509SVIDResponse.Unmarshal(m, b) @@ -466,7 +527,7 @@ func (m *FetchJWTSVIDRequest) Reset() { *m = FetchJWTSVIDRequest{} } func (m *FetchJWTSVIDRequest) String() string { return proto.CompactTextString(m) } func (*FetchJWTSVIDRequest) ProtoMessage() {} func (*FetchJWTSVIDRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{8} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{9} } func (m *FetchJWTSVIDRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchJWTSVIDRequest.Unmarshal(m, b) @@ -495,17 +556,21 @@ func (m *FetchJWTSVIDRequest) GetJsr() *JSR { type FetchJWTSVIDResponse struct { // The signed JWT-SVID - Svid *JWTSVID `protobuf:"bytes,1,opt,name=svid,proto3" json:"svid,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Svid *JWTSVID `protobuf:"bytes,1,opt,name=svid,proto3" json:"svid,omitempty"` + // Trust bundles associated with the SVID, keyed by trust domain SPIFFE + // ID. Bundles included are the trust bundle for the server trust domain + // and any federated trust domain bundles applicable to the SVID. + Bundles map[string]*Bundle `protobuf:"bytes,2,rep,name=bundles,proto3" json:"bundles,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *FetchJWTSVIDResponse) Reset() { *m = FetchJWTSVIDResponse{} } func (m *FetchJWTSVIDResponse) String() string { return proto.CompactTextString(m) } func (*FetchJWTSVIDResponse) ProtoMessage() {} func (*FetchJWTSVIDResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{9} + return fileDescriptor_node_2016e66bdd7f3eaa, []int{10} } func (m *FetchJWTSVIDResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchJWTSVIDResponse.Unmarshal(m, b) @@ -532,89 +597,18 @@ func (m *FetchJWTSVIDResponse) GetSvid() *JWTSVID { return nil } -// Represents a request with an array of SPIFFE Ids. -type FetchFederatedBundleRequest struct { - // An array of SPIFFE Ids. - SpiffeId []string `protobuf:"bytes,1,rep,name=spiffe_id,json=spiffeId,proto3" json:"spiffe_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FetchFederatedBundleRequest) Reset() { *m = FetchFederatedBundleRequest{} } -func (m *FetchFederatedBundleRequest) String() string { return proto.CompactTextString(m) } -func (*FetchFederatedBundleRequest) ProtoMessage() {} -func (*FetchFederatedBundleRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{10} -} -func (m *FetchFederatedBundleRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FetchFederatedBundleRequest.Unmarshal(m, b) -} -func (m *FetchFederatedBundleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FetchFederatedBundleRequest.Marshal(b, m, deterministic) -} -func (dst *FetchFederatedBundleRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_FetchFederatedBundleRequest.Merge(dst, src) -} -func (m *FetchFederatedBundleRequest) XXX_Size() int { - return xxx_messageInfo_FetchFederatedBundleRequest.Size(m) -} -func (m *FetchFederatedBundleRequest) XXX_DiscardUnknown() { - xxx_messageInfo_FetchFederatedBundleRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_FetchFederatedBundleRequest proto.InternalMessageInfo - -func (m *FetchFederatedBundleRequest) GetSpiffeId() []string { - if m != nil { - return m.SpiffeId - } - return nil -} - -// Represents a response with a map of SPIFFE Id, Federated CA Bundle. -type FetchFederatedBundleResponse struct { - // Map [ SPIFFE ID ] => Federated CA Bundle - FederatedBundles map[string][]byte `protobuf:"bytes,1,rep,name=federated_bundles,json=federatedBundles,proto3" json:"federated_bundles,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FetchFederatedBundleResponse) Reset() { *m = FetchFederatedBundleResponse{} } -func (m *FetchFederatedBundleResponse) String() string { return proto.CompactTextString(m) } -func (*FetchFederatedBundleResponse) ProtoMessage() {} -func (*FetchFederatedBundleResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_node_03d3dabc14be23f5, []int{11} -} -func (m *FetchFederatedBundleResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FetchFederatedBundleResponse.Unmarshal(m, b) -} -func (m *FetchFederatedBundleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FetchFederatedBundleResponse.Marshal(b, m, deterministic) -} -func (dst *FetchFederatedBundleResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_FetchFederatedBundleResponse.Merge(dst, src) -} -func (m *FetchFederatedBundleResponse) XXX_Size() int { - return xxx_messageInfo_FetchFederatedBundleResponse.Size(m) -} -func (m *FetchFederatedBundleResponse) XXX_DiscardUnknown() { - xxx_messageInfo_FetchFederatedBundleResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_FetchFederatedBundleResponse proto.InternalMessageInfo - -func (m *FetchFederatedBundleResponse) GetFederatedBundles() map[string][]byte { +func (m *FetchJWTSVIDResponse) GetBundles() map[string]*Bundle { if m != nil { - return m.FederatedBundles + return m.Bundles } return nil } func init() { + proto.RegisterType((*Bundle)(nil), "spire.api.node.Bundle") proto.RegisterType((*X509SVID)(nil), "spire.api.node.X509SVID") proto.RegisterType((*X509SVIDUpdate)(nil), "spire.api.node.X509SVIDUpdate") + proto.RegisterMapType((map[string]*Bundle)(nil), "spire.api.node.X509SVIDUpdate.BundlesEntry") proto.RegisterMapType((map[string]*X509SVID)(nil), "spire.api.node.X509SVIDUpdate.SvidsEntry") proto.RegisterType((*JSR)(nil), "spire.api.node.JSR") proto.RegisterType((*JWTSVID)(nil), "spire.api.node.JWTSVID") @@ -624,9 +618,7 @@ func init() { proto.RegisterType((*FetchX509SVIDResponse)(nil), "spire.api.node.FetchX509SVIDResponse") proto.RegisterType((*FetchJWTSVIDRequest)(nil), "spire.api.node.FetchJWTSVIDRequest") proto.RegisterType((*FetchJWTSVIDResponse)(nil), "spire.api.node.FetchJWTSVIDResponse") - proto.RegisterType((*FetchFederatedBundleRequest)(nil), "spire.api.node.FetchFederatedBundleRequest") - proto.RegisterType((*FetchFederatedBundleResponse)(nil), "spire.api.node.FetchFederatedBundleResponse") - proto.RegisterMapType((map[string][]byte)(nil), "spire.api.node.FetchFederatedBundleResponse.FederatedBundlesEntry") + proto.RegisterMapType((map[string]*Bundle)(nil), "spire.api.node.FetchJWTSVIDResponse.BundlesEntry") } // Reference imports to suppress errors if they are not otherwise used. @@ -649,9 +641,6 @@ type NodeClient interface { FetchX509SVID(ctx context.Context, opts ...grpc.CallOption) (Node_FetchX509SVIDClient, error) // Fetches a signed JWT-SVID for a workload intended for a specific audience. FetchJWTSVID(ctx context.Context, in *FetchJWTSVIDRequest, opts ...grpc.CallOption) (*FetchJWTSVIDResponse, error) - // Called by the Node Agent to fetch the named Federated CA Bundle. - // Used in the event that authorized workloads reference a Federated Bundle. - FetchFederatedBundle(ctx context.Context, in *FetchFederatedBundleRequest, opts ...grpc.CallOption) (*FetchFederatedBundleResponse, error) } type nodeClient struct { @@ -733,15 +722,6 @@ func (c *nodeClient) FetchJWTSVID(ctx context.Context, in *FetchJWTSVIDRequest, return out, nil } -func (c *nodeClient) FetchFederatedBundle(ctx context.Context, in *FetchFederatedBundleRequest, opts ...grpc.CallOption) (*FetchFederatedBundleResponse, error) { - out := new(FetchFederatedBundleResponse) - err := c.cc.Invoke(ctx, "/spire.api.node.Node/FetchFederatedBundle", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // NodeServer is the server API for Node service. type NodeServer interface { // Attest the node, get base node SVID. @@ -752,9 +732,6 @@ type NodeServer interface { FetchX509SVID(Node_FetchX509SVIDServer) error // Fetches a signed JWT-SVID for a workload intended for a specific audience. FetchJWTSVID(context.Context, *FetchJWTSVIDRequest) (*FetchJWTSVIDResponse, error) - // Called by the Node Agent to fetch the named Federated CA Bundle. - // Used in the event that authorized workloads reference a Federated Bundle. - FetchFederatedBundle(context.Context, *FetchFederatedBundleRequest) (*FetchFederatedBundleResponse, error) } func RegisterNodeServer(s *grpc.Server, srv NodeServer) { @@ -831,24 +808,6 @@ func _Node_FetchJWTSVID_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } -func _Node_FetchFederatedBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FetchFederatedBundleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NodeServer).FetchFederatedBundle(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/spire.api.node.Node/FetchFederatedBundle", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NodeServer).FetchFederatedBundle(ctx, req.(*FetchFederatedBundleRequest)) - } - return interceptor(ctx, in, info, handler) -} - var _Node_serviceDesc = grpc.ServiceDesc{ ServiceName: "spire.api.node.Node", HandlerType: (*NodeServer)(nil), @@ -857,10 +816,6 @@ var _Node_serviceDesc = grpc.ServiceDesc{ MethodName: "FetchJWTSVID", Handler: _Node_FetchJWTSVID_Handler, }, - { - MethodName: "FetchFederatedBundle", - Handler: _Node_FetchFederatedBundle_Handler, - }, }, Streams: []grpc.StreamDesc{ { @@ -879,52 +834,53 @@ var _Node_serviceDesc = grpc.ServiceDesc{ Metadata: "node.proto", } -func init() { proto.RegisterFile("node.proto", fileDescriptor_node_03d3dabc14be23f5) } - -var fileDescriptor_node_03d3dabc14be23f5 = []byte{ - // 701 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x5b, 0x4f, 0x13, 0x41, - 0x14, 0x76, 0xd9, 0xb6, 0xd2, 0xd3, 0x82, 0x38, 0x54, 0x6d, 0x96, 0x8b, 0xcd, 0x0a, 0x49, 0x15, - 0xb3, 0xc5, 0x1a, 0x13, 0x25, 0x2a, 0xe1, 0x22, 0x11, 0x4c, 0x0c, 0x99, 0x7a, 0x41, 0x5f, 0xea, - 0xb0, 0x3b, 0x85, 0x95, 0xb2, 0x5b, 0x66, 0xa6, 0x44, 0x9e, 0x7d, 0xf0, 0xd9, 0xff, 0xe6, 0x0f, - 0x32, 0x73, 0x59, 0xe8, 0xae, 0xa5, 0x68, 0xe2, 0x53, 0x67, 0xce, 0x7c, 0xe7, 0x9b, 0x6f, 0xce, - 0xf9, 0x4e, 0x17, 0x20, 0x8a, 0x03, 0xea, 0xf5, 0x58, 0x2c, 0x62, 0x34, 0xc9, 0x7b, 0x21, 0xa3, - 0x1e, 0xe9, 0x85, 0x9e, 0x8c, 0x3a, 0x8f, 0x0e, 0x42, 0x71, 0xd8, 0xdf, 0xf7, 0xfc, 0xf8, 0xb8, - 0xc1, 0x7b, 0x61, 0xa7, 0x43, 0x1b, 0x0a, 0xd1, 0x50, 0xf0, 0x86, 0x1f, 0x1f, 0x1f, 0xc7, 0x91, - 0xf9, 0xd1, 0x14, 0xee, 0x0b, 0x18, 0xdf, 0x7b, 0xb2, 0xfc, 0xac, 0xf5, 0x61, 0x7b, 0x13, 0x21, - 0xc8, 0xf9, 0x94, 0x89, 0xaa, 0x55, 0xb3, 0xea, 0x65, 0xac, 0xd6, 0x68, 0x0e, 0x80, 0x7e, 0x93, - 0x1c, 0xbc, 0x4d, 0x44, 0x75, 0xac, 0x66, 0xd5, 0x6d, 0x5c, 0x34, 0x91, 0x35, 0xe1, 0xfe, 0x1c, - 0x83, 0xc9, 0x24, 0xff, 0x7d, 0x2f, 0x20, 0x82, 0xa2, 0x55, 0xc8, 0xf3, 0xd3, 0x30, 0xe0, 0x55, - 0xab, 0x66, 0xd7, 0x4b, 0xcd, 0xfb, 0x5e, 0x5a, 0xa4, 0x97, 0x86, 0x7b, 0x2d, 0x89, 0x7d, 0x15, - 0x09, 0x76, 0x86, 0x75, 0x1e, 0xba, 0x0d, 0x85, 0xfd, 0x7e, 0x14, 0x74, 0xa9, 0xba, 0xae, 0x8c, - 0xcd, 0x0e, 0x61, 0xa8, 0x30, 0x7a, 0x10, 0x72, 0xc1, 0x88, 0x08, 0xe3, 0xa8, 0x4d, 0x23, 0xc1, - 0x42, 0xca, 0xab, 0xb6, 0xba, 0xe7, 0xae, 0xb9, 0xc7, 0xbc, 0x0e, 0x0f, 0x20, 0x35, 0xfb, 0x34, - 0xcb, 0x84, 0x42, 0xca, 0x1d, 0x0c, 0x70, 0x21, 0x00, 0x4d, 0x81, 0x7d, 0x44, 0xcf, 0xd4, 0xfb, - 0x8b, 0x58, 0x2e, 0x91, 0x07, 0xf9, 0x53, 0xd2, 0xed, 0x6b, 0x29, 0xa5, 0x66, 0xf5, 0xb2, 0xc7, - 0x60, 0x0d, 0x5b, 0x19, 0x7b, 0x6a, 0xb9, 0xbb, 0x60, 0xef, 0xb4, 0x30, 0x9a, 0x81, 0xa2, 0xee, - 0x41, 0x3b, 0x0c, 0x0c, 0xe5, 0xb8, 0x0e, 0x6c, 0x07, 0xc8, 0x81, 0x71, 0xd2, 0x0f, 0x42, 0x1a, - 0xf9, 0x92, 0xda, 0x96, 0x67, 0xc9, 0x5e, 0xaa, 0x10, 0xa2, 0x5b, 0xb5, 0x6b, 0x56, 0x3d, 0x8f, - 0xe5, 0xd2, 0x7d, 0x09, 0xd7, 0x77, 0x3e, 0xbe, 0x53, 0x3d, 0xaa, 0x40, 0x5e, 0xc4, 0x47, 0x34, - 0x32, 0x8c, 0x7a, 0x73, 0x55, 0x97, 0x7e, 0x58, 0x30, 0xb1, 0x26, 0x04, 0xe5, 0x02, 0xd3, 0x93, - 0x3e, 0xe5, 0x02, 0xbd, 0x86, 0x29, 0xa2, 0x02, 0xba, 0x94, 0x01, 0x11, 0x44, 0x31, 0x96, 0x9a, - 0x73, 0xe9, 0x3a, 0xae, 0x5d, 0xa0, 0x36, 0x89, 0x20, 0xf8, 0x06, 0x49, 0x07, 0xa4, 0x5a, 0x9f, - 0x33, 0xd3, 0x2a, 0xb9, 0x94, 0x6f, 0x63, 0x94, 0xf7, 0xe2, 0x88, 0x53, 0xf5, 0x88, 0x32, 0x3e, - 0xdf, 0xbb, 0x31, 0x4c, 0x26, 0x42, 0x74, 0x04, 0xad, 0x42, 0x49, 0xb6, 0xbd, 0xdd, 0x57, 0x76, - 0x30, 0x22, 0xe6, 0x47, 0x9b, 0x06, 0x83, 0x4c, 0x31, 0x7e, 0x9b, 0x85, 0xa2, 0x7f, 0x48, 0xba, - 0x5d, 0x1a, 0x1d, 0x24, 0x8e, 0xb9, 0x08, 0xb8, 0x0f, 0xa0, 0xb2, 0x45, 0x85, 0x7f, 0x78, 0xde, - 0x28, 0x53, 0x00, 0xe9, 0x75, 0xce, 0xb8, 0x2a, 0xbe, 0xf4, 0x3a, 0x67, 0xdc, 0xdd, 0x83, 0x5b, - 0x19, 0xec, 0x7f, 0xd2, 0xe8, 0x3e, 0x87, 0x69, 0xc5, 0x6c, 0xba, 0x98, 0x88, 0x58, 0x04, 0xfb, - 0x2b, 0x67, 0x86, 0x6f, 0x3a, 0xcb, 0xb7, 0xd3, 0xc2, 0x58, 0x9e, 0xbb, 0x1b, 0xe6, 0x0d, 0xe7, - 0xd9, 0x46, 0xd6, 0x12, 0xe4, 0xe4, 0x1d, 0x26, 0xff, 0xce, 0x1f, 0xf9, 0x06, 0xae, 0x40, 0xee, - 0x0a, 0xcc, 0x28, 0x92, 0x2d, 0x1a, 0x50, 0x46, 0x04, 0x0d, 0xd6, 0xd5, 0x54, 0x25, 0x52, 0x32, - 0x6e, 0xb5, 0x07, 0xdd, 0xea, 0xfe, 0xb2, 0x60, 0x76, 0x78, 0xb2, 0x51, 0x12, 0xc3, 0xcd, 0x4e, - 0x72, 0xd4, 0xd6, 0xe3, 0x9a, 0xcc, 0xff, 0x7a, 0x56, 0xd6, 0x28, 0x22, 0x2f, 0x13, 0x37, 0x7f, - 0x0c, 0x53, 0x9d, 0x4c, 0xd8, 0xd9, 0x90, 0xad, 0x1a, 0x02, 0x1d, 0x32, 0xc2, 0x95, 0xc1, 0x11, - 0x2e, 0x0f, 0x0c, 0x6a, 0xf3, 0xbb, 0x0d, 0xb9, 0xb7, 0x71, 0x40, 0xd1, 0x1b, 0x28, 0x68, 0x57, - 0xa2, 0xb9, 0xac, 0xda, 0xd4, 0xd8, 0x38, 0xf3, 0x97, 0x1d, 0x6b, 0xf9, 0x75, 0x6b, 0xd9, 0x42, - 0x5f, 0x60, 0x22, 0xe5, 0x22, 0xb4, 0x30, 0xb4, 0x02, 0x19, 0x43, 0x3a, 0x8b, 0x57, 0xa0, 0x06, - 0x6e, 0xf8, 0x04, 0xe5, 0x41, 0x3f, 0xa0, 0x7b, 0x43, 0x53, 0xd3, 0x5e, 0x73, 0x16, 0x46, 0x83, - 0x4c, 0x23, 0x4f, 0x8c, 0xd5, 0x32, 0xc5, 0x45, 0x4b, 0x7f, 0xd7, 0x45, 0x7d, 0xd5, 0xc3, 0x7f, - 0x69, 0xf9, 0x7a, 0xe1, 0x73, 0x4e, 0x82, 0x76, 0xaf, 0xed, 0x17, 0xd4, 0x27, 0xe9, 0xf1, 0xef, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x67, 0x12, 0x6e, 0x07, 0xe3, 0x06, 0x00, 0x00, +func init() { proto.RegisterFile("node.proto", fileDescriptor_node_2016e66bdd7f3eaa) } + +var fileDescriptor_node_2016e66bdd7f3eaa = []byte{ + // 707 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdd, 0x4e, 0x13, 0x41, + 0x14, 0x76, 0xbb, 0x6d, 0x69, 0x4f, 0x4b, 0xc5, 0x01, 0x75, 0xad, 0x82, 0xcd, 0x0a, 0x49, 0x15, + 0xb3, 0x85, 0x12, 0x13, 0x35, 0x2a, 0x29, 0xb4, 0x46, 0x20, 0x31, 0x64, 0x8a, 0x8a, 0xde, 0xd4, + 0x61, 0x77, 0x80, 0x95, 0xb2, 0x5b, 0x77, 0xa6, 0x44, 0x9e, 0xc0, 0x5b, 0x5f, 0xc9, 0x77, 0xf1, + 0x41, 0xcc, 0xfc, 0xf4, 0x67, 0x57, 0xa0, 0x5e, 0x70, 0xb5, 0x33, 0x67, 0xbe, 0xf3, 0x9d, 0xbf, + 0xef, 0x64, 0x01, 0x82, 0xd0, 0xa3, 0x4e, 0x2f, 0x0a, 0x79, 0x88, 0x4a, 0xac, 0xe7, 0x47, 0xd4, + 0x21, 0x3d, 0xdf, 0x11, 0xd6, 0xf2, 0xea, 0x91, 0xcf, 0x8f, 0xfb, 0x07, 0x8e, 0x1b, 0x9e, 0xd6, + 0x58, 0xcf, 0x3f, 0x3c, 0xa4, 0x35, 0x89, 0xa8, 0x49, 0x78, 0xcd, 0x0d, 0x4f, 0x4f, 0xc3, 0x40, + 0x7f, 0x14, 0x85, 0xbd, 0x06, 0xd9, 0x8d, 0x7e, 0xe0, 0x75, 0x29, 0x2a, 0x41, 0xca, 0xf7, 0x2c, + 0xa3, 0x62, 0x54, 0xf3, 0x38, 0xe5, 0x7b, 0xe8, 0x1e, 0xe4, 0x5c, 0xd2, 0x71, 0x69, 0xc4, 0x99, + 0x95, 0xaa, 0x18, 0xd5, 0x22, 0x9e, 0x72, 0xc9, 0xa6, 0xb8, 0xda, 0xaf, 0x21, 0xb7, 0xff, 0x6c, + 0xe5, 0x45, 0xfb, 0xe3, 0x56, 0x13, 0x21, 0x48, 0x0b, 0x8c, 0x74, 0x2c, 0x62, 0x79, 0x46, 0xf3, + 0x00, 0xf4, 0x87, 0x08, 0xcc, 0x3a, 0x84, 0x4b, 0x67, 0x13, 0xe7, 0xb5, 0xa5, 0xc1, 0xed, 0xdf, + 0x26, 0x94, 0x06, 0xfe, 0x1f, 0x7a, 0x1e, 0xe1, 0x14, 0xad, 0x43, 0x86, 0x9d, 0xf9, 0x1e, 0xb3, + 0x8c, 0x8a, 0x59, 0x2d, 0xd4, 0x1f, 0x3b, 0xf1, 0xca, 0x9c, 0x38, 0xdc, 0x69, 0x0b, 0x6c, 0x2b, + 0xe0, 0xd1, 0x39, 0x56, 0x7e, 0x68, 0x19, 0x6e, 0x35, 0x5b, 0xbb, 0xb8, 0xb5, 0xd9, 0xd8, 0x6b, + 0x35, 0x3b, 0x07, 0xb2, 0x24, 0x9d, 0xf6, 0xcc, 0xe8, 0x41, 0x97, 0x8a, 0x61, 0x2e, 0xa2, 0x47, + 0x3e, 0xe3, 0x11, 0xe1, 0x7e, 0x18, 0x74, 0x68, 0xc0, 0x23, 0x9f, 0x32, 0xcb, 0x94, 0xc1, 0x1f, + 0xea, 0xe0, 0xba, 0x4f, 0x78, 0x0c, 0xa9, 0x42, 0xce, 0x46, 0x09, 0x93, 0x4f, 0x19, 0x6a, 0xc1, + 0x94, 0x8a, 0xca, 0xac, 0xb4, 0xa4, 0x59, 0x9e, 0x50, 0x83, 0xca, 0x45, 0x57, 0x31, 0xf0, 0x2d, + 0x63, 0x80, 0x51, 0x71, 0x68, 0x06, 0xcc, 0x13, 0x7a, 0xae, 0x87, 0x22, 0x8e, 0xc8, 0x81, 0xcc, + 0x19, 0xe9, 0xf6, 0x55, 0x6d, 0x85, 0xba, 0x75, 0x59, 0x10, 0xac, 0x60, 0x2f, 0x53, 0xcf, 0x8d, + 0x32, 0x86, 0xe2, 0x78, 0xb0, 0x0b, 0x58, 0x9f, 0xc6, 0x59, 0xef, 0x24, 0x59, 0x95, 0xfb, 0x18, + 0xa7, 0xbd, 0x0b, 0xe6, 0x76, 0x1b, 0xa3, 0xfb, 0x90, 0x57, 0x42, 0xeb, 0x0c, 0xb5, 0x93, 0x53, + 0x86, 0x2d, 0x0f, 0x95, 0x21, 0x47, 0xfa, 0x9e, 0x4f, 0x03, 0x57, 0x10, 0x9b, 0xe2, 0x6d, 0x70, + 0x17, 0x39, 0x70, 0xde, 0xb5, 0xcc, 0x8a, 0x51, 0xcd, 0x60, 0x71, 0xb4, 0xdf, 0xc0, 0xd4, 0xf6, + 0xa7, 0x3d, 0xa9, 0xa9, 0x39, 0xc8, 0xf0, 0xf0, 0x84, 0x06, 0x9a, 0x51, 0x5d, 0x26, 0xa9, 0xea, + 0xa7, 0x01, 0xd3, 0x0d, 0xce, 0x29, 0xe3, 0x98, 0x7e, 0xef, 0x53, 0xc6, 0xd1, 0x3b, 0x98, 0x21, + 0xd2, 0xa0, 0xa6, 0xec, 0x11, 0x4e, 0x24, 0x63, 0xa1, 0x3e, 0x1f, 0x1f, 0x71, 0x63, 0x84, 0x6a, + 0x12, 0x4e, 0xf0, 0x4d, 0x12, 0x37, 0x88, 0x6c, 0x5d, 0x16, 0x69, 0x3d, 0x89, 0xa3, 0xa8, 0x2d, + 0xa2, 0xac, 0x17, 0x06, 0x8c, 0xca, 0x22, 0x8a, 0x78, 0x78, 0xb7, 0x43, 0x28, 0x0d, 0x12, 0x51, + 0x16, 0xb4, 0x0e, 0x05, 0x21, 0xd3, 0x4e, 0x5f, 0x8e, 0x5e, 0x27, 0xb1, 0x70, 0xb5, 0x40, 0x30, + 0x08, 0x17, 0xbd, 0x1f, 0x0f, 0x20, 0xef, 0x1e, 0x93, 0x6e, 0x97, 0x06, 0x47, 0x03, 0x59, 0x8f, + 0x0c, 0xf6, 0x13, 0x98, 0x7b, 0x4b, 0xb9, 0x7b, 0x3c, 0x1c, 0xbe, 0x6e, 0x80, 0xd8, 0x4d, 0x16, + 0x31, 0xd9, 0x7c, 0xb1, 0x9b, 0x2c, 0x62, 0xf6, 0x3e, 0xdc, 0x4e, 0x60, 0xaf, 0x29, 0x47, 0xfb, + 0x15, 0xcc, 0x4a, 0x66, 0x3d, 0xc5, 0x41, 0x12, 0x4b, 0x60, 0x7e, 0x63, 0x91, 0xe6, 0x9b, 0x4d, + 0xf2, 0x6d, 0xb7, 0x31, 0x16, 0xef, 0xf6, 0x1f, 0x43, 0x17, 0x31, 0x74, 0xd7, 0x79, 0x2d, 0x43, + 0x5a, 0x04, 0xd1, 0x04, 0x77, 0xff, 0x21, 0xd0, 0x70, 0x09, 0x42, 0x3b, 0xa3, 0x2d, 0x4c, 0xc9, + 0x2d, 0x5c, 0x4d, 0xe2, 0x2f, 0x8a, 0x71, 0xe9, 0x2e, 0x5e, 0xfb, 0xde, 0xd4, 0x7f, 0xa5, 0x20, + 0xfd, 0x3e, 0xf4, 0x28, 0xda, 0x81, 0xac, 0x12, 0x09, 0x9a, 0x4f, 0x7a, 0xc5, 0x54, 0x5c, 0x5e, + 0xb8, 0xec, 0x59, 0xe5, 0x5e, 0x35, 0x56, 0x0c, 0xf4, 0x15, 0xa6, 0x63, 0x43, 0x45, 0x8b, 0x17, + 0x96, 0x9d, 0xd0, 0x47, 0x79, 0x69, 0x02, 0x6a, 0x2c, 0xc2, 0x67, 0x28, 0x8e, 0x77, 0x0e, 0x3d, + 0xba, 0xba, 0xaf, 0x8a, 0x7f, 0xf1, 0x7f, 0x9a, 0xbf, 0x91, 0xfd, 0x92, 0x16, 0x8f, 0xbb, 0x37, + 0x0e, 0xb2, 0xf2, 0x9f, 0xb4, 0xf6, 0x37, 0x00, 0x00, 0xff, 0xff, 0x43, 0xcb, 0x03, 0xe1, 0xe4, + 0x06, 0x00, 0x00, } diff --git a/proto/api/node/node.proto b/proto/api/node/node.proto index d15e6725e0..4cb23c083a 100644 --- a/proto/api/node/node.proto +++ b/proto/api/node/node.proto @@ -9,6 +9,15 @@ option go_package = "node"; import public "github.com/spiffe/spire/proto/common/common.proto"; +/** Trust domain bundle */ +message Bundle { + // bundle identifier, i.e. the SPIFFE ID for the trust domain + string id = 1; + + // bundle data (ASN.1 encoded X.509 certificates) + bytes ca_certs = 2; +} + // A type which contains the "Spiffe Verifiable Identity Document" and // a TTL indicating when the SVID expires. message X509SVID { @@ -26,12 +35,18 @@ message X509SVIDUpdate { // keys. Map[SPIFFE_ID] => SVID. map svids = 1; - // Latest SPIRE Server bundle - bytes bundle = 2; + // DEPRECATED. Latest SPIRE Server bundle. + bytes DEPRECATED_bundle = 2; // A type representing a curated record that the Spire Server uses to set up - //and manage the various registered nodes and workloads that are controlled by it. + // and manage the various registered nodes and workloads that are controlled by it. repeated spire.common.RegistrationEntry registration_entries = 3; + + // Trust bundles associated with the SVIDs, keyed by trust domain SPIFFE + // ID. Bundles included are the trust bundle for the server trust domain + // and any federated trust domain bundles applicable to the SVIDs. + // Supersedes the deprecated `bundle` field. + map bundles = 4; } // JSR is a JWT SVID signing request. @@ -103,18 +118,11 @@ message FetchJWTSVIDRequest { message FetchJWTSVIDResponse { // The signed JWT-SVID JWTSVID svid = 1; -} - -// Represents a request with an array of SPIFFE Ids. -message FetchFederatedBundleRequest { - // An array of SPIFFE Ids. - repeated string spiffe_id = 1; -} -// Represents a response with a map of SPIFFE Id, Federated CA Bundle. -message FetchFederatedBundleResponse { - // Map [ SPIFFE ID ] => Federated CA Bundle - map federated_bundles = 1; + // Trust bundles associated with the SVID, keyed by trust domain SPIFFE + // ID. Bundles included are the trust bundle for the server trust domain + // and any federated trust domain bundles applicable to the SVID. + map bundles = 2; } service Node { @@ -128,8 +136,4 @@ service Node { // Fetches a signed JWT-SVID for a workload intended for a specific audience. rpc FetchJWTSVID(FetchJWTSVIDRequest) returns (FetchJWTSVIDResponse); - - // Called by the Node Agent to fetch the named Federated CA Bundle. - // Used in the event that authorized workloads reference a Federated Bundle. - rpc FetchFederatedBundle(FetchFederatedBundleRequest) returns (FetchFederatedBundleResponse); } diff --git a/proto/api/workload/README_pb.md b/proto/api/workload/README_pb.md index 78bcd33053..80eeca73a3 100644 --- a/proto/api/workload/README_pb.md +++ b/proto/api/workload/README_pb.md @@ -38,6 +38,7 @@ information, including CA bundles. | x509_svid | [bytes](#bytes) | | ASN.1 DER encoded certificate chain. MAY include intermediates, the leaf certificate (or SVID itself) MUST come first. | | x509_svid_key | [bytes](#bytes) | | ASN.1 DER encoded PKCS#8 private key. MUST be unencrypted. | | bundle | [bytes](#bytes) | | CA certificates belonging to the Trust Domain ASN.1 DER encoded | +| federates_with | [string](#string) | repeated | List of trust domains the SVID federates with, which corresponds to keys in the federated_bundles map in the X509SVIDResponse message. | diff --git a/proto/api/workload/workload.pb.go b/proto/api/workload/workload.pb.go index efb77cbadc..603fa7522b 100644 --- a/proto/api/workload/workload.pb.go +++ b/proto/api/workload/workload.pb.go @@ -33,7 +33,7 @@ func (m *X509SVIDRequest) Reset() { *m = X509SVIDRequest{} } func (m *X509SVIDRequest) String() string { return proto.CompactTextString(m) } func (*X509SVIDRequest) ProtoMessage() {} func (*X509SVIDRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_workload_9e6c20ae36e034a5, []int{0} + return fileDescriptor_workload_8163f6de28c2610e, []int{0} } func (m *X509SVIDRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_X509SVIDRequest.Unmarshal(m, b) @@ -76,7 +76,7 @@ func (m *X509SVIDResponse) Reset() { *m = X509SVIDResponse{} } func (m *X509SVIDResponse) String() string { return proto.CompactTextString(m) } func (*X509SVIDResponse) ProtoMessage() {} func (*X509SVIDResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_workload_9e6c20ae36e034a5, []int{1} + return fileDescriptor_workload_8163f6de28c2610e, []int{1} } func (m *X509SVIDResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_X509SVIDResponse.Unmarshal(m, b) @@ -129,7 +129,10 @@ type X509SVID struct { X509SvidKey []byte `protobuf:"bytes,3,opt,name=x509_svid_key,json=x509SvidKey,proto3" json:"x509_svid_key,omitempty"` // CA certificates belonging to the Trust Domain // ASN.1 DER encoded - Bundle []byte `protobuf:"bytes,4,opt,name=bundle,proto3" json:"bundle,omitempty"` + Bundle []byte `protobuf:"bytes,4,opt,name=bundle,proto3" json:"bundle,omitempty"` + // List of trust domains the SVID federates with, which corresponds to + // keys in the federated_bundles map in the X509SVIDResponse message. + FederatesWith []string `protobuf:"bytes,5,rep,name=federates_with,json=federatesWith,proto3" json:"federates_with,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -139,7 +142,7 @@ func (m *X509SVID) Reset() { *m = X509SVID{} } func (m *X509SVID) String() string { return proto.CompactTextString(m) } func (*X509SVID) ProtoMessage() {} func (*X509SVID) Descriptor() ([]byte, []int) { - return fileDescriptor_workload_9e6c20ae36e034a5, []int{2} + return fileDescriptor_workload_8163f6de28c2610e, []int{2} } func (m *X509SVID) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_X509SVID.Unmarshal(m, b) @@ -187,6 +190,13 @@ func (m *X509SVID) GetBundle() []byte { return nil } +func (m *X509SVID) GetFederatesWith() []string { + if m != nil { + return m.FederatesWith + } + return nil +} + func init() { proto.RegisterType((*X509SVIDRequest)(nil), "X509SVIDRequest") proto.RegisterType((*X509SVIDResponse)(nil), "X509SVIDResponse") @@ -301,28 +311,29 @@ var _SpiffeWorkloadAPI_serviceDesc = grpc.ServiceDesc{ Metadata: "workload.proto", } -func init() { proto.RegisterFile("workload.proto", fileDescriptor_workload_9e6c20ae36e034a5) } - -var fileDescriptor_workload_9e6c20ae36e034a5 = []byte{ - // 312 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0x4d, 0x4f, 0xf2, 0x40, - 0x10, 0xc7, 0xb3, 0xf4, 0x81, 0xc0, 0x00, 0x8f, 0xed, 0x46, 0xcd, 0x06, 0x0f, 0x36, 0xbd, 0xd8, - 0x53, 0x43, 0x30, 0x18, 0xf1, 0xe6, 0x1b, 0x09, 0xe1, 0x62, 0x8a, 0x51, 0x6f, 0x0d, 0xb0, 0xd3, - 0xd8, 0xd0, 0xd0, 0xda, 0x6d, 0xab, 0xbd, 0xf9, 0x51, 0xfd, 0x28, 0x66, 0xfb, 0x66, 0x52, 0xbd, - 0xcd, 0xfc, 0xe7, 0x37, 0x3b, 0x33, 0xfb, 0x87, 0xff, 0xef, 0x41, 0xb4, 0xf3, 0x83, 0x35, 0xb7, - 0xc2, 0x28, 0x88, 0x03, 0x43, 0x83, 0x83, 0x97, 0xe9, 0x78, 0xb6, 0x7a, 0x5a, 0xdc, 0xd9, 0xf8, - 0x96, 0xa0, 0x88, 0x8d, 0x2f, 0x02, 0xea, 0x8f, 0x26, 0xc2, 0x60, 0x2f, 0x90, 0x9e, 0x42, 0x5b, - 0xa4, 0x1e, 0x17, 0x8c, 0xe8, 0x8a, 0xd9, 0x9f, 0xf4, 0xac, 0x9a, 0x28, 0x74, 0xaa, 0x82, 0xb2, - 0x8d, 0x7c, 0xd6, 0xd2, 0x15, 0x73, 0x60, 0xcb, 0x90, 0x3e, 0x82, 0xe6, 0x22, 0xc7, 0x68, 0x1d, - 0x23, 0x77, 0x36, 0xc9, 0x9e, 0xfb, 0x28, 0x98, 0x92, 0xb7, 0x9f, 0x59, 0xcd, 0x01, 0xd6, 0xbc, - 0x42, 0x6f, 0x0a, 0xf2, 0x7e, 0x1f, 0x47, 0x99, 0xad, 0xba, 0x0d, 0x79, 0x74, 0x0b, 0x47, 0x7f, - 0xa2, 0x72, 0x81, 0x1d, 0x66, 0x8c, 0xe8, 0xc4, 0xec, 0xd9, 0x32, 0xa4, 0x87, 0xd0, 0x4e, 0xd7, - 0x7e, 0x82, 0xac, 0xa5, 0x13, 0x73, 0x60, 0x17, 0xc9, 0x55, 0xeb, 0x92, 0x18, 0x9f, 0x04, 0xba, - 0xd5, 0x06, 0xf4, 0x04, 0x7a, 0x22, 0xf4, 0x5c, 0x17, 0x1d, 0x8f, 0x97, 0xed, 0xdd, 0x42, 0x58, - 0x70, 0x59, 0xfc, 0x98, 0x8e, 0x67, 0x8e, 0x3c, 0xb2, 0x7c, 0xa7, 0x2b, 0x85, 0x55, 0xea, 0x71, - 0x6a, 0xc0, 0xb0, 0x2e, 0x3a, 0x72, 0xb8, 0x92, 0x03, 0xfd, 0x0a, 0x58, 0x62, 0x46, 0x8f, 0xa1, - 0x53, 0xdc, 0xce, 0xfe, 0xe5, 0xc5, 0x32, 0x9b, 0x2c, 0x41, 0x5b, 0xe5, 0x43, 0x9e, 0x4b, 0x43, - 0xae, 0x1f, 0x16, 0xf4, 0x02, 0x86, 0x73, 0x8c, 0xb7, 0xaf, 0xf5, 0x6e, 0xaa, 0xd5, 0x70, 0x67, - 0xa4, 0xfd, 0xfa, 0xba, 0x31, 0xd9, 0x74, 0x72, 0x33, 0xcf, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, - 0x54, 0xd1, 0x4a, 0xc1, 0xde, 0x01, 0x00, 0x00, +func init() { proto.RegisterFile("workload.proto", fileDescriptor_workload_8163f6de28c2610e) } + +var fileDescriptor_workload_8163f6de28c2610e = []byte{ + // 333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xcd, 0x4e, 0xc2, 0x40, + 0x10, 0xc7, 0xb3, 0x54, 0x08, 0x1d, 0x3e, 0x6c, 0x37, 0x6a, 0x36, 0x78, 0xb0, 0x69, 0x62, 0xec, + 0xa9, 0x21, 0x18, 0x8c, 0x78, 0xf3, 0x8b, 0x84, 0x70, 0x31, 0xc5, 0x88, 0xb7, 0x06, 0xd8, 0x6d, + 0xd8, 0xd0, 0x50, 0xec, 0x2e, 0x20, 0x4f, 0xe4, 0x6b, 0xf9, 0x28, 0x66, 0xfb, 0x65, 0x52, 0xbd, + 0xcd, 0xfc, 0xe7, 0x37, 0x3b, 0xf3, 0xdf, 0x81, 0xf6, 0x3e, 0x8a, 0x57, 0x61, 0x34, 0xa3, 0xee, + 0x26, 0x8e, 0x64, 0x64, 0x9b, 0x70, 0xfc, 0xde, 0xef, 0x0e, 0x26, 0x6f, 0xa3, 0x27, 0x8f, 0x7d, + 0x6c, 0x99, 0x90, 0xf6, 0x37, 0x02, 0xe3, 0x57, 0x13, 0x9b, 0x68, 0x2d, 0x18, 0xbe, 0x80, 0xaa, + 0xd8, 0x71, 0x2a, 0x08, 0xb2, 0x34, 0xa7, 0xd1, 0xd3, 0xdd, 0x82, 0x48, 0x75, 0x6c, 0x80, 0xb6, + 0x88, 0x43, 0x52, 0xb1, 0x34, 0xa7, 0xe9, 0xa9, 0x10, 0xbf, 0x82, 0x19, 0x30, 0xca, 0xe2, 0x99, + 0x64, 0xd4, 0x9f, 0x6f, 0xd7, 0x34, 0x64, 0x82, 0x68, 0x49, 0xfb, 0x95, 0x5b, 0x1e, 0xe0, 0x0e, + 0x73, 0xf4, 0x21, 0x25, 0x9f, 0xd7, 0x32, 0x3e, 0x78, 0x46, 0x50, 0x92, 0x3b, 0x8f, 0x70, 0xfa, + 0x2f, 0xaa, 0x16, 0x58, 0xb1, 0x03, 0x41, 0x16, 0x72, 0x74, 0x4f, 0x85, 0xf8, 0x04, 0xaa, 0xbb, + 0x59, 0xb8, 0x65, 0xa4, 0x62, 0x21, 0xa7, 0xe9, 0xa5, 0xc9, 0x5d, 0xe5, 0x16, 0xd9, 0x5f, 0x08, + 0xea, 0xf9, 0x06, 0xf8, 0x1c, 0x74, 0xb1, 0xe1, 0x41, 0xc0, 0x7c, 0x4e, 0xb3, 0xf6, 0x7a, 0x2a, + 0x8c, 0xa8, 0x2a, 0x7e, 0xf6, 0xbb, 0x03, 0x5f, 0x99, 0xcc, 0xde, 0xa9, 0x2b, 0x61, 0xb2, 0xe3, + 0x14, 0xdb, 0xd0, 0x2a, 0x8a, 0xbe, 0x1a, 0xae, 0x25, 0x40, 0x23, 0x07, 0xc6, 0xec, 0x80, 0xcf, + 0xa0, 0x96, 0x7a, 0x27, 0x47, 0x49, 0x31, 0xcb, 0xf0, 0x25, 0xb4, 0x73, 0x6f, 0xc2, 0xdf, 0x73, + 0xb9, 0x24, 0x55, 0x4b, 0x73, 0x74, 0xaf, 0x55, 0xa8, 0x53, 0x2e, 0x97, 0xbd, 0x31, 0x98, 0x93, + 0x64, 0x97, 0x69, 0x76, 0xb7, 0xfb, 0x97, 0x11, 0xbe, 0x81, 0xd6, 0x90, 0xc9, 0xc5, 0xb2, 0xb0, + 0x60, 0xb8, 0xa5, 0x23, 0x76, 0xcc, 0x3f, 0x3f, 0xdc, 0x45, 0xf3, 0x5a, 0x72, 0xf3, 0xeb, 0x9f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xe3, 0xd1, 0x92, 0x7b, 0x05, 0x02, 0x00, 0x00, } diff --git a/proto/api/workload/workload.proto b/proto/api/workload/workload.proto index b2c9044d37..a661c2cfa3 100644 --- a/proto/api/workload/workload.proto +++ b/proto/api/workload/workload.proto @@ -37,6 +37,9 @@ message X509SVID { // ASN.1 DER encoded bytes bundle = 4; + // List of trust domains the SVID federates with, which corresponds to + // keys in the federated_bundles map in the X509SVIDResponse message. + repeated string federates_with = 5; } service SpiffeWorkloadAPI { diff --git a/test/mock/agent/manager/cache/cache_mock.go b/test/mock/agent/manager/cache/cache_mock.go index 623ad3a29e..8f56bb38ee 100644 --- a/test/mock/agent/manager/cache/cache_mock.go +++ b/test/mock/agent/manager/cache/cache_mock.go @@ -7,7 +7,6 @@ package mock_cache import ( x509 "crypto/x509" gomock "github.com/golang/mock/gomock" - go_observer "github.com/imkira/go-observer" cache "github.com/spiffe/spire/pkg/agent/manager/cache" common "github.com/spiffe/spire/proto/common" reflect "reflect" @@ -84,26 +83,14 @@ func (mr *MockCacheMockRecorder) Entry(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Entry", reflect.TypeOf((*MockCache)(nil).Entry), arg0) } -// IsEmpty mocks base method -func (m *MockCache) IsEmpty() bool { - ret := m.ctrl.Call(m, "IsEmpty") - ret0, _ := ret[0].(bool) - return ret0 -} - -// IsEmpty indicates an expected call of IsEmpty -func (mr *MockCacheMockRecorder) IsEmpty() *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmpty", reflect.TypeOf((*MockCache)(nil).IsEmpty)) -} - -// SetBundle mocks base method -func (m *MockCache) SetBundle(arg0 []*x509.Certificate) { - m.ctrl.Call(m, "SetBundle", arg0) +// SetBundles mocks base method +func (m *MockCache) SetBundles(arg0 map[string][]*x509.Certificate) { + m.ctrl.Call(m, "SetBundles", arg0) } -// SetBundle indicates an expected call of SetBundle -func (mr *MockCacheMockRecorder) SetBundle(arg0 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBundle", reflect.TypeOf((*MockCache)(nil).SetBundle), arg0) +// SetBundles indicates an expected call of SetBundles +func (mr *MockCacheMockRecorder) SetBundles(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBundles", reflect.TypeOf((*MockCache)(nil).SetBundles), arg0) } // SetEntry mocks base method @@ -129,9 +116,9 @@ func (mr *MockCacheMockRecorder) Subscribe(arg0 interface{}) *gomock.Call { } // SubscribeToBundleChanges mocks base method -func (m *MockCache) SubscribeToBundleChanges() go_observer.Stream { +func (m *MockCache) SubscribeToBundleChanges() *cache.BundleStream { ret := m.ctrl.Call(m, "SubscribeToBundleChanges") - ret0, _ := ret[0].(go_observer.Stream) + ret0, _ := ret[0].(*cache.BundleStream) return ret0 } diff --git a/test/mock/agent/manager/manager_mock.go b/test/mock/agent/manager/manager_mock.go index 44037b2853..ae41a7dbd8 100644 --- a/test/mock/agent/manager/manager_mock.go +++ b/test/mock/agent/manager/manager_mock.go @@ -73,9 +73,9 @@ func (mr *MockManagerMockRecorder) Run(arg0 interface{}) *gomock.Call { } // SubscribeToBundleChanges mocks base method -func (m *MockManager) SubscribeToBundleChanges() go_observer.Stream { +func (m *MockManager) SubscribeToBundleChanges() *cache.BundleStream { ret := m.ctrl.Call(m, "SubscribeToBundleChanges") - ret0, _ := ret[0].(go_observer.Stream) + ret0, _ := ret[0].(*cache.BundleStream) return ret0 } diff --git a/test/mock/proto/api/node/node.go b/test/mock/proto/api/node/node.go index 29243b03c6..35350719e6 100644 --- a/test/mock/proto/api/node/node.go +++ b/test/mock/proto/api/node/node.go @@ -54,24 +54,6 @@ func (mr *MockNodeClientMockRecorder) Attest(arg0 interface{}, arg1 ...interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attest", reflect.TypeOf((*MockNodeClient)(nil).Attest), varargs...) } -// FetchFederatedBundle mocks base method -func (m *MockNodeClient) FetchFederatedBundle(arg0 context.Context, arg1 *node.FetchFederatedBundleRequest, arg2 ...grpc.CallOption) (*node.FetchFederatedBundleResponse, error) { - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "FetchFederatedBundle", varargs...) - ret0, _ := ret[0].(*node.FetchFederatedBundleResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchFederatedBundle indicates an expected call of FetchFederatedBundle -func (mr *MockNodeClientMockRecorder) FetchFederatedBundle(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchFederatedBundle", reflect.TypeOf((*MockNodeClient)(nil).FetchFederatedBundle), varargs...) -} - // FetchJWTSVID mocks base method func (m *MockNodeClient) FetchJWTSVID(arg0 context.Context, arg1 *node.FetchJWTSVIDRequest, arg2 ...grpc.CallOption) (*node.FetchJWTSVIDResponse, error) { varargs := []interface{}{arg0, arg1} @@ -503,19 +485,6 @@ func (mr *MockNodeServerMockRecorder) Attest(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attest", reflect.TypeOf((*MockNodeServer)(nil).Attest), arg0) } -// FetchFederatedBundle mocks base method -func (m *MockNodeServer) FetchFederatedBundle(arg0 context.Context, arg1 *node.FetchFederatedBundleRequest) (*node.FetchFederatedBundleResponse, error) { - ret := m.ctrl.Call(m, "FetchFederatedBundle", arg0, arg1) - ret0, _ := ret[0].(*node.FetchFederatedBundleResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchFederatedBundle indicates an expected call of FetchFederatedBundle -func (mr *MockNodeServerMockRecorder) FetchFederatedBundle(arg0, arg1 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchFederatedBundle", reflect.TypeOf((*MockNodeServer)(nil).FetchFederatedBundle), arg0, arg1) -} - // FetchJWTSVID mocks base method func (m *MockNodeServer) FetchJWTSVID(arg0 context.Context, arg1 *node.FetchJWTSVIDRequest) (*node.FetchJWTSVIDResponse, error) { ret := m.ctrl.Call(m, "FetchJWTSVID", arg0, arg1)