Update egress gateway configuration for wildcard hosts with SNI handling filters (#3058)

* add a tab section about mTLS

* remove leftover ";done"

* remove SNI monitoring and policy enforcement section

* add explanation why mTLS between sidecars and egress gateways is needed

* add mTLS enabled/disabled tabs to the egress MongoDB blog post

* remove placeholder SNI in logs

* add forward_downstream_sni and sni_verifier filters for wildcard TLS hosts

* add a required empty line

* make the sentence about enabling mTLS a note

* add inline comment in the yamls regarding the SNI filters

* a couple of filters -> Envoy filters

* rewrite the sentence why the SNI filters are used

* fix "so that policies will be enforced based on the original SNI value"

* prevents a possibility for deceiving Mixer -> prevents Mixer from being deceived

* will not match -> does not match

* make note ('>') one line to make lint happy
This commit is contained in:
Vadim Eisenberg 2019-01-14 19:56:47 +02:00 committed by istio-bot
parent b6fd2ecb36
commit 9f468b8a8e
2 changed files with 198 additions and 154 deletions

View File

@ -590,7 +590,11 @@ to be 443. The egress gateway accepts the MongoDB traffic on the port 443, match
the egress gateway monitor the identity of the source pods and to enable Mixer policy enforcement based on that
identity.)
{{< text bash >}}
{{< tabset cookie-name="mtls" >}}
{{% tab name="mTLS enabled" cookie-value="enabled" %}}
{{< text bash >}}
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
@ -665,11 +669,13 @@ to be 443. The egress gateway accepts the MongoDB traffic on the port 443, match
number: $MONGODB_PORT
weight: 100
EOF
{{< /text >}}
{{< /text >}}
otherwise:
{{% /tab %}}
{{< text bash >}}
{{% tab name="mTLS disabled" cookie-value="disabled" %}}
{{< text bash >}}
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
@ -733,7 +739,11 @@ to be 443. The egress gateway accepts the MongoDB traffic on the port 443, match
number: $MONGODB_PORT
weight: 100
EOF
{{< /text >}}
{{< /text >}}
{{% /tab %}}
{{< /tabset >}}
1. Refresh the web page of the application again and verify that the ratings are still displayed correctly.
@ -936,8 +946,9 @@ to hold the configuration of the Nginx SNI proxy:
{{< /text >}}
1. Create an egress `Gateway` for _*.com_, port 443, protocol TLS, a destination rule to set the
[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) for the gateway, and a virtual service to direct the
traffic destined for _*.com_ to the gateway.
[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) for the gateway, and Envoy filters to prevent tampering
with SNI by a malicious application (the filters verify that the SNI issued by the application is the SNI reported
to Mixer).
{{< text bash >}}
$ kubectl apply -f - <<EOF
@ -964,7 +975,7 @@ to hold the configuration of the Nginx SNI proxy:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: set-sni-for-egress-gateway
name: mtls-for-egress-gateway
spec:
host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
subsets:
@ -977,7 +988,42 @@ to hold the configuration of the Nginx SNI proxy:
number: 443
tls:
mode: ISTIO_MUTUAL
sni: placeholder.com
---
# The following filter is used to forward the original SNI (sent by the application) as the SNI of the mutual TLS
# connection.
# The forwarded SNI will be reported to Mixer so that policies will be enforced based on the original SNI value.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: forward-downstream-sni
spec:
filters:
- listenerMatch:
portNumber: $MONGODB_PORT
listenerType: SIDECAR_OUTBOUND
filterName: forward_downstream_sni
filterType: NETWORK
filterConfig: {}
---
# The following filter verifies that the SNI of the mutual TLS connection (the SNI reported to Mixer) is
# identical to the original SNI issued by the application (the SNI used for routing by the SNI proxy).
# The filter prevents Mixer from being deceived by a malicious application: routing to one SNI while
# reporting some other value of SNI. If the original SNI does not match the SNI of the mutual TLS connection, the
# filter will block the connection to the external service.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: egress-gateway-sni-verifier
spec:
workloadLabels:
app: istio-egressgateway-with-sni-proxy
filters:
- listenerMatch:
portNumber: 443
listenerType: GATEWAY
filterName: sni_verifier
filterType: NETWORK
filterConfig: {}
EOF
{{< /text >}}
@ -1035,8 +1081,8 @@ to hold the configuration of the Nginx SNI proxy:
You should see lines similar to the following:
{{< text plain >}}
[2019-01-02T17:22:04.602Z] "- - -" 0 - 768 1863 88 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49976 172.30.146.115:443 172.30.146.118:58510 placeholder.com
[2019-01-02T17:22:04.713Z] "- - -" 0 - 1534 2590 85 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49988 172.30.146.115:443 172.30.146.118:58522 placeholder.com
[2019-01-02T17:22:04.602Z] "- - -" 0 - 768 1863 88 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49976 172.30.146.115:443 172.30.146.118:58510 <your MongoDB host>
[2019-01-02T17:22:04.713Z] "- - -" 0 - 1534 2590 85 - "-" "-" "-" "-" "127.0.0.1:28543" outbound|28543||sni-proxy.local 127.0.0.1:49988 172.30.146.115:443 172.30.146.118:58522 <your MongoDB host>
{{< /text >}}
1. Check the logs of the SNI proxy. If Istio is deployed in the `istio-system` namespace, the command to print the
@ -1068,7 +1114,8 @@ section.
$ kubectl delete serviceentry mongo
$ kubectl delete gateway istio-egressgateway-with-sni-proxy
$ kubectl delete virtualservice direct-mongo-through-egress-gateway
$ kubectl delete destinationrule set-sni-for-egress-gateway
$ kubectl delete destinationrule mtls-for-egress-gateway
$ kubectl delete envoyfilter forward-downstream-sni egress-gateway-sni-verifier
{{< /text >}}
1. Delete the configuration items for the `egressgateway-with-sni-proxy` `Deployment`:

