Skip to content

Commit

Permalink
feat: downstream mTLS
Browse files Browse the repository at this point in the history
Relates to envoyproxy#2483

Signed-off-by: Arko Dasgupta <arko@tetrate.io>
  • Loading branch information
arkodg committed Jan 23, 2024
1 parent 45c03a2 commit 93ad32f
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 6 deletions.
11 changes: 11 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ const (
type TLSConfig struct {
// Certificates contains the set of certificates associated with this listener
Certificates []TLSCertificate `json:"certificates,omitempty" yaml:"certificates,omitempty"`
// CACertificate to verify the client
CACertificate *TLSCACertificate `json:"caCertificate,omitempty" yaml:"caCertificate,omitempty"`
// MinVersion defines the minimal version of the TLS protocol supported by this listener.
MinVersion *TLSVersion `json:"minVersion,omitempty" yaml:"version,omitempty"`
// MaxVersion defines the maximal version of the TLS protocol supported by this listener.
Expand All @@ -283,6 +285,15 @@ type TLSCertificate struct {
PrivateKey []byte `json:"privateKey,omitempty" yaml:"privateKey,omitempty"`
}

// TLSCACertificate holds CA Certificate to validate clients
// +k8s:deepcopy-gen=true
type TLSCACertificate struct {
// Name of the Secret object.
Name string `json:"name" yaml:"name"`
// Certificate content.
Certificate []byte `json:"certificate,omitempty" yaml:"certificate,omitempty"`
}

func (t TLSCertificate) Validate() error {
var errs error
if len(t.ServerCertificate) == 0 {
Expand Down
25 changes: 25 additions & 0 deletions internal/ir/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 34 additions & 3 deletions internal/xds/translator/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@ func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig) (*corev3.Transp
TlsParams: buildTLSParams(tlsConfig),
AlpnProtocols: buildALPNProtocols(tlsConfig.ALPNProtocols),
},
RequireClientCertificate: &wrappers.BoolValue{Value: false},
},
}

Expand All @@ -359,6 +358,16 @@ func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig) (*corev3.Transp
})
}

if tlsConfig.CACertificate != nil {
tlsCtx.DownstreamTlsContext.RequireClientCertificate = &wrappers.BoolValue{Value: true}
tlsCtx.DownstreamTlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{
ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{
Name: tlsConfig.CACertificate.Name,
SdsConfig: makeConfigSource(),
},
}
}

tlsCtxAny, err := anypb.New(tlsCtx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -389,6 +398,16 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock
})
}

if tlsConfig.CACertificate != nil {
tlsCtx.RequireClientCertificate = &wrappers.BoolValue{Value: true}
tlsCtx.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContextSdsSecretConfig{
ValidationContextSdsSecretConfig: &tlsv3.SdsSecretConfig{
Name: tlsConfig.CACertificate.Name,
SdsConfig: makeConfigSource(),
},
}
}

