Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the Validate RPC on built-in plugins #5303

Merged
merged 14 commits into from
Sep 23, 2024
Merged
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spiffe/go-spiffe/v2 v2.3.0
github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20230721151831-bf67dde4721d
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d
github.com/stretchr/testify v1.9.0
github.com/uber-go/tally/v4 v4.1.16
github.com/valyala/fastjson v1.6.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1393,8 +1393,8 @@ github.com/spiffe/go-spiffe/v2 v2.3.0 h1:g2jYNb/PDMB8I7mBGL2Zuq/Ur6hUhoroxGQFyD6
github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7zvJnTV8ZyIY=
github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c h1:lK/B2paDUiqbngUGsLxDBmNX/BsG2yKxS8W/iGT+x2c=
github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI=
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20230721151831-bf67dde4721d h1:LCRQGU6vOqKLfRrG+GJQrwMwDILcAddAEIf4/1PaSVc=
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20230721151831-bf67dde4721d/go.mod h1:GA6o2PVLwyJdevT6KKt5ZXCY/ziAPna13y/seGk49Ik=
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d h1:Upcyq8u1aWFHTQSEskwxBE2PehobpY+M21LXXDS/mPw=
github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d/go.mod h1:GA6o2PVLwyJdevT6KKt5ZXCY/ziAPna13y/seGk49Ik=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
Expand Down
30 changes: 24 additions & 6 deletions pkg/agent/plugin/nodeattestor/awsiid/iid.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
"github.com/spiffe/spire/pkg/common/catalog"
caws "github.com/spiffe/spire/pkg/common/plugin/aws"
"github.com/spiffe/spire/pkg/common/pluginconf"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand All @@ -40,6 +41,16 @@ type IIDAttestorConfig struct {
EC2MetadataEndpoint string `hcl:"ec2_metadata_endpoint"`
}

func buildConfig(coreConfig catalog.CoreConfig, hclText string, status *pluginconf.Status) *IIDAttestorConfig {
newConfig := &IIDAttestorConfig{}
if err := hcl.Decode(newConfig, hclText); err != nil {
status.ReportErrorf("unable to decode configuration: %v", err)
return nil
}

return newConfig
}

