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

Add support for EgressMTLS #1180

Merged
merged 9 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples-of-custom-resources/egress-mtls/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Egress MTLS

In this example, we deploy a web application, configure load balancing for it via a VirtualServer, and apply an Egress MTLS policy.
In this example, we deploy a secure web application, configure load balancing for it via a VirtualServer, and apply an Egress MTLS policy.

## Prerequisites

Expand All @@ -15,15 +15,16 @@ In this example, we deploy a web application, configure load balancing for it vi
```

## Step 1 - Deploy a Secure Web Application
lucacome marked this conversation as resolved.
Show resolved Hide resolved
The application requires clients to use TLS and present a client TLS certificate which it will verify.

Create the application deployment, service and secret:
```
$ kubectl apply -f secure-webapp.yaml
$ kubectl apply -f secure-app.yaml
```

## Step 2 - Deploy the Egress MLTS Secret

Create a secret with the name `egress-mtls-secret` that will be used for authentication to the Secure Web Application:
Create a secret with the name `egress-mtls-secret` that will be used for authentication to application:
```
$ kubectl apply -f egress-mtls-secret.yaml
```
Expand All @@ -49,7 +50,7 @@ Create a VirtualServer resource for the web application:
$ kubectl apply -f virtual-server.yaml
```

Note that the VirtualServer references the policy `egress-mtls-policy` created in Step 3.
Note that the VirtualServer references the policy `egress-mtls-policy` created in Step 4.

## Step 6 - Test the Configuration

Expand Down
2 changes: 1 addition & 1 deletion examples-of-custom-resources/egress-mtls/egress-mtls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ spec:
verifyServer: on
verifyDepth: 2
serverName: on
sslName: "secure-app.example.com"
sslName: secure-app.example.com
4 changes: 3 additions & 1 deletion examples-of-custom-resources/egress-mtls/virtual-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ spec:
- name: secure-app
service: secure-app
port: 8443
tls:
enable: true
routes:
- path: /
policies:
- name: egress-mtls-policy
- name: egress-mtls-policy
action:
pass: secure-app
36 changes: 24 additions & 12 deletions internal/configs/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const WildcardSecretName = "wildcard"
// JWTKeyKey is the key of the data field of a Secret where the JWK must be stored.
const JWTKeyKey = "jwk"

// IngressMTLSKey is the key of the data field of a Secret where the cert must be stored.
const IngressMTLSKey = "ca.crt"
// CAKey is the key of the data field of a Secret where the cert must be stored.
const CAKey = "ca.crt"

