Skip to content

Commit

Permalink
Add possibility to disable SPIFFE cert validation per envoy instance (#…
Browse files Browse the repository at this point in the history
…3014)

Signed-off-by: Anton Kaymakchi <anton.kaymakchi@transferwise.com>
  • Loading branch information
StupidScience authored and azdagron committed May 4, 2022
1 parent ca41ef4 commit c43130a
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 9 deletions.
2 changes: 2 additions & 0 deletions doc/spire_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ support for the [SPIFFE Certificate Validator](https://www.envoyproxy.io/docs/en
extension, which is only available starting with Envoy 1.18.
The default name is configurable (see `default_all_bundles_name` under [SDS Configuration](#sds-configuration).

The [SPIFFE Certificate Validator](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto) configures Envoy to perform SPIFFE authentication. It is used by default but can be disabled by setting `disable_spiffe_cert_validation` to `true` in [SDS Configuration](#sds-configuration). Alternatively, to disable for an individual envoy instance, the `disable_spiffe_cert_validation` key can be configured and set to `true` in the Envoy node metadata. When used, Envoy will perform standard X.509 certificate chain validation.

## OpenShift Support

The default security profile of [OpenShift](https://www.openshift.com/products/container-platform) forbids access to host level resources. A custom set of policies can be applied to enable the level of access needed by Spire to operate within OpenShift.
Expand Down
35 changes: 26 additions & 9 deletions pkg/agent/endpoints/sdsv3/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import (
"google.golang.org/protobuf/types/known/anypb"
)

const (
disableSPIFFECertValidationKey = "disable_spiffe_cert_validation"
)

type Attestor interface {
Attest(ctx context.Context) ([]*common.Selector, error)
}
Expand Down Expand Up @@ -249,15 +253,9 @@ func (h *Handler) buildResponse(versionInfo string, req *discovery_v3.DiscoveryR
}
returnAllEntries := len(names) == 0

// Use RootCA as default, but replace with SPIFFE auth when Envoy version is at least v1.18.0
var builder validationContextBuilder
if !h.c.DisableSPIFFECertValidation && supportsSPIFFEAuthExtension(req) {
builder, err = newSpiffeBuilder(upd.Bundle, upd.FederatedBundles)
if err != nil {
return nil, err
}
} else {
builder = newRootCABuilder(upd.Bundle, upd.FederatedBundles)
builder, err := h.getValidationContextBuilder(req, upd)
if err != nil {
return nil, err
}

// TODO: verify the type url
Expand Down Expand Up @@ -340,6 +338,14 @@ type validationContextBuilder interface {
buildAll(resourceName string) (*any.Any, error)
}

func (h *Handler) getValidationContextBuilder(req *discovery_v3.DiscoveryRequest, upd *cache.WorkloadUpdate) (validationContextBuilder, error) {
if !h.isSPIFFECertValidationDisabled(req) && supportsSPIFFEAuthExtension(req) {
return newSpiffeBuilder(upd.Bundle, upd.FederatedBundles)
}

return newRootCABuilder(upd.Bundle, upd.FederatedBundles), nil
}

type rootCABuilder struct {
bundles map[string]*bundleutil.Bundle
}
Expand Down Expand Up @@ -500,6 +506,17 @@ func supportsSPIFFEAuthExtension(req *discovery_v3.DiscoveryRequest) bool {
return false
}

func (h *Handler) isSPIFFECertValidationDisabled(req *discovery_v3.DiscoveryRequest) bool {
if h.c.DisableSPIFFECertValidation {
return true
}

fields := req.Node.GetMetadata().GetFields()
v, ok := fields[disableSPIFFECertValidationKey]

return ok && (v.GetBoolValue() || v.GetStringValue() == "true")
}

func buildTLSCertificate(identity cache.Identity, defaultSVIDName string) (*anypb.Any, error) {
name := identity.Entry.SpiffeId
if defaultSVIDName != "" {
Expand Down
121 changes: 121 additions & 0 deletions pkg/agent/endpoints/sdsv3/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
)

var (
Expand Down Expand Up @@ -470,6 +471,66 @@ func TestStreamSecrets(t *testing.T) {
},
expectSecrets: []*tls_v3.Secret{tdValidationContext3},
},
{
name: "Disable custom validation per instance",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"default"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewBoolValue(true),
},
},
},
},
expectSecrets: []*tls_v3.Secret{workloadTLSCertificate3},
},
{
name: "Disable SPIFFE cert validation per instance with string value",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"default"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewStringValue("true"),
},
},
},
},
expectSecrets: []*tls_v3.Secret{workloadTLSCertificate3},
},
{
name: "Disable SPIFFE cert validation set to false per instance",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"spiffe://domain.test"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewBoolValue(false),
},
},
},
},
expectSecrets: []*tls_v3.Secret{tdValidationContextSpiffeValidator},
},
{
name: "Disable SPIFFE cert validation set unknown string value",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"spiffe://domain.test"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewStringValue("test"),
},
},
},
},
expectSecrets: []*tls_v3.Secret{tdValidationContextSpiffeValidator},
},
} {
t.Run(tt.name, func(t *testing.T) {
test := setupTestWithConfig(t, tt.config)
Expand Down Expand Up @@ -853,6 +914,66 @@ func TestFetchSecrets(t *testing.T) {
},
expectSecrets: []*tls_v3.Secret{tdValidationContext3},
},
{
name: "Disable custom validation per instance",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"default"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewBoolValue(true),
},
},
},
},
expectSecrets: []*tls_v3.Secret{workloadTLSCertificate3},
},
{
name: "Disable SPIFFE cert validation per instance with string value",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"default"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewStringValue("true"),
},
},
},
},
expectSecrets: []*tls_v3.Secret{workloadTLSCertificate3},
},
{
name: "Disable SPIFFE cert validation set to false per instance",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"spiffe://domain.test"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewBoolValue(false),
},
},
},
},
expectSecrets: []*tls_v3.Secret{tdValidationContextSpiffeValidator},
},
{
name: "Disable SPIFFE cert validation set unknown string value",
req: &discovery_v3.DiscoveryRequest{
ResourceNames: []string{"spiffe://domain.test"},
Node: &core_v3.Node{
UserAgentVersionType: userAgentVersionTypeV18,
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
disableSPIFFECertValidationKey: structpb.NewStringValue("test"),
},
},
},
},
expectSecrets: []*tls_v3.Secret{tdValidationContextSpiffeValidator},
},
} {
t.Run(tt.name, func(t *testing.T) {
test := setupTestWithConfig(t, tt.config)
Expand Down

0 comments on commit c43130a

Please sign in to comment.