// IIDAttestorPlugin implements aws nodeattestation in the agent.
type IIDAttestorPlugin struct {
nodeattestorv1.UnsafeNodeAttestorServer
Expand Down Expand Up @@ -155,20 +166,27 @@ func readStringAndClose(r io.ReadCloser) (string, error) {

// Configure implements the Config interface method of the same name
func (p *IIDAttestorPlugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {
// Parse HCL config payload into config struct
config := &IIDAttestorConfig{}
if err := hcl.Decode(config, req.HclConfiguration); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "unable to decode configuration: %v", err)
newConfig, _, err := pluginconf.Build(req, buildConfig)
if err != nil {
return nil, err
}

p.mtx.Lock()
defer p.mtx.Unlock()

p.config = config
p.config = newConfig

return &configv1.ConfigureResponse{}, nil
}

func (p *IIDAttestorPlugin) Validate(_ context.Context, req *configv1.ValidateRequest) (*configv1.ValidateResponse, error) {
_, notes, err := pluginconf.Build(req, buildConfig)

return &configv1.ValidateResponse{
Valid: err == nil,
Notes: notes,
}, nil
}

func (p *IIDAttestorPlugin) getConfig() (*IIDAttestorConfig, error) {
p.mtx.RLock()
defer p.mtx.RUnlock()
Expand Down
15 changes: 14 additions & 1 deletion pkg/agent/plugin/nodeattestor/awsiid/iid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (

"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/fullsailor/pkcs7"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/pemutil"
"github.com/spiffe/spire/pkg/common/plugin/aws"
"github.com/spiffe/spire/test/plugintest"
Expand Down Expand Up @@ -93,6 +95,9 @@ func (s *Suite) SetupTest() {
}))

s.p = s.loadPlugin(
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configuref(`ec2_metadata_endpoint = "http://%s/latest"`, s.server.Listener.Addr()),
)

Expand Down Expand Up @@ -141,13 +146,21 @@ func (s *Suite) TestConfigure() {

var err error
s.loadPlugin(
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.CaptureConfigureError(&err),
plugintest.Configure("malformed"),
)
require.Error(err)

// success
s.loadPlugin(plugintest.Configure(""))
s.loadPlugin(
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure(""),
)
}

func (s *Suite) loadPlugin(opts ...plugintest.Option) nodeattestor.NodeAttestor {
Expand Down
43 changes: 30 additions & 13 deletions pkg/agent/plugin/nodeattestor/azuremsi/msi.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/plugin/azure"
"github.com/spiffe/spire/pkg/common/pluginconf"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
Expand Down Expand Up @@ -40,6 +41,20 @@ type MSIAttestorConfig struct {
ResourceID string `hcl:"resource_id"`
}

func buildConfig(coreConfig catalog.CoreConfig, hclText string, status *pluginconf.Status) *MSIAttestorConfig {
newConfig := new(MSIAttestorConfig)
if err := hcl.Decode(newConfig, hclText); err != nil {
status.ReportErrorf("unable to decode configuration: %v", err)
return nil
}

if newConfig.ResourceID == "" {
newConfig.ResourceID = azure.DefaultMSIResourceID
}

return newConfig
}

type MSIAttestorPlugin struct {
nodeattestorv1.UnsafeNodeAttestorServer
configv1.UnsafeConfigServer
Expand Down Expand Up @@ -85,19 +100,27 @@ func (p *MSIAttestorPlugin) AidAttestation(stream nodeattestorv1.NodeAttestor_Ai
}

func (p *MSIAttestorPlugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {
config := new(MSIAttestorConfig)
if err := hcl.Decode(config, req.HclConfiguration); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "unable to decode configuration: %v", err)
newConfig, _, err := pluginconf.Build(req, buildConfig)
if err != nil {
return nil, err
}

if config.ResourceID == "" {
config.ResourceID = azure.DefaultMSIResourceID
}
p.mu.Lock()
defer p.mu.Unlock()
p.config = newConfig

p.setConfig(config)
return &configv1.ConfigureResponse{}, nil
}

func (p *MSIAttestorPlugin) Validate(_ context.Context, req *configv1.ValidateRequest) (*configv1.ValidateResponse, error) {
_, notes, err := pluginconf.Build(req, buildConfig)

return &configv1.ValidateResponse{
Valid: err == nil,
Notes: notes,
}, nil
}

func (p *MSIAttestorPlugin) getConfig() (*MSIAttestorConfig, error) {
p.mu.RLock()
defer p.mu.RUnlock()
Expand All @@ -106,9 +129,3 @@ func (p *MSIAttestorPlugin) getConfig() (*MSIAttestorConfig, error) {
}
return p.config, nil
}

func (p *MSIAttestorPlugin) setConfig(config *MSIAttestorConfig) {
p.mu.Lock()
defer p.mu.Unlock()
p.config = config
}
40 changes: 35 additions & 5 deletions pkg/agent/plugin/nodeattestor/azuremsi/msi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (

jose "github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/plugin/azure"
"github.com/spiffe/spire/test/plugintest"
"github.com/spiffe/spire/test/spiretest"
Expand Down Expand Up @@ -49,7 +51,12 @@ func (s *MSIAttestorSuite) TestAidAttestationNotConfigured() {
func (s *MSIAttestorSuite) TestAidAttestationFailedToObtainToken() {
s.tokenErr = errors.New("FAILED")

attestor := s.loadAttestor(plugintest.Configure(""))
attestor := s.loadAttestor(
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure(""),
)
err := attestor.Attest(context.Background(), streamBuilder.Build())
s.RequireGRPCStatus(err, codes.Internal, "nodeattestor(azure_msi): unable to fetch token: FAILED")
}
Expand All @@ -59,23 +66,46 @@ func (s *MSIAttestorSuite) TestAidAttestationSuccess() {

expectPayload := []byte(fmt.Sprintf(`{"token":%q}`, s.token))

attestor := s.loadAttestor(plugintest.Configure(""))
attestor := s.loadAttestor(
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure(""),
)
err := attestor.Attest(context.Background(), streamBuilder.ExpectAndBuild(expectPayload))
s.Require().NoError(err)
}

func (s *MSIAttestorSuite) TestConfigure() {
// malformed configuration
var err error
s.loadAttestor(plugintest.CaptureConfigureError(&err), plugintest.Configure("blah"))
s.loadAttestor(
plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure("blah"),
)
s.RequireGRPCStatusContains(err, codes.InvalidArgument, "unable to decode configuration")

// success
s.loadAttestor(plugintest.CaptureConfigureError(&err), plugintest.Configure(""))
s.loadAttestor(
plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure(""),
)
s.Require().NoError(err)

// success with resource_id
s.loadAttestor(plugintest.CaptureConfigureError(&err), plugintest.Configure(`resource_id = "foo"`))
s.loadAttestor(
plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure(`resource_id = "foo"`),
)
s.Require().NoError(err)
}

Expand Down
44 changes: 32 additions & 12 deletions pkg/agent/plugin/nodeattestor/gcpiit/iit.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
configv1 "github.com/spiffe/spire-plugin-sdk/proto/spire/service/common/config/v1"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/plugin/gcp"
"github.com/spiffe/spire/pkg/common/pluginconf"
)

const (
Expand Down Expand Up @@ -50,6 +51,24 @@ type IITAttestorConfig struct {
ServiceAccount string `hcl:"service_account"`
}

func buildConfig(coreConfig catalog.CoreConfig, hclText string, status *pluginconf.Status) *IITAttestorConfig {
newConfig := &IITAttestorConfig{}
if err := hcl.Decode(newConfig, hclText); err != nil {
status.ReportErrorf("unable to decode configuration: %v", err)
return nil
}

if newConfig.ServiceAccount == "" {
newConfig.ServiceAccount = defaultServiceAccount
}

if newConfig.IdentityTokenHost == "" {
newConfig.IdentityTokenHost = defaultIdentityTokenHost
}

return newConfig
}

// NewIITAttestorPlugin creates a new IITAttestorPlugin.
func New() *IITAttestorPlugin {
return &IITAttestorPlugin{}
Expand All @@ -76,26 +95,27 @@ func (p *IITAttestorPlugin) AidAttestation(stream nodeattestorv1.NodeAttestor_Ai
}

func (p *IITAttestorPlugin) Configure(_ context.Context, req *configv1.ConfigureRequest) (*configv1.ConfigureResponse, error) {
config := &IITAttestorConfig{}
if err := hcl.Decode(config, req.HclConfiguration); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "unable to decode configuration: %v", err)
}

if config.ServiceAccount == "" {
config.ServiceAccount = defaultServiceAccount
}

if config.IdentityTokenHost == "" {
config.IdentityTokenHost = defaultIdentityTokenHost
newConfig, _, err := pluginconf.Build(req, buildConfig)
if err != nil {
return nil, err
}

p.mtx.Lock()
defer p.mtx.Unlock()
p.config = config
p.config = newConfig

return &configv1.ConfigureResponse{}, nil
}

func (p *IITAttestorPlugin) Validate(_ context.Context, req *configv1.ValidateRequest) (*configv1.ValidateResponse, error) {
_, notes, err := pluginconf.Build(req, buildConfig)

return &configv1.ValidateResponse{
Valid: err == nil,
Notes: notes,
}, nil
}

func (p *IITAttestorPlugin) getConfig() (*IITAttestorConfig, error) {
p.mtx.Lock()
defer p.mtx.Unlock()
Expand Down
16 changes: 13 additions & 3 deletions pkg/agent/plugin/nodeattestor/gcpiit/iit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/cryptosigner"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/spiffe/go-spiffe/v2/spiffeid"
"github.com/spiffe/spire/pkg/agent/plugin/nodeattestor"
nodeattestortest "github.com/spiffe/spire/pkg/agent/plugin/nodeattestor/test"
"github.com/spiffe/spire/pkg/common/catalog"
"github.com/spiffe/spire/pkg/common/plugin/gcp"
"github.com/spiffe/spire/test/plugintest"
"github.com/spiffe/spire/test/spiretest"
Expand Down Expand Up @@ -66,7 +68,10 @@ func (s *Suite) SetupSuite() {
func (s *Suite) SetupTest() {
s.status = http.StatusOK
s.body = ""
s.na = s.loadPlugin(plugintest.Configuref(`
s.na = s.loadPlugin(plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configuref(`
service_account = "%s"
identity_token_host = "%s"
`, testServiceAccount, s.server.Listener.Addr().String()))
Expand Down Expand Up @@ -111,7 +116,12 @@ func (s *Suite) TestConfigure() {

// malformed
var err error
s.loadPlugin(plugintest.CaptureConfigureError(&err), plugintest.Configure("malformed"))
s.loadPlugin(plugintest.CaptureConfigureError(&err),
plugintest.CoreConfig(catalog.CoreConfig{
TrustDomain: spiffeid.RequireTrustDomainFromString("example.org"),
}),
plugintest.Configure("malformed"),
)
require.Error(err)
}

Expand All @@ -135,7 +145,7 @@ func TestRetrieveIdentity(t *testing.T) {
},
{
msg: "invalid port",
url: "http://0.0.0.0:70000",
url: "http://127.0.0.1:70000",
expectErrContains: "invalid port",
},
{
Expand Down
Loading
Loading