// SPIFFE filenames and modes
const (
Expand Down Expand Up @@ -407,8 +407,6 @@ func (cnf *Configurator) addOrUpdateOpenTracingTracerConfig(content string) erro
func (cnf *Configurator) addOrUpdateVirtualServer(virtualServerEx *VirtualServerEx) (Warnings, error) {
var tlsPemFileName string
var ingressMTLSFileName string
var egressMTLSFileName string
var trustedCAFileName string
name := getFileNameForVirtualServer(virtualServerEx.VirtualServer)

if virtualServerEx.TLSSecret != nil {
Expand All @@ -417,17 +415,12 @@ func (cnf *Configurator) addOrUpdateVirtualServer(virtualServerEx *VirtualServer
if virtualServerEx.IngressMTLSCert != nil {
ingressMTLSFileName = cnf.addOrUpdateCASecret(virtualServerEx.IngressMTLSCert)
}
if virtualServerEx.TrustedCASecret != nil {
trustedCAFileName = cnf.addOrUpdateCASecret(virtualServerEx.TrustedCASecret)
}
if virtualServerEx.EgressTLSSecret != nil {
egressMTLSFileName = cnf.addOrUpdateTLSSecret(virtualServerEx.EgressTLSSecret)
}

jwtKeys := cnf.addOrUpdateJWKSecretsForVirtualServer(virtualServerEx.JWTKeys)
egressMTLSSecrets := cnf.addOrUpdateEgressMTLSecretsForVirtualServer(virtualServerEx.EgressTLSSecrets)

vsc := newVirtualServerConfigurator(cnf.cfgParams, cnf.isPlus, cnf.IsResolverConfigured(), cnf.staticCfgParams)
vsCfg, warnings := vsc.GenerateVirtualServerConfig(virtualServerEx, tlsPemFileName, jwtKeys, ingressMTLSFileName, egressMTLSFileName, trustedCAFileName)
vsCfg, warnings := vsc.GenerateVirtualServerConfig(virtualServerEx, tlsPemFileName, jwtKeys, ingressMTLSFileName, egressMTLSSecrets)
content, err := cnf.templateExecutorV2.ExecuteVirtualServerTemplate(&vsCfg)
if err != nil {
return warnings, fmt.Errorf("Error generating VirtualServer config: %v: %v", name, err)
Expand Down Expand Up @@ -668,6 +661,25 @@ func (cnf *Configurator) addOrUpdateJWKSecretsForVirtualServer(jwtKeys map[strin
return jwkSecrets
}

func (cnf *Configurator) addOrUpdateEgressMTLSecretsForVirtualServer(egressMTLSsecrets map[string]*api_v1.Secret) map[string]string {

secrets := make(map[string]string)
var filename string

for v, k := range egressMTLSsecrets {
if _, exists := k.Data[api_v1.TLSCertKey]; exists {
filename = cnf.addOrUpdateTLSSecret(k)
}
if _, exists := k.Data[CAKey]; exists {
filename = cnf.addOrUpdateCASecret(k)

}
secrets[v] = filename
}

return secrets
}

// AddOrUpdateResources adds or updates configuration for resources.
func (cnf *Configurator) AddOrUpdateResources(ingExes []*IngressEx, mergeableIngresses []*MergeableIngresses, virtualServerExes []*VirtualServerEx) (Warnings, error) {
allWarnings := newWarnings()
Expand Down Expand Up @@ -743,7 +755,7 @@ func GenerateCertAndKeyFileContent(secret *api_v1.Secret) []byte {
func GenerateCAFileContent(secret *api_v1.Secret) []byte {
var res bytes.Buffer

res.Write(secret.Data[IngressMTLSKey])
res.Write(secret.Data[CAKey])

return res.Bytes()
}
Expand Down
33 changes: 14 additions & 19 deletions internal/configs/virtualserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type VirtualServerEx struct {
TLSSecret *api_v1.Secret
JWTKeys map[string]*api_v1.Secret
IngressMTLSCert *api_v1.Secret
EgressTLSSecret *api_v1.Secret
EgressTLSSecrets map[string]*api_v1.Secret
TrustedCASecret *api_v1.Secret
lucacome marked this conversation as resolved.
Show resolved Hide resolved
VirtualServerRoutes []*conf_v1.VirtualServerRoute
ExternalNameSvcs map[string]bool
Expand Down Expand Up @@ -216,11 +216,11 @@ func (vsc *virtualServerConfigurator) generateEndpointsForUpstream(owner runtime
}

// GenerateVirtualServerConfig generates a full configuration for a VirtualServer
func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualServerEx, tlsPemFileName string, jwtKeys map[string]string, ingressMTLSPemFileName string, egressMTLSPemFileName string, trustedCAFileName string) (version2.VirtualServerConfig, Warnings) {
func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualServerEx, tlsPemFileName string, jwtKeys map[string]string, ingressMTLSPemFileName string, egressMTLSSecrets map[string]string) (version2.VirtualServerConfig, Warnings) {
vsc.clearWarnings()

policiesCfg := vsc.generatePolicies(vsEx.VirtualServer, vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Namespace,
vsEx.VirtualServer.Name, vsEx.VirtualServer.Spec.Policies, vsEx.Policies, jwtKeys, ingressMTLSPemFileName, specContext, tlsPemFileName, egressMTLSPemFileName, trustedCAFileName)
vsEx.VirtualServer.Name, vsEx.VirtualServer.Spec.Policies, vsEx.Policies, jwtKeys, ingressMTLSPemFileName, specContext, tlsPemFileName, egressMTLSSecrets)

// crUpstreams maps an UpstreamName to its conf_v1.Upstream as they are generated
// necessary for generateLocation to know what Upstream each Location references
Expand Down Expand Up @@ -327,7 +327,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualS
vsLocSnippets := r.LocationSnippets
// ingressMTLSPemFileName argument is always empty for route policies
routePoliciesCfg := vsc.generatePolicies(vsEx.VirtualServer, vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name,
r.Policies, vsEx.Policies, jwtKeys, "", routeContext, tlsPemFileName, egressMTLSPemFileName, trustedCAFileName)
r.Policies, vsEx.Policies, jwtKeys, "", routeContext, tlsPemFileName, egressMTLSSecrets)
limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...)

if len(r.Matches) > 0 {
Expand All @@ -353,9 +353,6 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualS
} else {
upstreamName := virtualServerUpstreamNamer.GetNameForUpstreamFromAction(r.Action)
upstream := crUpstreams[upstreamName]
if routePoliciesCfg.EgressMTLS != nil {
upstream.TLS.Enable = true
}

proxySSLName := generateProxySSLName(upstream.Service, vsEx.VirtualServer.Namespace)

Expand Down Expand Up @@ -393,11 +390,11 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig(vsEx *VirtualS
}
// ingressMTLSPemFileName argument is always empty for route policies
routePoliciesCfg := vsc.generatePolicies(vsr, vsr.Namespace, vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Name,
r.Policies, vsEx.Policies, jwtKeys, "", subRouteContext, tlsPemFileName, egressMTLSPemFileName, trustedCAFileName)
r.Policies, vsEx.Policies, jwtKeys, "", subRouteContext, tlsPemFileName, egressMTLSSecrets)
// use the VirtualServer route policies if the route does not define any
if len(r.Policies) == 0 {
routePoliciesCfg = vsc.generatePolicies(vsEx.VirtualServer, vsEx.VirtualServer.Namespace, vsEx.VirtualServer.Namespace,
vsEx.VirtualServer.Name, vsrPoliciesFromVs[vsrNamespaceName], vsEx.Policies, jwtKeys, "", routeContext, tlsPemFileName, egressMTLSPemFileName, trustedCAFileName)
vsEx.VirtualServer.Name, vsrPoliciesFromVs[vsrNamespaceName], vsEx.Policies, jwtKeys, "", routeContext, tlsPemFileName, egressMTLSSecrets)
}
limitReqZones = append(limitReqZones, routePoliciesCfg.LimitReqZones...)

Expand Down Expand Up @@ -495,7 +492,7 @@ type policiesCfg struct {

// TODO refactor generatePolicies
func (vsc *virtualServerConfigurator) generatePolicies(owner runtime.Object, ownerNamespace string, vsNamespace string,
vsName string, policyRefs []conf_v1.PolicyReference, policies map[string]*conf_v1alpha1.Policy, jwtKeys map[string]string, ingressMTLSPemFileName string, context string, tlsPemFileName string, egressMTLSPemFileName string, trustedCAFileName string) policiesCfg {
vsName string, policyRefs []conf_v1.PolicyReference, policies map[string]*conf_v1alpha1.Policy, jwtKeys map[string]string, ingressMTLSPemFileName string, context string, tlsPemFileName string, egressMTLSSecrets map[string]string) policiesCfg {
var policyErrorReturn *version2.Return
var allow, deny []string
var limitReqOptions version2.LimitReqOptions
Expand Down Expand Up @@ -595,26 +592,24 @@ func (vsc *virtualServerConfigurator) generatePolicies(owner runtime.Object, own
}

} else if pol.Spec.EgressMTLS != nil {
lucacome marked this conversation as resolved.
Show resolved Hide resolved
if context != routeContext {
vsc.addWarningf(owner, `EgressMTLS policy is not allowed in the %v context`, context)
policyError = true
break
}
if egressMTLS != nil {
vsc.addWarningf(owner, "Multiple egressMTLS policies are not allowed. EgressMTLS policy %q will be ignored", key)
vsc.addWarningf(owner, "Multiple egressMTLS policies in the same context is not valid. EgressMTLS policy %q will be ignored", key)
continue
}

egressMTLSPemFileName := fmt.Sprintf("%v/%v", polNamespace, pol.Spec.EgressMTLS.TLSSecret)
lucacome marked this conversation as resolved.
Show resolved Hide resolved
trustedCAFileName := fmt.Sprintf("%v/%v", polNamespace, pol.Spec.EgressMTLS.TrustedCertSecret)

egressMTLS = &version2.EgressMTLS{
Certificate: generateString(egressMTLSPemFileName, ""),
CertificateKey: generateString(egressMTLSPemFileName, ""),
Certificate: egressMTLSSecrets[egressMTLSPemFileName],
CertificateKey: egressMTLSSecrets[egressMTLSPemFileName],
Ciphers: generateString(pol.Spec.EgressMTLS.Ciphers, "DEFAULT"),
Protocols: generateString(pol.Spec.EgressMTLS.Protocols, "TLSv1 TLSv1.1 TLSv1.2"),
VerifyServer: generateBool(pol.Spec.EgressMTLS.VerifyServer, false),
VerifyDepth: generateIntFromPointer(pol.Spec.EgressMTLS.VerifyDepth, 1),
SessionReuse: generateBool(pol.Spec.EgressMTLS.SessionReuse, true),
ServerName: generateBool(pol.Spec.EgressMTLS.ServerName, false),
TrustedCert: generateString(trustedCAFileName, ""),
TrustedCert: egressMTLSSecrets[trustedCAFileName],
SSLName: generateString(pol.Spec.EgressMTLS.SSLName, "$proxy_host"),
}

Expand Down
Loading