Skip to content

Commit

Permalink
fix(MeshTrafficPermission): support permissive mtls (#8171)
Browse files Browse the repository at this point in the history
Signed-off-by: Jakub Dyszkiewicz <[email protected]>
  • Loading branch information
jakubdyszkiewicz authored and kumahq[bot] committed Oct 30, 2023
1 parent a281f78 commit e8576d4
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package v1alpha1
import (
envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"

"github.com/kumahq/kuma/pkg/core"
core_plugins "github.com/kumahq/kuma/pkg/core/plugins"
Expand Down Expand Up @@ -75,6 +76,10 @@ func (p plugin) Apply(rs *core_xds.ResourceSet, ctx xds_context.Context, proxy *
Mesh: proxy.Dataplane.GetMeta().GetMesh(),
}
for _, filterChain := range listener.FilterChains {
if filterChain.TransportSocket.GetName() != wellknown.TransportSocketTLS {
// we only want to configure RBAC on listeners protected by Kuma's TLS
continue
}
if err := configurer.Configure(filterChain); err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
meshtrafficpermission "github.com/kumahq/kuma/pkg/plugins/policies/meshtrafficpermission/plugin/v1alpha1"
"github.com/kumahq/kuma/pkg/test/matchers"
test_model "github.com/kumahq/kuma/pkg/test/resources/model"
"github.com/kumahq/kuma/pkg/test/resources/samples"
util_proto "github.com/kumahq/kuma/pkg/util/proto"
xds_context "github.com/kumahq/kuma/pkg/xds/context"
"github.com/kumahq/kuma/pkg/xds/envoy"
"github.com/kumahq/kuma/pkg/xds/envoy/listeners"
"github.com/kumahq/kuma/pkg/xds/generator"
)

<<<<<<< HEAD
var _ = Describe("Apply", func() {

It("should enrich matching listener with RBAC filter", func() {
Expand All @@ -39,6 +41,113 @@ var _ = Describe("Apply", func() {
Name: listener.GetName(),
Origin: generator.OriginInbound,
Resource: listener,
=======
var _ = Describe("RBAC", func() {
Context("for Dataplane", func() {
It("should enrich matching listener with RBAC filter", func() {
// given
rs := core_xds.NewResourceSet()
ctx := xds_context.Context{
Mesh: xds_context.MeshContext{
Resource: samples.MeshMTLSBuilder().WithName("mesh-1").Build(),
},
}

// listener that matches
listener, err := listeners.NewInboundListenerBuilder(envoy.APIV3, "192.168.0.1", 8080, core_xds.SocketAddressProtocolTCP).
WithOverwriteName("test_listener").
Configure(listeners.FilterChain(listeners.NewFilterChainBuilder(envoy.APIV3, envoy.AnonymousResource).
Configure(listeners.ServerSideMTLS(ctx.Mesh.Resource, envoy.NewSecretsTracker(ctx.Mesh.Resource.Meta.GetName(), nil))).
Configure(listeners.HttpConnectionManager("test_listener", false)))).
Build()
Expect(err).ToNot(HaveOccurred())
rs.Add(&core_xds.Resource{
Name: listener.GetName(),
Origin: generator.OriginInbound,
Resource: listener,
})

// listener that is originated from inbound proxy generator but won't match
listener2, err := listeners.NewInboundListenerBuilder(envoy.APIV3, "192.168.0.1", 8081, core_xds.SocketAddressProtocolTCP).
WithOverwriteName("test_listener2").
Configure(listeners.FilterChain(listeners.NewFilterChainBuilder(envoy.APIV3, envoy.AnonymousResource).
Configure(listeners.ServerSideMTLS(ctx.Mesh.Resource, envoy.NewSecretsTracker(ctx.Mesh.Resource.Meta.GetName(), nil))).
Configure(listeners.HttpConnectionManager("test_listener2", false)))).
Build()
Expect(err).ToNot(HaveOccurred())
rs.Add(&core_xds.Resource{
Name: listener2.GetName(),
Origin: generator.OriginInbound,
Resource: listener2,
})

// listener that matches but is not originated from inbound proxy generator
listener3, err := listeners.NewInboundListenerBuilder(envoy.APIV3, "192.168.0.1", 8082, core_xds.SocketAddressProtocolTCP).
WithOverwriteName("test_listener3").
Configure(listeners.FilterChain(listeners.NewFilterChainBuilder(envoy.APIV3, envoy.AnonymousResource).
Configure(listeners.ServerSideMTLS(ctx.Mesh.Resource, envoy.NewSecretsTracker(ctx.Mesh.Resource.Meta.GetName(), nil))).
Configure(listeners.HttpConnectionManager("test_listener3", false)))).
Build()
Expect(err).ToNot(HaveOccurred())
rs.Add(&core_xds.Resource{
Name: listener3.GetName(),
Origin: "not-inbound-origin",
Resource: listener3,
})

// listener that matches but it does not have mTLS
listener4, err := listeners.NewInboundListenerBuilder(envoy.APIV3, "192.168.0.1", 8083, core_xds.SocketAddressProtocolTCP).
WithOverwriteName("test_listener4").
Configure(listeners.FilterChain(listeners.NewFilterChainBuilder(envoy.APIV3, envoy.AnonymousResource).
Configure(listeners.HttpConnectionManager("test_listener", false)))).
Build()
Expect(err).ToNot(HaveOccurred())
rs.Add(&core_xds.Resource{
Name: listener4.GetName(),
Origin: generator.OriginInbound,
Resource: listener4,
})

proxy := &core_xds.Proxy{
Dataplane: &mesh.DataplaneResource{
Meta: &test_model.ResourceMeta{Name: "dp1", Mesh: "mesh-1"},
},
Policies: core_xds.MatchedPolicies{
Dynamic: map[core_model.ResourceType]core_xds.TypedMatchingPolicies{
policies_api.MeshTrafficPermissionType: {
FromRules: core_rules.FromRules{
Rules: map[core_rules.InboundListener]core_rules.Rules{
{
Address: "192.168.0.1", Port: 8080,
}: {
{
Subset: []core_rules.Tag{
{Key: mesh_proto.ServiceTag, Value: "frontend"},
},
Conf: policies_api.Conf{
Action: "Allow",
},
},
},
},
},
},
},
},
}

// when
p := meshtrafficpermission.NewPlugin().(plugins.PolicyPlugin)
err = p.Apply(rs, ctx, proxy)
Expect(err).ToNot(HaveOccurred())

// then
resp, err := rs.List().ToDeltaDiscoveryResponse()
Expect(err).ToNot(HaveOccurred())
bytes, err := util_proto.ToYAML(resp)
Expect(err).ToNot(HaveOccurred())
Expect(bytes).To(matchers.MatchGoldenYAML(path.Join("testdata", "apply.golden.yaml")))
>>>>>>> 0e0489feb (fix(MeshTrafficPermission): support permissive mtls (#8171))
})

// listener that is originated from inbound proxy generator but won't match
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ resources:
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
statPrefix: test_listener
transportSocket:
name: envoy.transport_sockets.tls
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
commonTlsContext:
combinedValidationContext:
defaultValidationContext:
matchTypedSubjectAltNames:
- matcher:
prefix: spiffe://mesh-1/
sanType: URI
validationContextSdsSecretConfig:
name: mesh_ca:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
tlsCertificateSdsSecretConfigs:
- name: identity_cert:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
name: test_listener
trafficDirection: INBOUND
- name: test_listener2
Expand All @@ -50,6 +72,28 @@ resources:
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
statPrefix: test_listener2
transportSocket:
name: envoy.transport_sockets.tls
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
commonTlsContext:
combinedValidationContext:
defaultValidationContext:
matchTypedSubjectAltNames:
- matcher:
prefix: spiffe://mesh-1/
sanType: URI
validationContextSdsSecretConfig:
name: mesh_ca:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
tlsCertificateSdsSecretConfigs:
- name: identity_cert:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
name: test_listener2
trafficDirection: INBOUND
- name: test_listener3
Expand All @@ -70,5 +114,47 @@ resources:
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
statPrefix: test_listener3
transportSocket:
name: envoy.transport_sockets.tls
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
commonTlsContext:
combinedValidationContext:
defaultValidationContext:
matchTypedSubjectAltNames:
- matcher:
prefix: spiffe://mesh-1/
sanType: URI
validationContextSdsSecretConfig:
name: mesh_ca:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
tlsCertificateSdsSecretConfigs:
- name: identity_cert:secret:mesh-1
sdsConfig:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
name: test_listener3
trafficDirection: INBOUND
- name: test_listener4
resource:
'@type': type.googleapis.com/envoy.config.listener.v3.Listener
address:
socketAddress:
address: 192.168.0.1
portValue: 8083
enableReusePort: false
filterChains:
- filters:
- name: envoy.filters.network.http_connection_manager
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
httpFilters:
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
statPrefix: test_listener
name: test_listener4
trafficDirection: INBOUND
7 changes: 7 additions & 0 deletions pkg/test/resources/builders/mesh_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ func (m *MeshBuilder) WithoutMTLSBackends() *MeshBuilder {
return m
}

func (m *MeshBuilder) WithPermissiveMTLSBackends() *MeshBuilder {
for _, backend := range m.res.Spec.Mtls.Backends {
backend.Mode = mesh_proto.CertificateAuthorityBackend_PERMISSIVE
}
return m
}

func (m *MeshBuilder) AddBuiltinMTLSBackend(name string) *MeshBuilder {
if m.res.Spec.Mtls == nil {
m.res.Spec.Mtls = &mesh_proto.Mesh_Mtls{}
Expand Down
14 changes: 9 additions & 5 deletions pkg/xds/generator/inbound_proxy_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,7 @@ func (g InboundProxyGenerator) Generate(ctx context.Context, xdsCtx xds_context.
Configure(envoy_listeners.ServerSideMTLS(xdsCtx.Mesh.Resource, proxy.SecretsTracker))
}
return filterChainBuilder.
Configure(envoy_listeners.Timeout(defaults_mesh.DefaultInboundTimeout(), protocol)).
Configure(envoy_listeners.NetworkRBAC(inboundListenerName, xdsCtx.Mesh.Resource.MTLSEnabled(),
proxy.Policies.TrafficPermissions[endpoint]))
Configure(envoy_listeners.Timeout(defaults_mesh.DefaultInboundTimeout(), protocol))
}

listenerBuilder := envoy_listeners.NewListenerBuilder(proxy.APIVersion).
Expand All @@ -139,7 +137,9 @@ func (g InboundProxyGenerator) Generate(ctx context.Context, xdsCtx xds_context.
switch xdsCtx.Mesh.Resource.GetEnabledCertificateAuthorityBackend().GetMode() {
case mesh_proto.CertificateAuthorityBackend_STRICT:
listenerBuilder.
Configure(envoy_listeners.FilterChain(filterChainBuilder(true)))
Configure(envoy_listeners.FilterChain(filterChainBuilder(true).Configure(
envoy_listeners.NetworkRBAC(inboundListenerName, xdsCtx.Mesh.Resource.MTLSEnabled(), proxy.Policies.TrafficPermissions[endpoint]),
)))
case mesh_proto.CertificateAuthorityBackend_PERMISSIVE:
listenerBuilder.
Configure(envoy_listeners.TLSInspector()).
Expand All @@ -148,13 +148,17 @@ func (g InboundProxyGenerator) Generate(ctx context.Context, xdsCtx xds_context.
envoy_listeners.MatchTransportProtocol("raw_buffer"))),
).
Configure(envoy_listeners.FilterChain(
// we need to differentiate between just TLS and Kuma's TLS, because with permissive mode
// the app itself might be protected by TLS.
filterChainBuilder(false).Configure(
envoy_listeners.MatchTransportProtocol("tls"))),
).
Configure(envoy_listeners.FilterChain(
filterChainBuilder(true).Configure(
envoy_listeners.MatchTransportProtocol("tls"),
envoy_listeners.MatchApplicationProtocols(xds_tls.KumaALPNProtocols...))),
envoy_listeners.MatchApplicationProtocols(xds_tls.KumaALPNProtocols...),
envoy_listeners.NetworkRBAC(inboundListenerName, xdsCtx.Mesh.Resource.MTLSEnabled(), proxy.Policies.TrafficPermissions[endpoint]),
)),
)
default:
return nil, errors.New("unknown mode for CA backend")
Expand Down
Loading

0 comments on commit e8576d4

Please sign in to comment.