View File

@ -412,7 +412,16 @@ The SNI proxy will forward the traffic to port `443`.
1. Create an egress `Gateway` for _*.wikipedia.org_, port 443, protocol TLS, and a virtual service to direct the
traffic destined for _*.wikipedia.org_ through the gateway.
{{< text bash >}}
Choose the instructions corresponding to whether or not you want to enable
[mutual TLS Authentication](/docs/tasks/security/mutual-tls/) between the source pod and the egress gateway.
> You may want to enable mutual TLS to let the egress gateway monitor the identity of the source pods and to enable Mixer policy enforcement based on that identity.
{{< tabset cookie-name="mtls" >}}
{{% tab name="mTLS enabled" cookie-value="enabled" %}}
{{< text bash >}}
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
@ -424,12 +433,15 @@ The SNI proxy will forward the traffic to port `443`.
servers:
- port:
number: 443
name: tls
name: tls-egress
protocol: TLS
hosts:
- "*.wikipedia.org"
tls:
mode: PASSTHROUGH
mode: MUTUAL
serverCertificate: /etc/certs/cert-chain.pem
privateKey: /etc/certs/key.pem
caCertificates: /etc/certs/root-cert.pem
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
@ -439,6 +451,14 @@ The SNI proxy will forward the traffic to port `443`.
host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
subsets:
- name: wikipedia
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
@ -464,20 +484,130 @@ The SNI proxy will forward the traffic to port `443`.
port:
number: 443
weight: 100
tcp:
- match:
- gateways:
- istio-egressgateway-with-sni-proxy
port: 443
sni_hosts:
- "*.wikipedia.org"
route:
- destination:
host: sni-proxy.local
port:
number: 8443
weight: 100
---
# The following filter is used to forward the original SNI (sent by the application) as the SNI of the mutual TLS
# connection.
# The forwarded SNI will be reported to Mixer so that policies will be enforced based on the original SNI value.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: forward-downstream-sni
spec:
filters:
- listenerMatch:
portNumber: 443
listenerType: SIDECAR_OUTBOUND
filterName: forward_downstream_sni
filterType: NETWORK
filterConfig: {}
---
# The following filter verifies that the SNI of the mutual TLS connection (the SNI reported to Mixer) is
# identical to the original SNI issued by the application (the SNI used for routing by the SNI proxy).
# The filter prevents Mixer from being deceived by a malicious application: routing to one SNI while
# reporting some other value of SNI. If the original SNI does not match the SNI of the mutual TLS connection, the
# filter will block the connection to the external service.
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: egress-gateway-sni-verifier
spec:
workloadLabels:
app: istio-egressgateway-with-sni-proxy
filters:
- listenerMatch:
portNumber: 443
listenerType: GATEWAY
filterName: sni_verifier
filterType: NETWORK
filterConfig: {}
EOF
{{< /text >}}
{{< /text >}}
{{% /tab %}}
{{% tab name="mTLS disabled" cookie-value="disabled" %}}
{{< text bash >}}
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-egressgateway-with-sni-proxy
spec:
selector:
istio: egressgateway-with-sni-proxy
servers:
- port:
number: 443
name: tls
protocol: TLS
hosts:
- "*.wikipedia.org"
tls:
mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: egressgateway-for-wikipedia
spec:
host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
subsets:
- name: wikipedia
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: direct-wikipedia-through-egress-gateway
spec:
hosts:
- "*.wikipedia.org"
gateways:
- mesh
- istio-egressgateway-with-sni-proxy
tls:
- match:
- gateways:
- mesh
port: 443
sni_hosts:
- "*.wikipedia.org"
route:
- destination:
host: istio-egressgateway-with-sni-proxy.istio-system.svc.cluster.local
subset: wikipedia
port:
number: 443
weight: 100
- match:
- gateways:
- istio-egressgateway-with-sni-proxy
port: 443
sni_hosts:
- "*.wikipedia.org"
route:
- destination:
host: sni-proxy.local
port:
number: 8443
weight: 100
EOF
{{< /text >}}
{{% /tab %}}
{{< /tabset >}}
1. Send HTTPS requests to
[https://en.wikipedia.org](https://en.wikipedia.org) and [https://de.wikipedia.org](https://de.wikipedia.org):
@ -515,146 +645,12 @@ The SNI proxy will forward the traffic to port `443`.
log is:
{{< text bash >}}
$ kubectl -n istio-system logs -l istio-mixer-type=telemetry -c mixer | grep '"connectionEvent":"open"' | grep '"sourceName":"istio-egressgateway' | grep 'wikipedia.org'; done
{"level":"info","time":"2018-08-26T16:16:34.784571Z","instance":"tcpaccesslog.logentry.istio-system","connectionDuration":"0s","connectionEvent":"open","connection_security_policy":"unknown","destinationApp":"","destinationIp":"127.0.0.1","destinationName":"unknown","destinationNamespace":"default","destinationOwner":"unknown","destinationPrincipal":"cluster.local/ns/istio-system/sa/istio-egressgateway-with-sni-proxy-service-account","destinationServiceHost":"","destinationWorkload":"unknown","protocol":"tcp","receivedBytes":298,"reporter":"source","requestedServerName":"placeholder.wikipedia.org","sentBytes":0,"sourceApp":"istio-egressgateway-with-sni-proxy","sourceIp":"172.30.146.88","sourceName":"istio-egressgateway-with-sni-proxy-7c4f7868fb-rc8pr","sourceNamespace":"istio-system","sourceOwner":"kubernetes://apis/extensions/v1beta1/namespaces/istio-system/deployments/istio-egressgateway-with-sni-proxy","sourcePrincipal":"cluster.local/ns/default/sa/default","sourceWorkload":"istio-egressgateway-with-sni-proxy","totalReceivedBytes":298,"totalSentBytes":0}
$ kubectl -n istio-system logs -l istio-mixer-type=telemetry -c mixer | grep '"connectionEvent":"open"' | grep '"sourceName":"istio-egressgateway' | grep 'wikipedia.org'
{"level":"info","time":"2018-08-26T16:16:34.784571Z","instance":"tcpaccesslog.logentry.istio-system","connectionDuration":"0s","connectionEvent":"open","connection_security_policy":"unknown","destinationApp":"","destinationIp":"127.0.0.1","destinationName":"unknown","destinationNamespace":"default","destinationOwner":"unknown","destinationPrincipal":"cluster.local/ns/istio-system/sa/istio-egressgateway-with-sni-proxy-service-account","destinationServiceHost":"","destinationWorkload":"unknown","protocol":"tcp","receivedBytes":298,"reporter":"source","requestedServerName":"en.wikipedia.org","sentBytes":0,"sourceApp":"istio-egressgateway-with-sni-proxy","sourceIp":"172.30.146.88","sourceName":"istio-egressgateway-with-sni-proxy-7c4f7868fb-rc8pr","sourceNamespace":"istio-system","sourceOwner":"kubernetes://apis/extensions/v1beta1/namespaces/istio-system/deployments/istio-egressgateway-with-sni-proxy","sourcePrincipal":"cluster.local/ns/sleep/sa/default","sourceWorkload":"istio-egressgateway-with-sni-proxy","totalReceivedBytes":298,"totalSentBytes":0}
{{< /text >}}
Note the `requestedServerName` attribute.
#### SNI monitoring and access policies
Now, once you directed the egress traffic through an egress gateway, you can apply monitoring and access policy enforcement on the egress traffic,
**securely**. In this section you will define a log entry and an access policy for the egress traffic to _*.wikipedia.org_.
1. Create the `logentry`, `rules` and `handlers`:
{{< text bash >}}
$ kubectl apply -f - <<EOF
# Log entry for egress access
apiVersion: "config.istio.io/v1alpha2"
kind: logentry
metadata:
name: egress-access
namespace: istio-system
spec:
severity: '"info"'
timestamp: context.time | timestamp("2017-01-01T00:00:00Z")
variables:
connectionEvent: connection.event | ""
source: source.labels["app"] | "unknown"
sourceNamespace: source.namespace | "unknown"
sourceWorkload: source.workload.name | ""
sourcePrincipal: source.principal | "unknown"
requestedServerName: connection.requested_server_name | "unknown"
destinationApp: destination.labels["app"] | ""
monitored_resource_type: '"UNSPECIFIED"'
---
# Handler for info egress access entries
apiVersion: "config.istio.io/v1alpha2"
kind: stdio
metadata:
name: egress-access-logger
namespace: istio-system
spec:
severity_levels:
info: 0 # output log level as info
outputAsJson: true
---
# Rule to handle access to *.wikipedia.org
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: handle-wikipedia-access
namespace: istio-system
spec:
match: source.labels["app"] == "istio-egressgateway-with-sni-proxy" && destination.labels["app"] == "" && connection.event == "open"
actions:
- handler: egress-access-logger.stdio
instances:
- egress-access.logentry
EOF
{{< /text >}}
1. Send HTTPS requests to
[https://en.wikipedia.org](https://en.wikipedia.org) and [https://de.wikipedia.org](https://de.wikipedia.org):
{{< text bash >}}
$ kubectl exec -it $SOURCE_POD -c sleep -- sh -c 'curl -s https://en.wikipedia.org/wiki/Main_Page | grep -o "<title>.*</title>"; curl -s https://de.wikipedia.org/wiki/Wikipedia:Hauptseite | grep -o "<title>.*</title>"'
<title>Wikipedia, the free encyclopedia</title>
<title>Wikipedia Die freie Enzyklopädie</title>
{{< /text >}}
1. Check the mixer log. If Istio is deployed in the `istio-system` namespace, the command to print the log is:
{{< text bash >}}
$ kubectl -n istio-system logs -l istio-mixer-type=telemetry -c mixer | grep 'egress-access.logentry.istio-system'; done
{{< /text >}}
1. Define a policy that will allow access to the hostnames matching `*.wikipedia.org` except for Wikipedia in
English:
{{< text bash >}}
$ cat <<EOF | kubectl create -f -
apiVersion: "config.istio.io/v1alpha2"
kind: listchecker
metadata:
name: wikipedia-checker
namespace: istio-system
spec:
overrides: ["en.wikipedia.org"] # overrides provide a static list
blacklist: true
---
apiVersion: "config.istio.io/v1alpha2"
kind: listentry
metadata:
name: requested-server-name
namespace: istio-system
spec:
value: connection.requested_server_name
---
# Rule to check access to *.wikipedia.org
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: check-wikipedia-access
namespace: istio-system
spec:
match: source.labels["app"] == "istio-egressgateway-with-sni-proxy" && destination.labels["app"] == ""
actions:
- handler: wikipedia-checker.listchecker
instances:
- requested-server-name.listentry
EOF
{{< /text >}}
1. Send an HTTPS request to the blacklisted [https://en.wikipedia.org](https://en.wikipedia.org):
{{< text bash >}}
$ kubectl exec -it $SOURCE_POD -c sleep -- sh -c 'curl -v https://en.wikipedia.org/wiki/Main_Page'
...
curl: (35) Unknown SSL protocol error in connection to en.wikipedia.org:443
command terminated with exit code 35
{{< /text >}}
1. Send HTTPS requests to some other sites, for example [https://es.wikipedia.org](https://es.wikipedia.org) and
[https://de.wikipedia.org](https://de.wikipedia.org):
{{< text bash >}}
$ kubectl exec -it $SOURCE_POD -c sleep -- sh -c 'curl -s https://es.wikipedia.org/wiki/Wikipedia:Portada | grep -o "<title>.*</title>"; curl -s https://de.wikipedia.org/wiki/Wikipedia:Hauptseite | grep -o "<title>.*</title>"'
<title>Wikipedia, la enciclopedia libre</title>
<title>Wikipedia Die freie Enzyklopädie</title>
{{< /text >}}
##### Cleanup of monitoring and policy enforcement
{{< text bash >}}
$ kubectl delete rule handle-wikipedia-access check-wikipedia-access -n istio-system
$ kubectl delete logentry egress-access -n istio-system
$ kubectl delete stdio egress-access-logger -n istio-system
$ kubectl delete listentry requested-server-name -n istio-system
$ kubectl delete listchecker wikipedia-checker -n istio-system
{{< /text >}}
#### Cleanup wildcard configuration for arbitrary domains
1. Delete the configuration items for _*.wikipedia.org_:
@ -664,6 +660,7 @@ $ kubectl delete listchecker wikipedia-checker -n istio-system
$ kubectl delete gateway istio-egressgateway-with-sni-proxy
$ kubectl delete virtualservice direct-wikipedia-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-wikipedia
$ kubectl delete --ignore-not-found=true envoyfilter forward-downstream-sni egress-gateway-sni-verifier
{{< /text >}}
1. Delete the configuration items for the `egressgateway-with-sni-proxy` `Deployment`: