Skip to content

Commit

Permalink
feat(kuma-cp) permissive mTLS mode (#2579)
Browse files Browse the repository at this point in the history
Signed-off-by: Ilya Lobkov <lobkovilya@yandex.ru>
  • Loading branch information
lobkovilya authored Aug 23, 2021
1 parent fd4bdd2 commit 46d6a19
Show file tree
Hide file tree
Showing 33 changed files with 2,073 additions and 139 deletions.
323 changes: 198 additions & 125 deletions api/mesh/v1alpha1/mesh.pb.go

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions api/mesh/v1alpha1/mesh.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ message CertificateAuthorityBackend {

// Configuration of the backend
google.protobuf.Struct conf = 4;

enum Mode {
// A STRICT mode implies that the server validates the connection and
// accepts only encrypted TLS traffic
STRICT = 0;
// A PERMISSIVE mode implies that the outbounds encrypt traffic the same way
// it happens in strict mode, but inbounds accept both TLS and plaintext
// traffic. This allows applications residing in the mesh to accept requests
// from outside of the mesh.
PERMISSIVE = 1;
}

// Mode defines the behaviour of inbound listeners with regard to traffic
// encryption
Mode mode = 5;
}

// Networking defines the networking configuration of the mesh
Expand Down
8 changes: 8 additions & 0 deletions pkg/xds/envoy/clusters/v3/client_side_mtls_configurer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ var _ = Describe("EdsClusterConfigurer", func() {
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
commonTlsContext:
alpnProtocols:
- kuma
combinedValidationContext:
defaultValidationContext:
matchSubjectAltNames:
Expand Down Expand Up @@ -145,6 +147,8 @@ var _ = Describe("EdsClusterConfigurer", func() {
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
commonTlsContext:
alpnProtocols:
- kuma
combinedValidationContext:
defaultValidationContext:
matchSubjectAltNames:
Expand All @@ -168,6 +172,8 @@ var _ = Describe("EdsClusterConfigurer", func() {
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
commonTlsContext:
alpnProtocols:
- kuma
combinedValidationContext:
defaultValidationContext:
matchSubjectAltNames:
Expand Down Expand Up @@ -229,6 +235,8 @@ var _ = Describe("EdsClusterConfigurer", func() {
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
commonTlsContext:
alpnProtocols:
- kuma
combinedValidationContext:
defaultValidationContext:
matchSubjectAltNames:
Expand Down
7 changes: 4 additions & 3 deletions pkg/xds/envoy/listeners/filter_chain_configurers.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ func HttpConnectionManager(statsName string, forwardClientCertDetails bool) Filt
})
}

func FilterChainMatch(transport string, serverNames ...string) FilterChainBuilderOpt {
func FilterChainMatch(transport string, serverNames, applicationProtocols []string) FilterChainBuilderOpt {
return AddFilterChainConfigurer(&v3.FilterChainMatchConfigurer{
ServerNames: serverNames,
TransportProtocol: transport,
ServerNames: serverNames,
TransportProtocol: transport,
ApplicationProtocols: applicationProtocols,
})
}

Expand Down
8 changes: 6 additions & 2 deletions pkg/xds/envoy/listeners/v3/filter_chain_match_configurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package v3
import envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"

type FilterChainMatchConfigurer struct {
ServerNames []string
TransportProtocol string
ServerNames []string
TransportProtocol string
ApplicationProtocols []string
}

func (f *FilterChainMatchConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
Expand All @@ -14,5 +15,8 @@ func (f *FilterChainMatchConfigurer) Configure(filterChain *envoy_listener.Filte
if f.TransportProtocol != "" {
filterChain.FilterChainMatch.TransportProtocol = f.TransportProtocol
}
if len(f.ApplicationProtocols) != 0 {
filterChain.FilterChainMatch.ApplicationProtocols = f.ApplicationProtocols
}
return nil
}
5 changes: 5 additions & 0 deletions pkg/xds/envoy/tls/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const (
IdentityCertResource = "identity_cert"
)

// KumaALPNProtocols are set for UpstreamTlsContext to show that mTLS is created by mesh.
// On the inbound side we have to distinguish Kuma mTLS and application TLS to properly
// support PERMISSIVE mode
var KumaALPNProtocols = []string{"kuma"}

func MeshSpiffeIDPrefix(mesh string) string {
return fmt.Sprintf("spiffe://%s/", mesh)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/xds/envoy/tls/v3/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func CreateUpstreamTlsContext(ctx xds_context.Context, upstreamService string, s
if err != nil {
return nil, err
}
commonTlsContext.AlpnProtocols = xds_tls.KumaALPNProtocols
return &envoy_tls.UpstreamTlsContext{
CommonTlsContext: commonTlsContext,
Sni: sni,
Expand Down
2 changes: 2 additions & 0 deletions pkg/xds/envoy/tls/v3/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ var _ = Describe("CreateUpstreamTlsContext()", func() {
upstreamService: "backend",
expected: `
commonTlsContext:
alpnProtocols:
- kuma
combinedValidationContext:
defaultValidationContext:
matchSubjectAltNames:
Expand Down
2 changes: 1 addition & 1 deletion pkg/xds/generator/admin_proxy_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (g AdminProxyGenerator) Generate(ctx xds_context.Context, proxy *core_xds.P
se.HeaderExactMatch = fmt.Sprintf("Bearer %s", token)
}
filterChains = append(filterChains, envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion).
Configure(envoy_listeners.FilterChainMatch("tls")).
Configure(envoy_listeners.FilterChainMatch("tls", nil, nil)).
Configure(envoy_listeners.StaticTlsEndpoints(envoy_names.GetAdminListenerName(), ctx.ControlPlane.AdminProxyKeyPair, staticTlsEndpointPaths)),
))
}
Expand Down
36 changes: 29 additions & 7 deletions pkg/xds/generator/inbound_proxy_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
envoy_names "github.com/kumahq/kuma/pkg/xds/envoy/names"
v3 "github.com/kumahq/kuma/pkg/xds/envoy/routes/v3"
"github.com/kumahq/kuma/pkg/xds/envoy/tags"
xds_tls "github.com/kumahq/kuma/pkg/xds/envoy/tls"
)

// OriginInbound is a marker to indicate by which ProxyGenerator resources were generated.
Expand Down Expand Up @@ -65,7 +66,7 @@ func (g InboundProxyGenerator) Generate(ctx xds_context.Context, proxy *model.Pr
// generate LDS resource
service := iface.GetService()
inboundListenerName := envoy_names.GetInboundListenerName(endpoint.DataplaneIP, endpoint.DataplanePort)
filterChainBuilder := func() *envoy_listeners.FilterChainBuilder {
filterChainBuilder := func(serverSideMTLS bool) *envoy_listeners.FilterChainBuilder {
filterChainBuilder := envoy_listeners.NewFilterChainBuilder(proxy.APIVersion)
switch protocol {
// configuration for HTTP case
Expand Down Expand Up @@ -94,15 +95,36 @@ func (g InboundProxyGenerator) Generate(ctx xds_context.Context, proxy *model.Pr
// configuration for non-HTTP cases
filterChainBuilder.Configure(envoy_listeners.TcpProxy(localClusterName, envoy_common.NewCluster(envoy_common.WithService(localClusterName))))
}
if serverSideMTLS {
filterChainBuilder.
Configure(envoy_listeners.ServerSideMTLS(ctx))
}
return filterChainBuilder.
Configure(envoy_listeners.ServerSideMTLS(ctx)).
Configure(envoy_listeners.NetworkRBAC(inboundListenerName, ctx.Mesh.Resource.MTLSEnabled(), proxy.Policies.TrafficPermissions[endpoint]))
}()
inboundListener, err := envoy_listeners.NewListenerBuilder(proxy.APIVersion).
}

listenerBuilder := envoy_listeners.NewListenerBuilder(proxy.APIVersion).
Configure(envoy_listeners.InboundListener(inboundListenerName, endpoint.DataplaneIP, endpoint.DataplanePort, model.SocketAddressProtocolTCP)).
Configure(envoy_listeners.FilterChain(filterChainBuilder)).
Configure(envoy_listeners.TransparentProxying(proxy.Dataplane.Spec.Networking.GetTransparentProxying())).
Build()
Configure(envoy_listeners.TLSInspector()).
Configure(envoy_listeners.TransparentProxying(proxy.Dataplane.Spec.Networking.GetTransparentProxying()))

switch ctx.Mesh.Resource.GetEnabledCertificateAuthorityBackend().GetMode() {
case mesh_proto.CertificateAuthorityBackend_STRICT:
listenerBuilder.
Configure(envoy_listeners.FilterChain(filterChainBuilder(true)))
case mesh_proto.CertificateAuthorityBackend_PERMISSIVE:
listenerBuilder.
Configure(envoy_listeners.FilterChain(filterChainBuilder(false).
Configure(envoy_listeners.FilterChainMatch("raw_buffer", nil, nil)))).
Configure(envoy_listeners.FilterChain(filterChainBuilder(false).
Configure(envoy_listeners.FilterChainMatch("tls", nil, nil)))).
Configure(envoy_listeners.FilterChain(filterChainBuilder(true).
Configure(envoy_listeners.FilterChainMatch("tls", nil, xds_tls.KumaALPNProtocols))))
default:
return nil, errors.New("unknown mode for CA backend")
}

inboundListener, err := listenerBuilder.Build()
if err != nil {
return nil, errors.Wrapf(err, "%s: could not generate listener %s", validators.RootedAt("dataplane").Field("networking").Field("inbound").Index(i), inboundListenerName)
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/xds/generator/inbound_proxy_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var _ = Describe("InboundProxyGenerator", func() {
type testCase struct {
dataplaneFile string
expected string
mode mesh_proto.CertificateAuthorityBackend_Mode
}

DescribeTable("Generate Envoy xDS resources",
Expand All @@ -48,6 +49,7 @@ var _ = Describe("InboundProxyGenerator", func() {
{
Name: "builtin",
Type: "builtin",
Mode: given.mode,
},
},
},
Expand Down Expand Up @@ -235,5 +237,15 @@ var _ = Describe("InboundProxyGenerator", func() {
dataplaneFile: "4-dataplane.input.yaml",
expected: "4-envoy-config.golden.yaml",
}),
Entry("05. transparent_proxying=false, ip_addresses=2, ports=2, mode=permissive", testCase{
dataplaneFile: "5-dataplane.input.yaml",
expected: "5-envoy-config.golden.yaml",
mode: mesh_proto.CertificateAuthorityBackend_PERMISSIVE,
}),
Entry("06. transparent_proxying=true, ip_addresses=2, ports=2, mode=permissive", testCase{
dataplaneFile: "6-dataplane.input.yaml",
expected: "6-envoy-config.golden.yaml",
mode: mesh_proto.CertificateAuthorityBackend_PERMISSIVE,
}),
)
})
2 changes: 1 addition & 1 deletion pkg/xds/generator/ingress_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (i IngressGenerator) generateLDS(
sniUsed[sni] = true
inboundListenerBuilder = inboundListenerBuilder.
Configure(envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(apiVersion).
Configure(envoy_listeners.FilterChainMatch("tls", sni)).
Configure(envoy_listeners.FilterChainMatch("tls", []string{sni}, nil)).
Configure(envoy_listeners.TcpProxyWithMetadata(service, envoy_common.NewCluster(
envoy_common.WithService(service),
envoy_common.WithTags(meshDestination.WithoutTags(mesh_proto.ServiceTag)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.1:443
trafficDirection: INBOUND
- name: inbound:192.168.0.1:80
Expand Down Expand Up @@ -218,6 +222,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.1:80
trafficDirection: INBOUND
- name: inbound:192.168.0.2:443
Expand Down Expand Up @@ -259,6 +267,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.2:443
trafficDirection: INBOUND
- name: inbound:192.168.0.2:80
Expand Down Expand Up @@ -319,5 +331,9 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.2:80
trafficDirection: INBOUND
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.1:443
trafficDirection: INBOUND
- name: inbound:192.168.0.1:80
Expand Down Expand Up @@ -220,6 +224,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.1:80
trafficDirection: INBOUND
- name: inbound:192.168.0.2:443
Expand Down Expand Up @@ -262,6 +270,10 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.2:443
trafficDirection: INBOUND
- name: inbound:192.168.0.2:80
Expand Down Expand Up @@ -323,5 +335,9 @@ resources:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
listenerFilters:
- name: envoy.filters.listener.tls_inspector
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
name: inbound:192.168.0.2:80
trafficDirection: INBOUND
23 changes: 23 additions & 0 deletions pkg/xds/generator/testdata/inbound-proxy/5-dataplane.input.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
networking:
address: 192.168.0.1
inbound:
- port: 80
servicePort: 8080
tags:
kuma.io/service: backend1
kuma.io/protocol: http
- port: 443
servicePort: 8443
tags:
kuma.io/service: backend2
- address: 192.168.0.2
port: 80
servicePort: 8080
tags:
kuma.io/service: backend3
kuma.io/protocol: http
- address: 192.168.0.2
port: 443
servicePort: 8443
tags:
kuma.io/service: backend4
Loading

0 comments on commit 46d6a19

Please sign in to comment.