tlsCtxAny, err := anypb.New(tlsCtx)
if err != nil {
return nil, err
Expand Down Expand Up @@ -451,8 +470,7 @@ func buildALPNProtocols(alpn []string) []string {
return alpn
}

func buildXdsDownstreamTLSSecret(tlsConfig ir.TLSCertificate) *tlsv3.Secret {
// Build the tls secret
func buildXdsTLSCertSecret(tlsConfig ir.TLSCertificate) *tlsv3.Secret {
return &tlsv3.Secret{
Name: tlsConfig.Name,
Type: &tlsv3.Secret_TlsCertificate{
Expand All @@ -468,6 +486,19 @@ func buildXdsDownstreamTLSSecret(tlsConfig ir.TLSCertificate) *tlsv3.Secret {
}
}

func buildXdsTLSCACertSecret(CACertificate *ir.TLSCACertificate) *tlsv3.Secret {
return &tlsv3.Secret{
Name: CACertificate.Name,
Type: &tlsv3.Secret_ValidationContext{
ValidationContext: &tlsv3.CertificateValidationContext{
TrustedCa: &corev3.DataSource{
Specifier: &corev3.DataSource_InlineBytes{InlineBytes: CACertificate.Certificate},
},
},
},
}
}

func buildXdsUDPListener(clusterName string, udpListener *ir.UDPListener, accesslog *ir.AccessLog) (*listenerv3.Listener, error) {
if udpListener == nil {
return nil, errors.New("udp listener is nil")
Expand Down
34 changes: 34 additions & 0 deletions internal/xds/translator/testdata/in/xds-ir/mutual-tls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
http:
- name: "first-listener"
address: "0.0.0.0"
port: 10080
hostnames:
- "*"
path:
mergeSlashes: true
escapedSlashesAction: UnescapeAndRedirect
tls:
alpnProtocols:
- h2
- http/1.1
certificates:
- name: secret-1
# byte slice representation of "key-data"
serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97]
# byte slice representation of "key-data"
privateKey: [107, 101, 121, 45, 100, 97, 116, 97]
- name: secret-2
serverCertificate: [99, 101, 114, 116, 45, 100, 97, 116, 97]
privateKey: [107, 101, 121, 45, 100, 97, 116, 97]
caCertificate:
name: ca-cert
certificate: [99, 101, 114, 116, 45, 100, 97, 116, 97]
routes:
- name: "first-route"
hostname: "*"
destination:
name: "first-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
requireClientCertificate: false
name: envoy-gateway/gateway-1/tls-quic
udpListenerConfig:
downstreamSocketConfig: {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
- commonLbConfig:
localityWeightedLbConfig: {}
connectTimeout: 10s
dnsLookupFamily: V4_ONLY
edsClusterConfig:
edsConfig:
ads: {}
resourceApiVersion: V3
serviceName: first-route-dest
lbPolicy: LEAST_REQUEST
name: first-route-dest
outlierDetection: {}
perConnectionBufferLimitBytes: 32768
type: EDS
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- clusterName: first-route-dest
endpoints:
- lbEndpoints:
- endpoint:
address:
socketAddress:
address: 1.2.3.4
portValue: 50000
loadBalancingWeight: 1
loadBalancingWeight: 1
locality:
region: first-route-dest/backend/0
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
- address:
socketAddress:
address: 0.0.0.0
portValue: 10080
filterChains:
- filters:
- name: envoy.filters.network.http_connection_manager
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
commonHttpProtocolOptions:
headersWithUnderscoresAction: REJECT_REQUEST
http2ProtocolOptions:
initialConnectionWindowSize: 1048576
initialStreamWindowSize: 65536
maxConcurrentStreams: 100
httpFilters:
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
mergeSlashes: true
normalizePath: true
pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
rds:
configSource:
ads: {}
resourceApiVersion: V3
routeConfigName: first-listener
statPrefix: https
upgradeConfigs:
- upgradeType: websocket
useRemoteAddress: true
transportSocket:
name: envoy.transport_sockets.tls
typedConfig:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
commonTlsContext:
alpnProtocols:
- h2
- http/1.1
tlsCertificateSdsSecretConfigs:
- name: secret-1
sdsConfig:
ads: {}
resourceApiVersion: V3
- name: secret-2
sdsConfig:
ads: {}
resourceApiVersion: V3
validationContextSdsSecretConfig:
name: ca-cert
sdsConfig:
ads: {}
resourceApiVersion: V3
requireClientCertificate: true
name: first-listener
perConnectionBufferLimitBytes: 32768
12 changes: 12 additions & 0 deletions internal/xds/translator/testdata/out/xds-ir/mutual-tls.routes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- ignorePortInHostMatching: true
name: first-listener
virtualHosts:
- domains:
- '*'
name: first-listener/*
routes:
- match:
prefix: /
name: first-route
route:
cluster: first-route-dest
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
- name: secret-1
tlsCertificate:
certificateChain:
inlineBytes: Y2VydC1kYXRh
privateKey:
inlineBytes: a2V5LWRhdGE=
- name: secret-2
tlsCertificate:
certificateChain:
inlineBytes: Y2VydC1kYXRh
privateKey:
inlineBytes: a2V5LWRhdGE=
- name: ca-cert
validationContext:
trustedCa:
inlineBytes: Y2VydC1kYXRh
17 changes: 15 additions & 2 deletions internal/xds/translator/translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,18 @@ func (t *Translator) processHTTPListenerXdsTranslation(
// 1:1 between IR TLSListenerConfig and xDS Secret
if httpListener.TLS != nil {
for t := range httpListener.TLS.Certificates {
secret := buildXdsDownstreamTLSSecret(httpListener.TLS.Certificates[t])
secret := buildXdsTLSCertSecret(httpListener.TLS.Certificates[t])
if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil {
errs = errors.Join(errs, err)
}
}

if httpListener.TLS.CACertificate != nil {
caSecret := buildXdsTLSCACertSecret(httpListener.TLS.CACertificate)
if err := tCtx.AddXdsResource(resourcev3.SecretType, caSecret); err != nil {
errs = errors.Join(errs, err)
}
}
}

// store virtual hosts by domain
Expand Down Expand Up @@ -357,11 +364,17 @@ func processTCPListenerXdsTranslation(tCtx *types.ResourceVersionTable, tcpListe

if tcpListener.TLS != nil && tcpListener.TLS.Terminate != nil {
for _, s := range tcpListener.TLS.Terminate.Certificates {
secret := buildXdsDownstreamTLSSecret(s)
secret := buildXdsTLSCertSecret(s)
if err := tCtx.AddXdsResource(resourcev3.SecretType, secret); err != nil {
errs = errors.Join(errs, err)
}
}
if tcpListener.TLS.Terminate.CACertificate != nil {
caSecret := buildXdsTLSCACertSecret(tcpListener.TLS.Terminate.CACertificate)
if err := tCtx.AddXdsResource(resourcev3.SecretType, caSecret); err != nil {
errs = errors.Join(errs, err)
}
}
}
}
return errs
Expand Down
4 changes: 4 additions & 0 deletions internal/xds/translator/translator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ func TestTranslateXds(t *testing.T) {
name: "simple-tls",
requireSecrets: true,
},
{
name: "mutual-tls",
requireSecrets: true,
},
{
name: "http3",
requireSecrets: true,
Expand Down

0 comments on commit 93ad32f

Please sign in to comment.