From 5005aafff08a7ce7b0623b97ef3ba59a2978a734 Mon Sep 17 00:00:00 2001 From: Andrew Harding Date: Fri, 20 Jul 2018 13:39:38 -0600 Subject: [PATCH] Updates node API to support JWT SVID - Adds JWT methods to node API and renames things to clearly distinguish between JWT and X509 - Updates agent client to support fetching JWT SVIDs - Provides server stub Signed-off-by: Andrew Harding --- pkg/agent/attestor/node/node.go | 2 +- pkg/agent/attestor/node/node_test.go | 12 +- pkg/agent/attestor/workload/workload_test.go | 2 +- pkg/agent/client/client.go | 51 ++- pkg/agent/client/client_test.go | 9 +- pkg/agent/client/update.go | 2 +- pkg/agent/client/update_test.go | 8 +- pkg/agent/manager/manager.go | 4 +- pkg/agent/manager/manager_test.go | 102 +++-- pkg/agent/manager/sync.go | 19 +- pkg/agent/svid/rotator.go | 8 +- pkg/agent/svid/rotator_test.go | 8 +- pkg/server/endpoints/node/handler.go | 38 +- pkg/server/endpoints/node/handler_test.go | 28 +- proto/api/node/README_pb.md | 99 ++++- proto/api/node/node.pb.go | 430 ++++++++++++++----- proto/api/node/node.proto | 51 ++- test/mock/agent/client/client_mock.go | 25 +- test/mock/agent/client/generate.go | 3 + test/mock/proto/api/node/node.go | 31 ++ 20 files changed, 674 insertions(+), 258 deletions(-) create mode 100644 test/mock/agent/client/generate.go diff --git a/pkg/agent/attestor/node/node.go b/pkg/agent/attestor/node/node.go index 4e9df5ed52..a6c096d00d 100644 --- a/pkg/agent/attestor/node/node.go +++ b/pkg/agent/attestor/node/node.go @@ -317,7 +317,7 @@ func (a *attestor) parseAttestationResponse(id string, r *node.AttestResponse) ( return nil, nil, fmt.Errorf("incorrect svid: %s", id) } - svid, err := x509.ParseCertificate(svidMsg.SvidCert) + svid, err := x509.ParseCertificate(svidMsg.Cert) if err != nil { return nil, nil, fmt.Errorf("invalid svid: %v", err) } diff --git a/pkg/agent/attestor/node/node_test.go b/pkg/agent/attestor/node/node_test.go index 38073df72c..2f688d3d4b 100644 --- a/pkg/agent/attestor/node/node_test.go +++ b/pkg/agent/attestor/node/node_test.go @@ -44,7 +44,7 @@ type NodeAttestorTestSuite struct { keyManager *mock_keymanager.MockKeyManager nodeClient *mock_node.MockNodeClient config *Config - expectation *node.SvidUpdate + expectation *node.X509SVIDUpdate } func (s *NodeAttestorTestSuite) SetupTest() { @@ -247,11 +247,11 @@ func (s *NodeAttestorTestSuite) setAttestResponse(challenges []challengeResponse }, nil) } stream.EXPECT().Recv().Return(&node.AttestResponse{ - SvidUpdate: &node.SvidUpdate{ - Svids: map[string]*node.Svid{ - "spiffe://example.com/spire/agent/join_token/foobar": &node.Svid{ - SvidCert: svid.Raw, - Ttl: 300, + SvidUpdate: &node.X509SVIDUpdate{ + Svids: map[string]*node.X509SVID{ + "spiffe://example.com/spire/agent/join_token/foobar": &node.X509SVID{ + Cert: svid.Raw, + ExpiresAt: svid.NotAfter.Unix(), }}, }}, nil) stream.EXPECT().CloseSend() diff --git a/pkg/agent/attestor/workload/workload_test.go b/pkg/agent/attestor/workload/workload_test.go index 50bc47765c..e47e55ae1a 100644 --- a/pkg/agent/attestor/workload/workload_test.go +++ b/pkg/agent/attestor/workload/workload_test.go @@ -33,7 +33,7 @@ type WorkloadAttestorTestSuite struct { ctrl *gomock.Controller attestor *attestor - expectation *node.SvidUpdate + expectation *node.X509SVIDUpdate attestor1 *mock_workloadattestor.MockWorkloadAttestor attestor2 *mock_workloadattestor.MockWorkloadAttestor } diff --git a/pkg/agent/client/client.go b/pkg/agent/client/client.go index 1faf5e0881..49d51b6897 100644 --- a/pkg/agent/client/client.go +++ b/pkg/agent/client/client.go @@ -28,8 +28,14 @@ var ( ErrUnableToGetStream = errors.New("unable to get a stream") ) +type JWTSVID struct { + Token string + ExpiresAt time.Time +} + type Client interface { - FetchUpdates(req *node.FetchX509SVIDRequest) (*Update, error) + FetchUpdates(ctx context.Context, req *node.FetchX509SVIDRequest) (*Update, error) + FetchJWTSVID(ctx context.Context, jsr *node.JSR) (*JWTSVID, error) // Release releases any resources that were held by this Client, if any. Release() @@ -74,8 +80,8 @@ func (c *client) credsFunc() (credentials.TransportCredentials, error) { return credentials.NewTLS(tlsConfig), nil } -func (c *client) dial() (*grpc.ClientConn, error) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // TODO: Make this timeout configurable? +func (c *client) dial(ctx context.Context) (*grpc.ClientConn, error) { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) // TODO: Make this timeout configurable? defer cancel() config := grpcutil.GRPCDialerConfig{ @@ -90,13 +96,13 @@ func (c *client) dial() (*grpc.ClientConn, error) { return conn, nil } -func (c *client) FetchUpdates(req *node.FetchX509SVIDRequest) (*Update, error) { - nodeClient, err := c.newNodeClient() +func (c *client) FetchUpdates(ctx context.Context, req *node.FetchX509SVIDRequest) (*Update, error) { + nodeClient, err := c.newNodeClient(ctx) if err != nil { return nil, err } - stream, err := nodeClient.FetchX509SVID(context.Background()) + stream, err := nodeClient.FetchX509SVID(ctx) // We weren't able to get a stream...close the client and return the error. if err != nil { c.Release() @@ -114,7 +120,7 @@ func (c *client) FetchUpdates(req *node.FetchX509SVIDRequest) (*Update, error) { } regEntries := map[string]*common.RegistrationEntry{} - svids := map[string]*node.Svid{} + svids := map[string]*node.X509SVID{} var lastBundle []byte // Read all the server responses from the stream. for { @@ -142,6 +148,33 @@ func (c *client) FetchUpdates(req *node.FetchX509SVIDRequest) (*Update, error) { }, nil } +func (c *client) FetchJWTSVID(ctx context.Context, jsr *node.JSR) (*JWTSVID, error) { + nodeClient, err := c.newNodeClient(ctx) + if err != nil { + return nil, err + } + + response, err := nodeClient.FetchJWTSVID(ctx, &node.FetchJWTSVIDRequest{ + Jsr: jsr, + }) + // We weren't able to make the request...close the client and return the error. + if err != nil { + c.Release() + c.c.Log.Errorf("%v: %v", ErrUnableToGetStream, err) + return nil, ErrUnableToGetStream + } + + svid := response.GetSvid() + if svid == nil { + return nil, errors.New("JWTSVID response missing SVID") + } + + return &JWTSVID{ + Token: svid.Token, + ExpiresAt: time.Unix(svid.ExpiresAt, 0), + }, nil +} + func (c *client) Release() { c.m.Lock() defer c.m.Unlock() @@ -152,7 +185,7 @@ func (c *client) Release() { } } -func (c *client) newNodeClient() (node.NodeClient, error) { +func (c *client) newNodeClient(ctx context.Context) (node.NodeClient, error) { if c.newNodeClientCallback != nil { return c.newNodeClientCallback() } @@ -161,7 +194,7 @@ func (c *client) newNodeClient() (node.NodeClient, error) { defer c.m.Unlock() if c.conn == nil { - conn, err := c.dial() + conn, err := c.dial(ctx) if err != nil { return nil, err } diff --git a/pkg/agent/client/client_test.go b/pkg/agent/client/client_test.go index 0b355b3ab1..1790e65c43 100644 --- a/pkg/agent/client/client_test.go +++ b/pkg/agent/client/client_test.go @@ -1,6 +1,7 @@ package client import ( + "context" "io" "testing" @@ -34,14 +35,14 @@ func TestFetchUpdates(t *testing.T) { Csrs: [][]byte{{1, 2, 3, 4}}, } res := &node.FetchX509SVIDResponse{ - SvidUpdate: &node.SvidUpdate{ + SvidUpdate: &node.X509SVIDUpdate{ Bundle: []byte{10, 20, 30, 40}, RegistrationEntries: []*common.RegistrationEntry{{ EntryId: "1", }}, - Svids: map[string]*node.Svid{ + Svids: map[string]*node.X509SVID{ "someSpiffeId": { - SvidCert: []byte{11, 22, 33}, + Cert: []byte{11, 22, 33}, }, }, }, @@ -53,7 +54,7 @@ func TestFetchUpdates(t *testing.T) { nodeFsc.EXPECT().Recv().Return(res, nil) nodeFsc.EXPECT().Recv().Return(nil, io.EOF) - update, err := client.FetchUpdates(req) + update, err := client.FetchUpdates(context.Background(), req) require.Nil(t, err) assert.Equal(t, res.SvidUpdate.Bundle, update.Bundle) diff --git a/pkg/agent/client/update.go b/pkg/agent/client/update.go index 86de41d68a..69b8af86b2 100644 --- a/pkg/agent/client/update.go +++ b/pkg/agent/client/update.go @@ -10,7 +10,7 @@ import ( type Update struct { Entries map[string]*common.RegistrationEntry - SVIDs map[string]*node.Svid + SVIDs map[string]*node.X509SVID Bundle []byte } diff --git a/pkg/agent/client/update_test.go b/pkg/agent/client/update_test.go index 318801817e..f93040ccf8 100644 --- a/pkg/agent/client/update_test.go +++ b/pkg/agent/client/update_test.go @@ -17,15 +17,15 @@ func TestString(t *testing.T) { u := &Update{ Bundle: []byte{1, 2, 3}, Entries: map[string]*common.RegistrationEntry{entries[0].EntryId: entries[0]}, - SVIDs: map[string]*node.Svid{ + SVIDs: map[string]*node.X509SVID{ "spiffe://example.org": { - SvidCert: []byte{4, 5}, - Ttl: 5, + Cert: []byte{4, 5}, + ExpiresAt: 5, }, }, } - 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: svid_cert:\"\\004\\005\" ttl: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 ], Bundle: bytes}" if u.String() != expected { t.Errorf("expected: %s, got: %s", expected, u.String()) } diff --git a/pkg/agent/manager/manager.go b/pkg/agent/manager/manager.go index 7f6b15af70..71f01b5b37 100644 --- a/pkg/agent/manager/manager.go +++ b/pkg/agent/manager/manager.go @@ -71,7 +71,7 @@ func (m *manager) Initialize(ctx context.Context) error { m.storeSVID(m.svid.State().SVID) m.storeBundle(m.cache.Bundle()) - return m.synchronize() + return m.synchronize(ctx) } func (m *manager) Run(ctx context.Context) error { @@ -120,7 +120,7 @@ func (m *manager) runSynchronizer(ctx context.Context) error { for { select { case <-t.C: - err := m.synchronize() + err := m.synchronize(ctx) if err != nil { // Just log the error to keep waiting for next sinchronization... m.c.Log.Errorf("synchronize failed: %v", err) diff --git a/pkg/agent/manager/manager_test.go b/pkg/agent/manager/manager_test.go index f3193acc99..1d0f489acd 100644 --- a/pkg/agent/manager/manager_test.go +++ b/pkg/agent/manager/manager_test.go @@ -156,11 +156,11 @@ func TestHappyPathWithoutSyncNorRotation(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponseForTestHappyPathWithoutSyncNorRotation, - svidTTL: 200, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVIDForTestHappyPathWithoutSyncNorRotation, + svidTTL: 200, }) apiHandler.start() defer apiHandler.stop() @@ -233,11 +233,11 @@ func TestSVIDRotation(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponse, - svidTTL: 3, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVID, + svidTTL: 3, }) apiHandler.start() defer apiHandler.stop() @@ -302,11 +302,11 @@ func TestSynchronization(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponse, - svidTTL: 3, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVID, + svidTTL: 3, }) apiHandler.start() defer apiHandler.stop() @@ -426,11 +426,11 @@ func TestSynchronizationClearsStaleCacheEntries(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponseForStaleCacheTest, - svidTTL: 3, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVIDForStaleCacheTest, + svidTTL: 3, }) apiHandler.start() defer apiHandler.stop() @@ -465,7 +465,7 @@ func TestSynchronizationClearsStaleCacheEntries(t *testing.T) { regEntriesFromCacheEntries(m.cache.Entries())) // manually synchronize again - if err := m.synchronize(); err != nil { + if err := m.synchronize(context.Background()); err != nil { t.Fatal(err) } @@ -482,11 +482,11 @@ func TestSubscribersGetUpToDateBundle(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponseForTestSubscribersGetUpToDateBundle, - svidTTL: 200, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVIDForTestSubscribersGetUpToDateBundle, + svidTTL: 200, }) apiHandler.start() defer apiHandler.stop() @@ -538,10 +538,10 @@ func TestSurvivesCARotation(t *testing.T) { trustDomain := "example.org" apiHandler := newMockNodeAPIHandler(&mockNodeAPIHandlerConfig{ - t: t, - trustDomain: trustDomain, - dir: dir, - fetchSVIDResponse: fetchSVIDResponseForTestSurvivesCARotation, + t: t, + trustDomain: trustDomain, + dir: dir, + fetchX509SVID: fetchX509SVIDForTestSurvivesCARotation, // Give a low ttl to get expired entries on each synchronization, forcing // the manager to fetch entries from the server. svidTTL: 3, @@ -595,7 +595,7 @@ func TestSurvivesCARotation(t *testing.T) { } } -func fetchSVIDResponseForTestHappyPathWithoutSyncNorRotation(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { +func fetchX509SVIDForTestHappyPathWithoutSyncNorRotation(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { switch h.reqCount { case 1: if len(req.Csrs) != 0 { @@ -622,7 +622,7 @@ func fetchSVIDResponseForTestHappyPathWithoutSyncNorRotation(h *mockNodeAPIHandl } } -func fetchSVIDResponse(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { +func fetchX509SVID(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { svid, err := h.getCertFromCtx(stream.Context()) if err != nil { return fmt.Errorf("cannot get SVID from stream context: %v. reqCount: %d", err, h.reqCount) @@ -651,7 +651,7 @@ func fetchSVIDResponse(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, st return stream.Send(newFetchX509SVIDResponse(resps, svids, h.bundle)) } -func fetchSVIDResponseForStaleCacheTest(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { +func fetchX509SVIDForStaleCacheTest(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { svids, err := h.makeSvids(req.Csrs) if err != nil { return err @@ -670,17 +670,17 @@ func fetchSVIDResponseForStaleCacheTest(h *mockNodeAPIHandler, req *node.FetchX5 return stream.Send(newFetchX509SVIDResponse(nil, nil, h.bundle)) } -func fetchSVIDResponseForTestSubscribersGetUpToDateBundle(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { +func fetchX509SVIDForTestSubscribersGetUpToDateBundle(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { switch h.reqCount { case 2: ca, _ := createCA(h.c.t, h.c.trustDomain) h.bundle = append(h.bundle, ca) } - return fetchSVIDResponse(h, req, stream) + return fetchX509SVID(h, req, stream) } -func fetchSVIDResponseForTestSurvivesCARotation(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { +func fetchX509SVIDForTestSurvivesCARotation(h *mockNodeAPIHandler, req *node.FetchX509SVIDRequest, stream node.Node_FetchX509SVIDServer) error { switch h.reqCount { case 2: ca, key := createCA(h.c.t, h.c.trustDomain) @@ -693,7 +693,7 @@ func fetchSVIDResponseForTestSurvivesCARotation(h *mockNodeAPIHandler, req *node return fmt.Errorf("server was restarted") } - return fetchSVIDResponse(h, req, stream) + return fetchX509SVID(h, req, stream) } func newFetchX509SVIDResponse(regEntriesKeys []string, svids svidMap, bundle []*x509.Certificate) *node.FetchX509SVIDResponse { @@ -710,7 +710,7 @@ func newFetchX509SVIDResponse(regEntriesKeys []string, svids svidMap, bundle []* } return &node.FetchX509SVIDResponse{ - SvidUpdate: &node.SvidUpdate{ + SvidUpdate: &node.X509SVIDUpdate{ RegistrationEntries: regEntries, Svids: svids, Bundle: bundleBytes.Bytes(), @@ -761,15 +761,18 @@ func compareRegistrationEntries(t *testing.T, expected, actual []*common.Registr } } -type svidMap map[string]*node.Svid +type svidMap map[string]*node.X509SVID type mockNodeAPIHandlerConfig struct { t *testing.T trustDomain string + // Directory used to save server related files, like unix sockets files. dir string - // Callback used to build the response according to the request and state of mockNodeAPIHandler. - fetchSVIDResponse func(*mockNodeAPIHandler, *node.FetchX509SVIDRequest, node.Node_FetchX509SVIDServer) error + + // Callbacks used to build the response according to the request and state of mockNodeAPIHandler. + fetchX509SVID func(*mockNodeAPIHandler, *node.FetchX509SVIDRequest, node.Node_FetchX509SVIDServer) error + fetchJWTSVID func(*mockNodeAPIHandler, *node.FetchJWTSVIDRequest) (*node.FetchJWTSVIDResponse, error) svidTTL int } @@ -824,7 +827,10 @@ func (h *mockNodeAPIHandler) makeSvids(csrs [][]byte) (svidMap, error) { if err != nil { return nil, fmt.Errorf("cannot get spiffeID from SVID: %v. reqCount: %d", err, h.reqCount) } - svids[spiffeID] = &node.Svid{SvidCert: svid.Raw, Ttl: int32(h.c.svidTTL)} + svids[spiffeID] = &node.X509SVID{ + Cert: svid.Raw, + ExpiresAt: svid.NotAfter.Unix(), + } } return svids, nil } @@ -845,12 +851,20 @@ func (h *mockNodeAPIHandler) FetchX509SVID(stream node.Node_FetchX509SVIDServer) if err != nil { return err } - if h.c.fetchSVIDResponse != nil { - return h.c.fetchSVIDResponse(h, req, stream) + if h.c.fetchX509SVID != nil { + return h.c.fetchX509SVID(h, req, stream) } return nil } +func (h *mockNodeAPIHandler) FetchJWTSVID(ctx context.Context, req *node.FetchJWTSVIDRequest) (*node.FetchJWTSVIDResponse, error) { + h.countRequest() + if h.c.fetchJWTSVID != nil { + return h.c.fetchJWTSVID(h, req) + } + 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 diff --git a/pkg/agent/manager/sync.go b/pkg/agent/manager/sync.go index ee33ccdfa0..847f21cfcc 100644 --- a/pkg/agent/manager/sync.go +++ b/pkg/agent/manager/sync.go @@ -1,6 +1,7 @@ package manager import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -15,11 +16,11 @@ import ( ) // synchronize hits the node api, checks for entries we haven't fetched yet, and fetches them. -func (m *manager) synchronize() (err error) { +func (m *manager) synchronize(ctx context.Context) (err error) { var regEntries map[string]*proto.RegistrationEntry var cEntryRequests = entryRequests{} - regEntries, _, err = m.fetchUpdates(nil) + regEntries, _, err = m.fetchUpdates(ctx, nil) if err != nil { return err } @@ -36,7 +37,7 @@ func (m *manager) synchronize() (err error) { return err } - err = m.processEntryRequests(cEntryRequests) + err = m.processEntryRequests(ctx, cEntryRequests) if err != nil { return err } @@ -44,7 +45,7 @@ func (m *manager) synchronize() (err error) { return nil } -func (m *manager) fetchUpdates(entryRequests map[string]*entryRequest) (map[string]*common.RegistrationEntry, map[string]*node.Svid, error) { +func (m *manager) fetchUpdates(ctx context.Context, entryRequests map[string]*entryRequest) (map[string]*common.RegistrationEntry, map[string]*node.X509SVID, error) { // Put all the CSRs in an array to make just one call with all the CSRs. csrs := [][]byte{} if entryRequests != nil { @@ -54,7 +55,7 @@ func (m *manager) fetchUpdates(entryRequests map[string]*entryRequest) (map[stri } } - update, err := m.client.FetchUpdates(&node.FetchX509SVIDRequest{Csrs: csrs}) + update, err := m.client.FetchUpdates(ctx, &node.FetchX509SVIDRequest{Csrs: csrs}) if err != nil { return nil, nil, err } @@ -73,12 +74,12 @@ func (m *manager) fetchUpdates(entryRequests map[string]*entryRequest) (map[stri return update.Entries, update.SVIDs, nil } -func (m *manager) processEntryRequests(entryRequests entryRequests) error { +func (m *manager) processEntryRequests(ctx context.Context, entryRequests entryRequests) error { if len(entryRequests) == 0 { return nil } - _, svids, err := m.fetchUpdates(entryRequests) + _, svids, err := m.fetchUpdates(ctx, entryRequests) if err != nil { return err } @@ -90,12 +91,12 @@ func (m *manager) processEntryRequests(entryRequests entryRequests) error { return nil } -func (m *manager) updateEntriesSVIDs(entryRequestsMap map[string]*entryRequest, svids map[string]*node.Svid) error { +func (m *manager) updateEntriesSVIDs(entryRequestsMap map[string]*entryRequest, svids map[string]*node.X509SVID) error { for _, entryRequest := range entryRequestsMap { ce := entryRequest.entry svid, ok := svids[ce.RegistrationEntry.SpiffeId] if ok { - cert, err := x509.ParseCertificate(svid.SvidCert) + cert, err := x509.ParseCertificate(svid.Cert) if err != nil { return err } diff --git a/pkg/agent/svid/rotator.go b/pkg/agent/svid/rotator.go index 30c546d852..84cd3fd5eb 100644 --- a/pkg/agent/svid/rotator.go +++ b/pkg/agent/svid/rotator.go @@ -52,7 +52,7 @@ func (r *rotator) Run(ctx context.Context) error { return nil case <-t.C: if r.shouldRotate() { - if err := r.rotateSVID(); err != nil { + if err := r.rotateSVID(ctx); err != nil { r.c.Log.Errorf("Could not rotate agent SVID: %v", err) } } @@ -84,7 +84,7 @@ func (r *rotator) shouldRotate() bool { } // rotateSVID asks SPIRE's server for a new agent's SVID. -func (r *rotator) rotateSVID() error { +func (r *rotator) rotateSVID(ctx context.Context) error { r.c.Log.Debug("Rotating agent SVID") key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -97,7 +97,7 @@ func (r *rotator) rotateSVID() error { return err } - update, err := r.client.FetchUpdates(&node.FetchX509SVIDRequest{Csrs: [][]byte{csr}}) + update, err := r.client.FetchUpdates(ctx, &node.FetchX509SVIDRequest{Csrs: [][]byte{csr}}) if err != nil { return err } @@ -110,7 +110,7 @@ func (r *rotator) rotateSVID() error { if !ok { return errors.New("it was not possible to get agent SVID from FetchX509SVID response") } - cert, err := x509.ParseCertificate(svid.SvidCert) + cert, err := x509.ParseCertificate(svid.Cert) if err != nil { return err } diff --git a/pkg/agent/svid/rotator_test.go b/pkg/agent/svid/rotator_test.go index cded65818b..a6b9b97b85 100644 --- a/pkg/agent/svid/rotator_test.go +++ b/pkg/agent/svid/rotator_test.go @@ -168,7 +168,7 @@ func (s *RotatorTestSuite) TestRotateSVID() { stream := s.r.Subscribe() s.expectSVIDRotation(cert) - err = s.r.rotateSVID() + err = s.r.rotateSVID(context.Background()) s.Assert().NoError(err) s.Require().True(stream.HasNext()) @@ -180,11 +180,11 @@ func (s *RotatorTestSuite) TestRotateSVID() { // the the provided certificate to the client.Client caller. func (s *RotatorTestSuite) expectSVIDRotation(cert *x509.Certificate) { s.client.EXPECT(). - FetchUpdates(gomock.Any()). + FetchUpdates(gomock.Any(), gomock.Any()). Return(&client.Update{ - SVIDs: map[string]*node.Svid{ + SVIDs: map[string]*node.X509SVID{ s.r.c.SpiffeID: { - SvidCert: cert.Raw, + Cert: cert.Raw, }, }, }, nil) diff --git a/pkg/server/endpoints/node/handler.go b/pkg/server/endpoints/node/handler.go index 43247c36ed..c5e0d318c9 100644 --- a/pkg/server/endpoints/node/handler.go +++ b/pkg/server/endpoints/node/handler.go @@ -206,7 +206,7 @@ func (h *Handler) FetchX509SVID(server node.Node_FetchX509SVIDServer) (err error } err = server.Send(&node.FetchX509SVIDResponse{ - SvidUpdate: &node.SvidUpdate{ + SvidUpdate: &node.X509SVIDUpdate{ Svids: svids, Bundle: bundle, RegistrationEntries: regEntries, @@ -218,6 +218,10 @@ func (h *Handler) FetchX509SVID(server node.Node_FetchX509SVIDServer) (err error } } +func (h *Handler) FetchJWTSVID(ctx context.Context, req *node.FetchJWTSVIDRequest) (*node.FetchJWTSVIDResponse, error) { + return nil, errors.New("not implemented") +} + //TODO func (h *Handler) FetchFederatedBundle( ctx context.Context, request *node.FetchFederatedBundleRequest) ( @@ -443,12 +447,8 @@ func (h *Handler) getAttestResponse(ctx context.Context, return &node.AttestResponse{}, err } - svids := make(map[string]*node.Svid) - svids[baseSpiffeID] = &node.Svid{ - SvidCert: cert.Raw, - Ttl: int32(h.timeUntil(cert.NotAfter).Seconds()), - } - + svids := make(map[string]*node.X509SVID) + svids[baseSpiffeID] = makeX509SVID(cert) regEntries, err := regentryutil.FetchRegistrationEntries(ctx, h.c.Catalog.DataStores()[0], baseSpiffeID) if err != nil { return nil, err @@ -459,7 +459,7 @@ func (h *Handler) getAttestResponse(ctx context.Context, return nil, err } - svidUpdate := &node.SvidUpdate{ + svidUpdate := &node.X509SVIDUpdate{ Svids: svids, Bundle: bundle, RegistrationEntries: regEntries, @@ -487,7 +487,7 @@ func (h *Handler) getCertFromCtx(ctx context.Context) (certificate *x509.Certifi func (h *Handler) signCSRs(ctx context.Context, peerCert *x509.Certificate, csrs [][]byte, regEntries []*common.RegistrationEntry) ( - svids map[string]*node.Svid, err error) { + svids map[string]*node.X509SVID, err error) { uriNames, err := uri.GetURINamesFromCertificate(peerCert) if err != nil { @@ -503,7 +503,7 @@ func (h *Handler) signCSRs(ctx context.Context, } dataStore := h.c.Catalog.DataStores()[0] - svids = make(map[string]*node.Svid) + svids = make(map[string]*node.X509SVID) //iterate the CSRs and sign them for _, csr := range csrs { spiffeID, err := getSpiffeIDFromCSR(csr) @@ -552,7 +552,7 @@ func (h *Handler) signCSRs(ctx context.Context, func (h *Handler) buildSVID(ctx context.Context, spiffeID string, regEntries map[string]*common.RegistrationEntry, csr []byte) ( - *node.Svid, error) { + *node.X509SVID, error) { //TODO: Validate that other fields are not populated https://github.com/spiffe/spire/issues/161 //validate that is present in the registration entries, otherwise we shouldn't sign @@ -566,19 +566,16 @@ func (h *Handler) buildSVID(ctx context.Context, if err != nil { return nil, err } - return &node.Svid{SvidCert: cert.Raw, Ttl: entry.Ttl}, nil + return makeX509SVID(cert), nil } -func (h *Handler) buildBaseSVID(ctx context.Context, csr []byte) (*node.Svid, *x509.Certificate, error) { +func (h *Handler) buildBaseSVID(ctx context.Context, csr []byte) (*node.X509SVID, *x509.Certificate, error) { cert, err := h.c.ServerCA.SignX509SVID(ctx, csr, 0) if err != nil { return nil, nil, err } - return &node.Svid{ - SvidCert: cert.Raw, - Ttl: int32(h.timeUntil(cert.NotAfter).Seconds()), - }, cert, nil + return makeX509SVID(cert), cert, nil } // getBundle fetches the current CA bundle from the datastore. @@ -618,3 +615,10 @@ func getSpiffeIDFromCSR(csr []byte) (spiffeID string, err error) { return spiffeID, nil } + +func makeX509SVID(cert *x509.Certificate) *node.X509SVID { + return &node.X509SVID{ + Cert: cert.Raw, + ExpiresAt: cert.NotAfter.Unix(), + } +} diff --git a/pkg/server/endpoints/node/handler_test.go b/pkg/server/endpoints/node/handler_test.go index 3a5fb96610..e849ce3b64 100644 --- a/pkg/server/endpoints/node/handler_test.go +++ b/pkg/server/endpoints/node/handler_test.go @@ -166,10 +166,9 @@ func TestFetchX509SVIDWithRotation(t *testing.T) { // Calculate expected TTL cert := data.generatedCerts[3] - ttl := int32(cert.NotAfter.Sub(suite.now).Seconds()) data.expectation = getExpectedFetchX509SVID(data) - data.expectation.Svids[data.baseSpiffeID] = &node.Svid{SvidCert: cert.Raw, Ttl: ttl} + data.expectation.Svids[data.baseSpiffeID] = makeX509SVID(cert) setFetchX509SVIDExpectations(suite, data) suite.mockDataStore.EXPECT().FetchAttestedNodeEntry(gomock.Any(), @@ -428,7 +427,7 @@ func setAttestExpectations( CaCerts: caCert.Raw}, nil) } -func getExpectedAttest(suite *HandlerTestSuite, baseSpiffeID string, cert *x509.Certificate) *node.SvidUpdate { +func getExpectedAttest(suite *HandlerTestSuite, baseSpiffeID string, cert *x509.Certificate) *node.X509SVIDUpdate { expectedRegEntries := []*common.RegistrationEntry{ { Selectors: []*common.Selector{ @@ -453,14 +452,11 @@ func getExpectedAttest(suite *HandlerTestSuite, baseSpiffeID string, cert *x509. }, } - // Calculate expected TTL - ttl := int32(cert.NotAfter.Sub(suite.now).Seconds()) - - svids := make(map[string]*node.Svid) - svids[baseSpiffeID] = &node.Svid{SvidCert: cert.Raw, Ttl: ttl} + svids := make(map[string]*node.X509SVID) + svids[baseSpiffeID] = makeX509SVID(cert) caCert, _, _ := util.LoadCAFixture() - svidUpdate := &node.SvidUpdate{ + svidUpdate := &node.X509SVIDUpdate{ Svids: svids, Bundle: caCert.Raw, RegistrationEntries: expectedRegEntries, @@ -481,7 +477,7 @@ type fetchSVIDData struct { nodeResolutionList []*datastore.NodeResolverMapEntry bySelectorsEntries []*common.RegistrationEntry byParentIDEntries []*common.RegistrationEntry - expectation *node.SvidUpdate + expectation *node.X509SVIDUpdate } func getFetchX509SVIDTestData() *fetchSVIDData { @@ -605,12 +601,12 @@ func setFetchX509SVIDExpectations( } -func getExpectedFetchX509SVID(data *fetchSVIDData) *node.SvidUpdate { +func getExpectedFetchX509SVID(data *fetchSVIDData) *node.X509SVIDUpdate { //TODO: improve this, put it in an array in data and iterate it - svids := map[string]*node.Svid{ - data.nodeSpiffeID: {SvidCert: data.generatedCerts[0].Raw, Ttl: 4444}, - data.databaseSpiffeID: {SvidCert: data.generatedCerts[1].Raw, Ttl: 2222}, - data.blogSpiffeID: {SvidCert: data.generatedCerts[2].Raw, Ttl: 3333}, + svids := map[string]*node.X509SVID{ + data.nodeSpiffeID: makeX509SVID(data.generatedCerts[0]), + data.databaseSpiffeID: makeX509SVID(data.generatedCerts[1]), + data.blogSpiffeID: makeX509SVID(data.generatedCerts[2]), } // returned in sorted order (according to sorting rules in util.SortRegistrationEntries) @@ -622,7 +618,7 @@ func getExpectedFetchX509SVID(data *fetchSVIDData) *node.SvidUpdate { } caCert, _, _ := util.LoadCAFixture() - svidUpdate := &node.SvidUpdate{ + svidUpdate := &node.X509SVIDUpdate{ Svids: svids, Bundle: caCert.Raw, RegistrationEntries: registrationEntries, diff --git a/proto/api/node/README_pb.md b/proto/api/node/README_pb.md index d1ac67e02b..3d58a2d3a3 100644 --- a/proto/api/node/README_pb.md +++ b/proto/api/node/README_pb.md @@ -21,11 +21,15 @@ - [FetchFederatedBundleRequest](#spire.api.node.FetchFederatedBundleRequest) - [FetchFederatedBundleResponse](#spire.api.node.FetchFederatedBundleResponse) - [FetchFederatedBundleResponse.FederatedBundlesEntry](#spire.api.node.FetchFederatedBundleResponse.FederatedBundlesEntry) + - [FetchJWTSVIDRequest](#spire.api.node.FetchJWTSVIDRequest) + - [FetchJWTSVIDResponse](#spire.api.node.FetchJWTSVIDResponse) - [FetchX509SVIDRequest](#spire.api.node.FetchX509SVIDRequest) - [FetchX509SVIDResponse](#spire.api.node.FetchX509SVIDResponse) - - [Svid](#spire.api.node.Svid) - - [SvidUpdate](#spire.api.node.SvidUpdate) - - [SvidUpdate.SvidsEntry](#spire.api.node.SvidUpdate.SvidsEntry) + - [JSR](#spire.api.node.JSR) + - [JWTSVID](#spire.api.node.JWTSVID) + - [X509SVID](#spire.api.node.X509SVID) + - [X509SVIDUpdate](#spire.api.node.X509SVIDUpdate) + - [X509SVIDUpdate.SvidsEntry](#spire.api.node.X509SVIDUpdate.SvidsEntry) @@ -179,8 +183,8 @@ all current Registration Entries which are relevant to the caller SPIFFE ID | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| svid_update | [SvidUpdate](#spire.api.node.SvidUpdate) | | It includes a map of signed SVIDs and an array of all current Registration Entries which are relevant to the caller SPIFFE ID. | -| challenge | [bytes](#bytes) | | This is a challenge issued by the server to the node. If populated, the node is expected to respond with another AttestRequest with the response. This field is mutually exclusive with the svid_update field. | +| svid_update | [X509SVIDUpdate](#spire.api.node.X509SVIDUpdate) | | It includes a map of signed SVIDs and an array of all current Registration Entries which are relevant to the caller SPIFFE ID. | +| challenge | [bytes](#bytes) | | This is a challenge issued by the server to the node. If populated, the node is expected to respond with another AttestRequest with the response. This field is mutually exclusive with the update field. | @@ -233,6 +237,36 @@ Represents a response with a map of SPIFFE Id, Federated CA Bundle. + + +### FetchJWTSVIDRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| jsr | [JSR](#spire.api.node.JSR) | | The JWT signing request | + + + + + + + + +### FetchJWTSVIDResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| svid | [JWTSVID](#spire.api.node.JWTSVID) | | The signed JWT-SVID | + + + + + + ### FetchX509SVIDRequest @@ -257,40 +291,72 @@ of all current Registration Entries which are relevant to the caller SPIFFE ID. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| svid_update | [SvidUpdate](#spire.api.node.SvidUpdate) | | It includes a map of signed SVIDs and an array of all current Registration Entries which are relevant to the caller SPIFFE ID. | +| svid_update | [X509SVIDUpdate](#spire.api.node.X509SVIDUpdate) | | It includes a map of signed SVIDs and an array of all current Registration Entries which are relevant to the caller SPIFFE ID. | + + + + + + + + +### JSR +JSR is a JWT SVID signing request. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| spiffe_id | [string](#string) | | SPIFFE ID of the workload | +| audience | [string](#string) | repeated | List of intended audience | + + + + + + + + +### JWTSVID +JWTSVID is a signed JWT-SVID with fields lifted out for convenience. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| token | [string](#string) | | JWT-SVID JWT token | +| expires_at | [int64](#int64) | | SVID expiration timestamp (seconds since Unix epoch) | - + -### Svid +### X509SVID A type which contains the "Spiffe Verifiable Identity Document" and a TTL indicating when the SVID expires. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| svid_cert | [bytes](#bytes) | | Spiffe Verifiable Identity Document. | -| ttl | [int32](#int32) | | SVID expiration. | +| cert | [bytes](#bytes) | | X509 SVID (ASN.1 encoding) | +| expires_at | [int64](#int64) | | SVID expiration timestamp (in seconds since Unix epoch) | - + -### SvidUpdate +### X509SVIDUpdate A message returned by the Spire Server, which includes a map of signed SVIDs and a list of all current Registration Entries which are relevant to the caller SPIFFE ID. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| svids | [SvidUpdate.SvidsEntry](#spire.api.node.SvidUpdate.SvidsEntry) | repeated | A map containing SVID values and corresponding SPIFFE IDs as the keys. Map[SPIFFE_ID] => SVID. | +| 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 | | 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. | @@ -299,16 +365,16 @@ a list of all current Registration Entries which are relevant to the caller SPIF - + -### SvidUpdate.SvidsEntry +### X509SVIDUpdate.SvidsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | -| value | [Svid](#spire.api.node.Svid) | | | +| value | [X509SVID](#spire.api.node.X509SVID) | | | @@ -330,6 +396,7 @@ 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 3bb06ad544..7e93f6d2d8 100644 --- a/proto/api/node/node.pb.go +++ b/proto/api/node/node.pb.go @@ -44,60 +44,60 @@ type RegistrationEntries = common.RegistrationEntries // A type which contains the "Spiffe Verifiable Identity Document" and // a TTL indicating when the SVID expires. -type Svid struct { - // Spiffe Verifiable Identity Document. - SvidCert []byte `protobuf:"bytes,1,opt,name=svid_cert,json=svidCert,proto3" json:"svid_cert,omitempty"` - // SVID expiration. - Ttl int32 `protobuf:"varint,2,opt,name=ttl" json:"ttl,omitempty"` +type X509SVID struct { + // X509 SVID (ASN.1 encoding) + Cert []byte `protobuf:"bytes,1,opt,name=cert,proto3" json:"cert,omitempty"` + // SVID expiration timestamp (in seconds since Unix epoch) + ExpiresAt int64 `protobuf:"varint,2,opt,name=expires_at,json=expiresAt" json:"expires_at,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *Svid) Reset() { *m = Svid{} } -func (m *Svid) String() string { return proto.CompactTextString(m) } -func (*Svid) ProtoMessage() {} -func (*Svid) Descriptor() ([]byte, []int) { - return fileDescriptor_node_4be23de3db683a67, []int{0} +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_13be1b7ed7e739ea, []int{0} } -func (m *Svid) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Svid.Unmarshal(m, b) +func (m *X509SVID) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_X509SVID.Unmarshal(m, b) } -func (m *Svid) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Svid.Marshal(b, m, deterministic) +func (m *X509SVID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_X509SVID.Marshal(b, m, deterministic) } -func (dst *Svid) XXX_Merge(src proto.Message) { - xxx_messageInfo_Svid.Merge(dst, src) +func (dst *X509SVID) XXX_Merge(src proto.Message) { + xxx_messageInfo_X509SVID.Merge(dst, src) } -func (m *Svid) XXX_Size() int { - return xxx_messageInfo_Svid.Size(m) +func (m *X509SVID) XXX_Size() int { + return xxx_messageInfo_X509SVID.Size(m) } -func (m *Svid) XXX_DiscardUnknown() { - xxx_messageInfo_Svid.DiscardUnknown(m) +func (m *X509SVID) XXX_DiscardUnknown() { + xxx_messageInfo_X509SVID.DiscardUnknown(m) } -var xxx_messageInfo_Svid proto.InternalMessageInfo +var xxx_messageInfo_X509SVID proto.InternalMessageInfo -func (m *Svid) GetSvidCert() []byte { +func (m *X509SVID) GetCert() []byte { if m != nil { - return m.SvidCert + return m.Cert } return nil } -func (m *Svid) GetTtl() int32 { +func (m *X509SVID) GetExpiresAt() int64 { if m != nil { - return m.Ttl + return m.ExpiresAt } return 0 } // A message returned by the Spire Server, which includes a map of signed SVIDs and // a list of all current Registration Entries which are relevant to the caller SPIFFE ID. -type SvidUpdate struct { +type X509SVIDUpdate struct { // A map containing SVID values and corresponding SPIFFE IDs as the // keys. Map[SPIFFE_ID] => SVID. - Svids map[string]*Svid `protobuf:"bytes,1,rep,name=svids" json:"svids,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Svids map[string]*X509SVID `protobuf:"bytes,1,rep,name=svids" json:"svids,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Latest SPIRE Server bundle Bundle []byte `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` // A type representing a curated record that the Spire Server uses to set up @@ -108,51 +108,149 @@ type SvidUpdate struct { XXX_sizecache int32 `json:"-"` } -func (m *SvidUpdate) Reset() { *m = SvidUpdate{} } -func (m *SvidUpdate) String() string { return proto.CompactTextString(m) } -func (*SvidUpdate) ProtoMessage() {} -func (*SvidUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_node_4be23de3db683a67, []int{1} +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_13be1b7ed7e739ea, []int{1} } -func (m *SvidUpdate) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SvidUpdate.Unmarshal(m, b) +func (m *X509SVIDUpdate) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_X509SVIDUpdate.Unmarshal(m, b) } -func (m *SvidUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SvidUpdate.Marshal(b, m, deterministic) +func (m *X509SVIDUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_X509SVIDUpdate.Marshal(b, m, deterministic) } -func (dst *SvidUpdate) XXX_Merge(src proto.Message) { - xxx_messageInfo_SvidUpdate.Merge(dst, src) +func (dst *X509SVIDUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_X509SVIDUpdate.Merge(dst, src) } -func (m *SvidUpdate) XXX_Size() int { - return xxx_messageInfo_SvidUpdate.Size(m) +func (m *X509SVIDUpdate) XXX_Size() int { + return xxx_messageInfo_X509SVIDUpdate.Size(m) } -func (m *SvidUpdate) XXX_DiscardUnknown() { - xxx_messageInfo_SvidUpdate.DiscardUnknown(m) +func (m *X509SVIDUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_X509SVIDUpdate.DiscardUnknown(m) } -var xxx_messageInfo_SvidUpdate proto.InternalMessageInfo +var xxx_messageInfo_X509SVIDUpdate proto.InternalMessageInfo -func (m *SvidUpdate) GetSvids() map[string]*Svid { +func (m *X509SVIDUpdate) GetSvids() map[string]*X509SVID { if m != nil { return m.Svids } return nil } -func (m *SvidUpdate) GetBundle() []byte { +func (m *X509SVIDUpdate) GetBundle() []byte { if m != nil { return m.Bundle } return nil } -func (m *SvidUpdate) GetRegistrationEntries() []*common.RegistrationEntry { +func (m *X509SVIDUpdate) GetRegistrationEntries() []*common.RegistrationEntry { if m != nil { return m.RegistrationEntries } return nil } +// JSR is a JWT SVID signing request. +type JSR struct { + // SPIFFE ID of the workload + SpiffeId string `protobuf:"bytes,1,opt,name=spiffe_id,json=spiffeId" json:"spiffe_id,omitempty"` + // List of intended audience + Audience []string `protobuf:"bytes,2,rep,name=audience" json:"audience,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +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_13be1b7ed7e739ea, []int{2} +} +func (m *JSR) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_JSR.Unmarshal(m, b) +} +func (m *JSR) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_JSR.Marshal(b, m, deterministic) +} +func (dst *JSR) XXX_Merge(src proto.Message) { + xxx_messageInfo_JSR.Merge(dst, src) +} +func (m *JSR) XXX_Size() int { + return xxx_messageInfo_JSR.Size(m) +} +func (m *JSR) XXX_DiscardUnknown() { + xxx_messageInfo_JSR.DiscardUnknown(m) +} + +var xxx_messageInfo_JSR proto.InternalMessageInfo + +func (m *JSR) GetSpiffeId() string { + if m != nil { + return m.SpiffeId + } + return "" +} + +func (m *JSR) GetAudience() []string { + if m != nil { + return m.Audience + } + return nil +} + +// JWTSVID is a signed JWT-SVID with fields lifted out for convenience. +type JWTSVID struct { + // JWT-SVID JWT token + Token string `protobuf:"bytes,1,opt,name=token" json:"token,omitempty"` + // SVID expiration timestamp (seconds since Unix epoch) + ExpiresAt int64 `protobuf:"varint,2,opt,name=expires_at,json=expiresAt" json:"expires_at,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +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_13be1b7ed7e739ea, []int{3} +} +func (m *JWTSVID) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_JWTSVID.Unmarshal(m, b) +} +func (m *JWTSVID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_JWTSVID.Marshal(b, m, deterministic) +} +func (dst *JWTSVID) XXX_Merge(src proto.Message) { + xxx_messageInfo_JWTSVID.Merge(dst, src) +} +func (m *JWTSVID) XXX_Size() int { + return xxx_messageInfo_JWTSVID.Size(m) +} +func (m *JWTSVID) XXX_DiscardUnknown() { + xxx_messageInfo_JWTSVID.DiscardUnknown(m) +} + +var xxx_messageInfo_JWTSVID proto.InternalMessageInfo + +func (m *JWTSVID) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +func (m *JWTSVID) GetExpiresAt() int64 { + if m != nil { + return m.ExpiresAt + } + return 0 +} + // Represents a request to attest the node. type AttestRequest struct { // A type which contains attestation data for specific platform. @@ -170,7 +268,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_4be23de3db683a67, []int{2} + return fileDescriptor_node_13be1b7ed7e739ea, []int{4} } func (m *AttestRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttestRequest.Unmarshal(m, b) @@ -216,10 +314,10 @@ func (m *AttestRequest) GetResponse() []byte { type AttestResponse struct { // It includes a map of signed SVIDs and an array of all current // Registration Entries which are relevant to the caller SPIFFE ID. - SvidUpdate *SvidUpdate `protobuf:"bytes,1,opt,name=svid_update,json=svidUpdate" json:"svid_update,omitempty"` + SvidUpdate *X509SVIDUpdate `protobuf:"bytes,1,opt,name=svid_update,json=svidUpdate" json:"svid_update,omitempty"` // This is a challenge issued by the server to the node. If populated, the // node is expected to respond with another AttestRequest with the response. - // This field is mutually exclusive with the svid_update field. + // This field is mutually exclusive with the update field. Challenge []byte `protobuf:"bytes,2,opt,name=challenge,proto3" json:"challenge,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -230,7 +328,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_4be23de3db683a67, []int{3} + return fileDescriptor_node_13be1b7ed7e739ea, []int{5} } func (m *AttestResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AttestResponse.Unmarshal(m, b) @@ -250,7 +348,7 @@ func (m *AttestResponse) XXX_DiscardUnknown() { var xxx_messageInfo_AttestResponse proto.InternalMessageInfo -func (m *AttestResponse) GetSvidUpdate() *SvidUpdate { +func (m *AttestResponse) GetSvidUpdate() *X509SVIDUpdate { if m != nil { return m.SvidUpdate } @@ -277,7 +375,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_4be23de3db683a67, []int{4} + return fileDescriptor_node_13be1b7ed7e739ea, []int{6} } func (m *FetchX509SVIDRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchX509SVIDRequest.Unmarshal(m, b) @@ -309,17 +407,17 @@ func (m *FetchX509SVIDRequest) GetCsrs() [][]byte { type FetchX509SVIDResponse struct { // It includes a map of signed SVIDs and an array of all current Registration // Entries which are relevant to the caller SPIFFE ID. - SvidUpdate *SvidUpdate `protobuf:"bytes,1,opt,name=svid_update,json=svidUpdate" json:"svid_update,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + SvidUpdate *X509SVIDUpdate `protobuf:"bytes,1,opt,name=svid_update,json=svidUpdate" json:"svid_update,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } 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_4be23de3db683a67, []int{5} + return fileDescriptor_node_13be1b7ed7e739ea, []int{7} } func (m *FetchX509SVIDResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchX509SVIDResponse.Unmarshal(m, b) @@ -339,13 +437,91 @@ func (m *FetchX509SVIDResponse) XXX_DiscardUnknown() { var xxx_messageInfo_FetchX509SVIDResponse proto.InternalMessageInfo -func (m *FetchX509SVIDResponse) GetSvidUpdate() *SvidUpdate { +func (m *FetchX509SVIDResponse) GetSvidUpdate() *X509SVIDUpdate { if m != nil { return m.SvidUpdate } return nil } +type FetchJWTSVIDRequest struct { + // The JWT signing request + Jsr *JSR `protobuf:"bytes,1,opt,name=jsr" json:"jsr,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +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_13be1b7ed7e739ea, []int{8} +} +func (m *FetchJWTSVIDRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FetchJWTSVIDRequest.Unmarshal(m, b) +} +func (m *FetchJWTSVIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FetchJWTSVIDRequest.Marshal(b, m, deterministic) +} +func (dst *FetchJWTSVIDRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_FetchJWTSVIDRequest.Merge(dst, src) +} +func (m *FetchJWTSVIDRequest) XXX_Size() int { + return xxx_messageInfo_FetchJWTSVIDRequest.Size(m) +} +func (m *FetchJWTSVIDRequest) XXX_DiscardUnknown() { + xxx_messageInfo_FetchJWTSVIDRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_FetchJWTSVIDRequest proto.InternalMessageInfo + +func (m *FetchJWTSVIDRequest) GetJsr() *JSR { + if m != nil { + return m.Jsr + } + return nil +} + +type FetchJWTSVIDResponse struct { + // The signed JWT-SVID + Svid *JWTSVID `protobuf:"bytes,1,opt,name=svid" json:"svid,omitempty"` + 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_13be1b7ed7e739ea, []int{9} +} +func (m *FetchJWTSVIDResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FetchJWTSVIDResponse.Unmarshal(m, b) +} +func (m *FetchJWTSVIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FetchJWTSVIDResponse.Marshal(b, m, deterministic) +} +func (dst *FetchJWTSVIDResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_FetchJWTSVIDResponse.Merge(dst, src) +} +func (m *FetchJWTSVIDResponse) XXX_Size() int { + return xxx_messageInfo_FetchJWTSVIDResponse.Size(m) +} +func (m *FetchJWTSVIDResponse) XXX_DiscardUnknown() { + xxx_messageInfo_FetchJWTSVIDResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_FetchJWTSVIDResponse proto.InternalMessageInfo + +func (m *FetchJWTSVIDResponse) GetSvid() *JWTSVID { + if m != nil { + return m.Svid + } + return nil +} + // Represents a request with an array of SPIFFE Ids. type FetchFederatedBundleRequest struct { // An array of SPIFFE Ids. @@ -359,7 +535,7 @@ func (m *FetchFederatedBundleRequest) Reset() { *m = FetchFederatedBundl func (m *FetchFederatedBundleRequest) String() string { return proto.CompactTextString(m) } func (*FetchFederatedBundleRequest) ProtoMessage() {} func (*FetchFederatedBundleRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_node_4be23de3db683a67, []int{6} + return fileDescriptor_node_13be1b7ed7e739ea, []int{10} } func (m *FetchFederatedBundleRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchFederatedBundleRequest.Unmarshal(m, b) @@ -399,7 +575,7 @@ func (m *FetchFederatedBundleResponse) Reset() { *m = FetchFederatedBund func (m *FetchFederatedBundleResponse) String() string { return proto.CompactTextString(m) } func (*FetchFederatedBundleResponse) ProtoMessage() {} func (*FetchFederatedBundleResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_node_4be23de3db683a67, []int{7} + return fileDescriptor_node_13be1b7ed7e739ea, []int{11} } func (m *FetchFederatedBundleResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FetchFederatedBundleResponse.Unmarshal(m, b) @@ -427,13 +603,17 @@ func (m *FetchFederatedBundleResponse) GetFederatedBundles() map[string][]byte { } func init() { - proto.RegisterType((*Svid)(nil), "spire.api.node.Svid") - proto.RegisterType((*SvidUpdate)(nil), "spire.api.node.SvidUpdate") - proto.RegisterMapType((map[string]*Svid)(nil), "spire.api.node.SvidUpdate.SvidsEntry") + proto.RegisterType((*X509SVID)(nil), "spire.api.node.X509SVID") + proto.RegisterType((*X509SVIDUpdate)(nil), "spire.api.node.X509SVIDUpdate") + 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") proto.RegisterType((*AttestRequest)(nil), "spire.api.node.AttestRequest") proto.RegisterType((*AttestResponse)(nil), "spire.api.node.AttestResponse") proto.RegisterType((*FetchX509SVIDRequest)(nil), "spire.api.node.FetchX509SVIDRequest") 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") @@ -456,6 +636,8 @@ type NodeClient interface { // Base Node SVID or the Registered Node SVID used for this call) // List can be empty to allow Node Agent cache refresh). 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) @@ -531,6 +713,15 @@ func (x *nodeFetchX509SVIDClient) Recv() (*FetchX509SVIDResponse, error) { return m, nil } +func (c *nodeClient) FetchJWTSVID(ctx context.Context, in *FetchJWTSVIDRequest, opts ...grpc.CallOption) (*FetchJWTSVIDResponse, error) { + out := new(FetchJWTSVIDResponse) + err := grpc.Invoke(ctx, "/spire.api.node.Node/FetchJWTSVID", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *nodeClient) FetchFederatedBundle(ctx context.Context, in *FetchFederatedBundleRequest, opts ...grpc.CallOption) (*FetchFederatedBundleResponse, error) { out := new(FetchFederatedBundleResponse) err := grpc.Invoke(ctx, "/spire.api.node.Node/FetchFederatedBundle", in, out, c.cc, opts...) @@ -549,6 +740,8 @@ type NodeServer interface { // Base Node SVID or the Registered Node SVID used for this call) // List can be empty to allow Node Agent cache refresh). 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) @@ -610,6 +803,24 @@ func (x *nodeFetchX509SVIDServer) Recv() (*FetchX509SVIDRequest, error) { return m, nil } +func _Node_FetchJWTSVID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FetchJWTSVIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServer).FetchJWTSVID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/spire.api.node.Node/FetchJWTSVID", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServer).FetchJWTSVID(ctx, req.(*FetchJWTSVIDRequest)) + } + 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 { @@ -632,6 +843,10 @@ var _Node_serviceDesc = grpc.ServiceDesc{ ServiceName: "spire.api.node.Node", HandlerType: (*NodeServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "FetchJWTSVID", + Handler: _Node_FetchJWTSVID_Handler, + }, { MethodName: "FetchFederatedBundle", Handler: _Node_FetchFederatedBundle_Handler, @@ -654,45 +869,52 @@ var _Node_serviceDesc = grpc.ServiceDesc{ Metadata: "node.proto", } -func init() { proto.RegisterFile("node.proto", fileDescriptor_node_4be23de3db683a67) } - -var fileDescriptor_node_4be23de3db683a67 = []byte{ - // 583 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x6f, 0x6f, 0x12, 0x4f, - 0x10, 0xfe, 0x1d, 0x50, 0x02, 0x03, 0xed, 0x0f, 0x57, 0x34, 0x97, 0x6b, 0xab, 0xe4, 0x62, 0x13, - 0x52, 0xcd, 0x51, 0x31, 0x4d, 0xb4, 0x7d, 0x55, 0x5a, 0x1b, 0x1b, 0x93, 0xc6, 0x6c, 0xd5, 0x18, - 0xdf, 0xe0, 0x72, 0x37, 0xc0, 0xa5, 0x70, 0x77, 0xdd, 0x5d, 0x48, 0xfa, 0x01, 0x8c, 0x5f, 0xc5, - 0x0f, 0xe4, 0x07, 0x32, 0xbb, 0x7b, 0x17, 0xe0, 0x42, 0xfd, 0x93, 0xf8, 0x8a, 0xd9, 0x67, 0x9f, - 0x9d, 0x79, 0x66, 0xe6, 0xe1, 0x00, 0xa2, 0x38, 0x40, 0x2f, 0xe1, 0xb1, 0x8c, 0xc9, 0x96, 0x48, - 0x42, 0x8e, 0x1e, 0x4b, 0x42, 0x4f, 0xa1, 0xce, 0xf3, 0x51, 0x28, 0xc7, 0xb3, 0x81, 0xe7, 0xc7, - 0xd3, 0x8e, 0x48, 0xc2, 0xe1, 0x10, 0x3b, 0x9a, 0xd1, 0xd1, 0xf4, 0x8e, 0x1f, 0x4f, 0xa7, 0x71, - 0x94, 0xfe, 0x98, 0x14, 0xee, 0x21, 0x94, 0xae, 0xe6, 0x61, 0x40, 0xb6, 0xa1, 0x2a, 0xe6, 0x61, - 0xd0, 0xf7, 0x91, 0x4b, 0xdb, 0x6a, 0x59, 0xed, 0x3a, 0xad, 0x28, 0xe0, 0x14, 0xb9, 0x24, 0x0d, - 0x28, 0x4a, 0x39, 0xb1, 0x0b, 0x2d, 0xab, 0xbd, 0x41, 0x55, 0xe8, 0x7e, 0x2d, 0x00, 0xa8, 0x77, - 0x1f, 0x92, 0x80, 0x49, 0x24, 0xc7, 0xb0, 0xa1, 0xc8, 0xc2, 0xb6, 0x5a, 0xc5, 0x76, 0xad, 0xbb, - 0xe7, 0xad, 0x0a, 0xf3, 0x16, 0x54, 0x1d, 0x8a, 0xd7, 0x91, 0xe4, 0xb7, 0xd4, 0xbc, 0x21, 0x0f, - 0xa1, 0x3c, 0x98, 0x45, 0xc1, 0x04, 0x75, 0x81, 0x3a, 0x4d, 0x4f, 0x84, 0x42, 0x93, 0xe3, 0x28, - 0x14, 0x92, 0x33, 0x19, 0xc6, 0x51, 0x1f, 0x23, 0xc9, 0x43, 0x14, 0x76, 0x51, 0xd7, 0x78, 0x9c, - 0xd6, 0x48, 0xbb, 0xa1, 0x4b, 0x4c, 0x93, 0xfd, 0x3e, 0xcf, 0x41, 0x21, 0x0a, 0xe7, 0xd2, 0xc8, - 0x36, 0x02, 0x54, 0x5f, 0xd7, 0x78, 0xab, 0xdb, 0xad, 0x52, 0x15, 0x92, 0x7d, 0xd8, 0x98, 0xb3, - 0xc9, 0xcc, 0x48, 0xa9, 0x75, 0x9b, 0xeb, 0x1a, 0xa1, 0x86, 0x72, 0x54, 0x78, 0x69, 0xb9, 0xdf, - 0x2c, 0xd8, 0x3c, 0x91, 0x12, 0x85, 0xa4, 0x78, 0x33, 0x43, 0x21, 0xc9, 0x1b, 0x68, 0x30, 0x0d, - 0x18, 0xd1, 0x01, 0x93, 0x4c, 0x17, 0xa8, 0x75, 0x77, 0x57, 0x15, 0x9f, 0x2c, 0x58, 0x67, 0x4c, - 0x32, 0xfa, 0x3f, 0x5b, 0x05, 0x94, 0x3a, 0x5f, 0xf0, 0x74, 0x28, 0x2a, 0x24, 0x0e, 0x54, 0x38, - 0x8a, 0x24, 0x8e, 0x04, 0xda, 0x45, 0xb3, 0xa3, 0xec, 0xec, 0x5e, 0xc3, 0x56, 0x26, 0xc4, 0x20, - 0xe4, 0x18, 0x6a, 0x7a, 0xa5, 0x33, 0x3d, 0xf8, 0x54, 0x84, 0x73, 0xf7, 0x6a, 0x28, 0x88, 0xc5, - 0x46, 0x77, 0xa0, 0xea, 0x8f, 0xd9, 0x64, 0x82, 0xd1, 0x28, 0xdb, 0xcb, 0x02, 0x70, 0xf7, 0xa1, - 0x79, 0x8e, 0xd2, 0x1f, 0x7f, 0x3a, 0x3c, 0x78, 0x75, 0xf5, 0xf1, 0xe2, 0x2c, 0x6b, 0x9e, 0x40, - 0xc9, 0x17, 0x5c, 0xd8, 0x85, 0x56, 0xb1, 0x5d, 0xa7, 0x3a, 0x76, 0xdf, 0xc3, 0x83, 0x1c, 0xf7, - 0x1f, 0xe8, 0x73, 0x8f, 0x60, 0x5b, 0x67, 0x3d, 0xc7, 0x00, 0x39, 0x93, 0x18, 0xf4, 0xb4, 0x69, - 0x32, 0x21, 0xca, 0xce, 0xfa, 0x0f, 0xd0, 0x0f, 0x03, 0x6d, 0xca, 0x2a, 0xad, 0x18, 0xe0, 0x22, - 0x70, 0x7f, 0x58, 0xb0, 0xb3, 0xfe, 0x71, 0xaa, 0x2c, 0x86, 0x7b, 0xc3, 0xec, 0xaa, 0x6f, 0xdc, - 0x98, 0x59, 0xbb, 0x97, 0xd7, 0xf7, 0xab, 0x44, 0x5e, 0x0e, 0x4f, 0x7d, 0xdf, 0x18, 0xe6, 0x60, - 0xe7, 0x54, 0xcd, 0x68, 0x0d, 0x75, 0x8d, 0x43, 0x9b, 0xcb, 0x0e, 0xad, 0x2f, 0x79, 0xb1, 0xfb, - 0xbd, 0x00, 0xa5, 0xcb, 0x38, 0x40, 0xf2, 0x16, 0xca, 0xc6, 0x0a, 0x64, 0x37, 0xaf, 0x76, 0xc5, - 0xab, 0xce, 0xa3, 0xbb, 0xae, 0x8d, 0xfc, 0xb6, 0x75, 0x60, 0x91, 0x2f, 0xb0, 0xb9, 0xb2, 0x3e, - 0xf2, 0x64, 0xed, 0x04, 0x72, 0x4e, 0x70, 0xf6, 0x7e, 0xc3, 0x5a, 0xaa, 0x70, 0x93, 0x9a, 0x29, - 0x37, 0x01, 0xf2, 0xf4, 0xcf, 0x46, 0x6d, 0xea, 0x3d, 0xfb, 0x9b, 0xbd, 0xf4, 0xca, 0x9f, 0x4b, - 0x8a, 0xf4, 0xee, 0xbf, 0x41, 0x59, 0x7f, 0x06, 0x5f, 0xfc, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xfc, - 0x7e, 0x82, 0x00, 0x57, 0x05, 0x00, 0x00, +func init() { proto.RegisterFile("node.proto", fileDescriptor_node_13be1b7ed7e739ea) } + +var fileDescriptor_node_13be1b7ed7e739ea = []byte{ + // 689 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x5b, 0x4f, 0x13, 0x41, + 0x14, 0x76, 0xd9, 0x52, 0xe9, 0x69, 0x41, 0x1c, 0xaa, 0x36, 0xcb, 0x45, 0xb2, 0x42, 0x52, 0xc5, + 0x6c, 0xb1, 0xc6, 0x44, 0x89, 0x42, 0xb8, 0x48, 0x04, 0x13, 0x63, 0xa6, 0x5e, 0xd0, 0x97, 0x3a, + 0xec, 0x4e, 0x61, 0xa5, 0xec, 0x96, 0x99, 0x29, 0x91, 0x67, 0x1f, 0x7c, 0xf6, 0xbf, 0xf9, 0x83, + 0xcc, 0x5c, 0x16, 0x76, 0xd7, 0x52, 0x34, 0xf1, 0x89, 0x9d, 0x33, 0xdf, 0x39, 0xe7, 0x9b, 0xf3, + 0x7d, 0x87, 0x02, 0x44, 0x71, 0x40, 0xbd, 0x1e, 0x8b, 0x45, 0x8c, 0x26, 0x78, 0x2f, 0x64, 0xd4, + 0x23, 0xbd, 0xd0, 0x93, 0x51, 0xe7, 0xd1, 0x41, 0x28, 0x0e, 0xfb, 0xfb, 0x9e, 0x1f, 0x1f, 0x37, + 0x78, 0x2f, 0xec, 0x74, 0x68, 0x43, 0x21, 0x1a, 0x0a, 0xde, 0xf0, 0xe3, 0xe3, 0xe3, 0x38, 0x32, + 0x7f, 0x74, 0x09, 0xf7, 0x05, 0x8c, 0xed, 0x3d, 0x59, 0x7e, 0xd6, 0xfa, 0xb0, 0xb3, 0x85, 0x10, + 0x14, 0x7c, 0xca, 0x44, 0xcd, 0x9a, 0xb7, 0xea, 0x15, 0xac, 0xbe, 0xd1, 0x2c, 0x00, 0xfd, 0x26, + 0x6b, 0xf0, 0x36, 0x11, 0xb5, 0x91, 0x79, 0xab, 0x6e, 0xe3, 0x92, 0x89, 0xac, 0x0b, 0xf7, 0xe7, + 0x08, 0x4c, 0x24, 0xf9, 0xef, 0x7b, 0x01, 0x11, 0x14, 0xad, 0xc1, 0x28, 0x3f, 0x0d, 0x03, 0x5e, + 0xb3, 0xe6, 0xed, 0x7a, 0xb9, 0x79, 0xdf, 0xcb, 0x92, 0xf4, 0xb2, 0x70, 0xaf, 0x25, 0xb1, 0x2f, + 0x23, 0xc1, 0xce, 0xb0, 0xce, 0x43, 0xb7, 0xa1, 0xb8, 0xdf, 0x8f, 0x82, 0x2e, 0x55, 0xed, 0x2a, + 0xd8, 0x9c, 0x10, 0x86, 0x2a, 0xa3, 0x07, 0x21, 0x17, 0x8c, 0x88, 0x30, 0x8e, 0xda, 0x34, 0x12, + 0x2c, 0xa4, 0xbc, 0x66, 0xab, 0x3e, 0x77, 0x4d, 0x1f, 0xf3, 0x3a, 0x9c, 0x42, 0xea, 0xea, 0x53, + 0x2c, 0x17, 0x0a, 0x29, 0x77, 0x30, 0xc0, 0x05, 0x01, 0x34, 0x09, 0xf6, 0x11, 0x3d, 0x53, 0xef, + 0x2f, 0x61, 0xf9, 0x89, 0x3c, 0x18, 0x3d, 0x25, 0xdd, 0xbe, 0xa6, 0x52, 0x6e, 0xd6, 0x2e, 0x7b, + 0x0c, 0xd6, 0xb0, 0x95, 0x91, 0xa7, 0x96, 0xbb, 0x0a, 0xf6, 0x6e, 0x0b, 0xa3, 0x69, 0x28, 0x69, + 0x0d, 0xda, 0x61, 0x60, 0x4a, 0x8e, 0xe9, 0xc0, 0x4e, 0x80, 0x1c, 0x18, 0x23, 0xfd, 0x20, 0xa4, + 0x91, 0x2f, 0x4b, 0xdb, 0xf2, 0x2e, 0x39, 0xbb, 0xab, 0x70, 0x7d, 0xf7, 0xe3, 0x3b, 0xa5, 0x48, + 0x15, 0x46, 0x45, 0x7c, 0x44, 0x23, 0x93, 0xaf, 0x0f, 0x57, 0x69, 0xf2, 0xc3, 0x82, 0xf1, 0x75, + 0x21, 0x28, 0x17, 0x98, 0x9e, 0xf4, 0x29, 0x17, 0xe8, 0x15, 0x4c, 0x12, 0x15, 0xd0, 0x83, 0x0b, + 0x88, 0x20, 0xaa, 0x62, 0xb9, 0x39, 0x9b, 0x9d, 0xda, 0xfa, 0x05, 0x6a, 0x8b, 0x08, 0x82, 0x6f, + 0x90, 0x6c, 0x40, 0x4e, 0xc8, 0xe7, 0xcc, 0x08, 0x23, 0x3f, 0xe5, 0x4b, 0x18, 0xe5, 0xbd, 0x38, + 0xe2, 0xb4, 0x66, 0xab, 0xf0, 0xf9, 0xd9, 0x8d, 0x61, 0x22, 0x21, 0xa2, 0x23, 0x68, 0x0d, 0xca, + 0x52, 0xe4, 0x76, 0x5f, 0x89, 0x6f, 0x48, 0xcc, 0x0d, 0xb7, 0x08, 0x06, 0x99, 0x62, 0xdc, 0x35, + 0x03, 0x25, 0xff, 0x90, 0x74, 0xbb, 0x34, 0x3a, 0x48, 0xfc, 0x71, 0x11, 0x70, 0x1f, 0x40, 0x75, + 0x9b, 0x0a, 0xff, 0xf0, 0x5c, 0x16, 0x33, 0x00, 0xe9, 0x6c, 0xce, 0xb8, 0x1a, 0xb5, 0x74, 0x36, + 0x67, 0xdc, 0xdd, 0x83, 0x5b, 0x39, 0xec, 0x7f, 0xe2, 0xe8, 0x3e, 0x87, 0x29, 0x55, 0xd9, 0xa8, + 0x98, 0x90, 0x58, 0x04, 0xfb, 0x2b, 0x67, 0xa6, 0xde, 0x54, 0xbe, 0xde, 0x6e, 0x0b, 0x63, 0x79, + 0xef, 0x6e, 0x9a, 0x37, 0x9c, 0x67, 0x1b, 0x5a, 0x4b, 0x50, 0x90, 0x3d, 0x4c, 0xfe, 0x9d, 0x3f, + 0xf2, 0x0d, 0x5c, 0x81, 0xdc, 0x15, 0x98, 0x56, 0x45, 0xb6, 0x69, 0x40, 0x19, 0x11, 0x34, 0xd8, + 0x50, 0x3b, 0x94, 0x50, 0xc9, 0x79, 0xd3, 0x4e, 0x7b, 0xd3, 0xfd, 0x65, 0xc1, 0xcc, 0xe0, 0x64, + 0xc3, 0x24, 0x86, 0x9b, 0x9d, 0xe4, 0xaa, 0xad, 0x97, 0x33, 0xd9, 0xf6, 0x8d, 0x3c, 0xad, 0x61, + 0x85, 0xbc, 0x5c, 0xdc, 0xfc, 0x1b, 0x98, 0xec, 0xe4, 0xc2, 0xce, 0xa6, 0x94, 0x6a, 0x00, 0x74, + 0xc0, 0xc2, 0x56, 0xd3, 0x0b, 0x5b, 0x49, 0xad, 0x65, 0xf3, 0xbb, 0x0d, 0x85, 0x37, 0x71, 0x40, + 0xd1, 0x6b, 0x28, 0x6a, 0x57, 0xa2, 0xd9, 0x3c, 0xdb, 0xcc, 0xda, 0x38, 0x73, 0x97, 0x5d, 0x6b, + 0xfa, 0x75, 0x6b, 0xd9, 0x42, 0x5f, 0x60, 0x3c, 0xe3, 0x22, 0xb4, 0x30, 0x70, 0x02, 0x39, 0x43, + 0x3a, 0x8b, 0x57, 0xa0, 0x52, 0x1d, 0x3e, 0x41, 0x25, 0xed, 0x07, 0x74, 0x6f, 0x60, 0x6a, 0xd6, + 0x6b, 0xce, 0xc2, 0x70, 0x90, 0x11, 0xf2, 0xc4, 0x58, 0x2d, 0x37, 0x5c, 0xb4, 0xf4, 0x77, 0x2a, + 0xea, 0x56, 0x0f, 0xff, 0x45, 0xf2, 0x8d, 0xe2, 0xe7, 0x82, 0x04, 0xbd, 0xbd, 0xb6, 0x5f, 0x54, + 0x3f, 0x40, 0x8f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x7c, 0xb3, 0x59, 0x91, 0xd1, 0x06, 0x00, + 0x00, } diff --git a/proto/api/node/node.proto b/proto/api/node/node.proto index ffb60d3b88..58ebb6d6e8 100644 --- a/proto/api/node/node.proto +++ b/proto/api/node/node.proto @@ -11,20 +11,20 @@ import public "github.com/spiffe/spire/proto/common/common.proto"; // A type which contains the "Spiffe Verifiable Identity Document" and // a TTL indicating when the SVID expires. -message Svid { - // Spiffe Verifiable Identity Document. - bytes svid_cert = 1; +message X509SVID { + // X509 SVID (ASN.1 encoding) + bytes cert = 1; - // SVID expiration. - int32 ttl = 2; + // SVID expiration timestamp (in seconds since Unix epoch) + int64 expires_at = 2; } // A message returned by the Spire Server, which includes a map of signed SVIDs and //a list of all current Registration Entries which are relevant to the caller SPIFFE ID. -message SvidUpdate { +message X509SVIDUpdate { // A map containing SVID values and corresponding SPIFFE IDs as the // keys. Map[SPIFFE_ID] => SVID. - map svids = 1; + map svids = 1; // Latest SPIRE Server bundle bytes bundle = 2; @@ -34,6 +34,24 @@ message SvidUpdate { repeated spire.common.RegistrationEntry registration_entries = 3; } +// JSR is a JWT SVID signing request. +message JSR { + // SPIFFE ID of the workload + string spiffe_id = 1; + + // List of intended audience + repeated string audience = 2; +} + +// JWTSVID is a signed JWT-SVID with fields lifted out for convenience. +message JWTSVID { + // JWT-SVID JWT token + string token = 1; + + // SVID expiration timestamp (seconds since Unix epoch) + int64 expires_at = 2; +} + // Represents a request to attest the node. message AttestRequest { // A type which contains attestation data for specific platform. @@ -51,11 +69,11 @@ message AttestRequest { message AttestResponse { // It includes a map of signed SVIDs and an array of all current // Registration Entries which are relevant to the caller SPIFFE ID. - SvidUpdate svid_update = 1; + X509SVIDUpdate svid_update = 1; // This is a challenge issued by the server to the node. If populated, the // node is expected to respond with another AttestRequest with the response. - // This field is mutually exclusive with the svid_update field. + // This field is mutually exclusive with the update field. bytes challenge = 2; } @@ -70,7 +88,17 @@ message FetchX509SVIDRequest { message FetchX509SVIDResponse { // It includes a map of signed SVIDs and an array of all current Registration // Entries which are relevant to the caller SPIFFE ID. - SvidUpdate svid_update = 1; + X509SVIDUpdate svid_update = 1; +} + +message FetchJWTSVIDRequest { + // The JWT signing request + JSR jsr = 1; +} + +message FetchJWTSVIDResponse { + // The signed JWT-SVID + JWTSVID svid = 1; } // Represents a request with an array of SPIFFE Ids. @@ -94,6 +122,9 @@ service Node { // List can be empty to allow Node Agent cache refresh). rpc FetchX509SVID(stream FetchX509SVIDRequest) returns (stream FetchX509SVIDResponse); + // 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/test/mock/agent/client/client_mock.go b/test/mock/agent/client/client_mock.go index 72ddea3d9a..034f43fe0c 100644 --- a/test/mock/agent/client/client_mock.go +++ b/test/mock/agent/client/client_mock.go @@ -5,11 +5,11 @@ package mock_client import ( - reflect "reflect" - + context "context" gomock "github.com/golang/mock/gomock" client "github.com/spiffe/spire/pkg/agent/client" node "github.com/spiffe/spire/proto/api/node" + reflect "reflect" ) // MockClient is a mock of Client interface @@ -35,17 +35,30 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } +// FetchJWTSVID mocks base method +func (m *MockClient) FetchJWTSVID(arg0 context.Context, arg1 *node.JSR) (*client.JWTSVID, error) { + ret := m.ctrl.Call(m, "FetchJWTSVID", arg0, arg1) + ret0, _ := ret[0].(*client.JWTSVID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchJWTSVID indicates an expected call of FetchJWTSVID +func (mr *MockClientMockRecorder) FetchJWTSVID(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJWTSVID", reflect.TypeOf((*MockClient)(nil).FetchJWTSVID), arg0, arg1) +} + // FetchUpdates mocks base method -func (m *MockClient) FetchUpdates(arg0 *node.FetchX509SVIDRequest) (*client.Update, error) { - ret := m.ctrl.Call(m, "FetchUpdates", arg0) +func (m *MockClient) FetchUpdates(arg0 context.Context, arg1 *node.FetchX509SVIDRequest) (*client.Update, error) { + ret := m.ctrl.Call(m, "FetchUpdates", arg0, arg1) ret0, _ := ret[0].(*client.Update) ret1, _ := ret[1].(error) return ret0, ret1 } // FetchUpdates indicates an expected call of FetchUpdates -func (mr *MockClientMockRecorder) FetchUpdates(arg0 interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchUpdates", reflect.TypeOf((*MockClient)(nil).FetchUpdates), arg0) +func (mr *MockClientMockRecorder) FetchUpdates(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchUpdates", reflect.TypeOf((*MockClient)(nil).FetchUpdates), arg0, arg1) } // Release mocks base method diff --git a/test/mock/agent/client/generate.go b/test/mock/agent/client/generate.go new file mode 100644 index 0000000000..97cf92b3d1 --- /dev/null +++ b/test/mock/agent/client/generate.go @@ -0,0 +1,3 @@ +package mock_client + +//go:generate sh -c "mockgen github.com/spiffe/spire/pkg/agent/client Client > client_mock.go" diff --git a/test/mock/proto/api/node/node.go b/test/mock/proto/api/node/node.go index b89afec1b7..29243b03c6 100644 --- a/test/mock/proto/api/node/node.go +++ b/test/mock/proto/api/node/node.go @@ -72,6 +72,24 @@ func (mr *MockNodeClientMockRecorder) FetchFederatedBundle(arg0, arg1 interface{ 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} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "FetchJWTSVID", varargs...) + ret0, _ := ret[0].(*node.FetchJWTSVIDResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchJWTSVID indicates an expected call of FetchJWTSVID +func (mr *MockNodeClientMockRecorder) FetchJWTSVID(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJWTSVID", reflect.TypeOf((*MockNodeClient)(nil).FetchJWTSVID), varargs...) +} + // FetchX509SVID mocks base method func (m *MockNodeClient) FetchX509SVID(arg0 context.Context, arg1 ...grpc.CallOption) (node.Node_FetchX509SVIDClient, error) { varargs := []interface{}{arg0} @@ -498,6 +516,19 @@ func (mr *MockNodeServerMockRecorder) FetchFederatedBundle(arg0, arg1 interface{ 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) + ret0, _ := ret[0].(*node.FetchJWTSVIDResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchJWTSVID indicates an expected call of FetchJWTSVID +func (mr *MockNodeServerMockRecorder) FetchJWTSVID(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchJWTSVID", reflect.TypeOf((*MockNodeServer)(nil).FetchJWTSVID), arg0, arg1) +} + // FetchX509SVID mocks base method func (m *MockNodeServer) FetchX509SVID(arg0 node.Node_FetchX509SVIDServer) error { ret := m.ctrl.Call(m, "FetchX509SVID", arg0)