Egress 网关的 TLS 发起过程
为 Egress 流量发起 TLS 连接 示例中演示了如何配置 Istio 以对外部服务流量实施 TLS origination。 配置 Egress Gateway 示例中演示了如何配置 Istio 来通过专门的 egress 网关服务引导 egress 流量。 本示例兼容以上两者,描述如何配置 egress 网关,为外部服务流量发起 TLS 连接。
开始之前
遵照安装指南中的指令,安装 Istio。
启动 sleep 样本应用,作为外部请求的测试源。
若已开启自动 sidecar 注入,执行
$ kubectl apply -f @samples/sleep/sleep.yaml@
否则,必须在部署
sleep应用之前手动注入 sidecar:$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@)
注意每一个可以执行
exec和curl操作的 pod,都需要注入。创建一个 shell 变量,来保存向外部服务发送请求的源 pod 的名称。 若使用 sleep 样例,运行:
$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
通过 egress 网关发起 TLS 连接
本节描述如何使用 egress 网关发起与示例为 Egress 流量发起 TLS 连接中一样的 TLS。 注意,这种情况下,TLS 的发起过程由 egress 网关完成,而不是像之前示例演示的那样由 sidecar 完成。
为
edition.cnn.com定义一个ServiceEntry:$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: ServiceEntrymetadata:name: cnnspec:hosts:- edition.cnn.comports:- number: 80name: httpprotocol: HTTP- number: 443name: httpsprotocol: HTTPSresolution: DNSEOF
发送一个请求至 http://edition.cnn.com/politics,验证
ServiceEntry已被正确应用。$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politicsHTTP/1.1 301 Moved Permanently...location: https://edition.cnn.com/politics...command terminated with exit code 35
如果在输出中看到 _301 Moved Permanently_,说明
ServiceEntry配置正确。为 edition.cnn.com 创建一个 egress
Gateway,端口 443,以及一个 sidecar 请求的目标规则,sidecar 请求被直接导向 egress 网关。根据需要开启源 pod 与 egress 网关之间的双向 TLS 认证,选择相应的命令。
若开启双向 TLS ,则源 pod 与 egress 网关之间的流量为加密状态。 此外,双向 TLS 将允许 egress 网关监控源 pods 的身份,并开启 Mixer 基于该身份标识的强制策略实施。
$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata:name: istio-egressgatewayspec:selector:istio: egressgatewayservers:- port:number: 80name: httpsprotocol: HTTPShosts:- edition.cnn.comtls:mode: MUTUALserverCertificate: /etc/certs/cert-chain.pemprivateKey: /etc/certs/key.pemcaCertificates: /etc/certs/root-cert.pem---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: egressgateway-for-cnnspec:host: istio-egressgateway.istio-system.svc.cluster.localsubsets:- name: cnntrafficPolicy:loadBalancer:simple: ROUND_ROBINportLevelSettings:- port:number: 80tls:mode: ISTIO_MUTUALsni: edition.cnn.comEOF
$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata:name: istio-egressgatewayspec:selector:istio: egressgatewayservers:- port:number: 80name: http-port-for-tls-originationprotocol: HTTPhosts:- edition.cnn.com---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: egressgateway-for-cnnspec:host: istio-egressgateway.istio-system.svc.cluster.localsubsets:- name: cnnEOF
定义一个
VirtualService来引导流量流经 egress 网关,以及一个DestinationRule为访问edition.cnn.com的请求发起 TLS 连接:$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: direct-cnn-through-egress-gatewayspec:hosts:- edition.cnn.comgateways:- istio-egressgateway- meshhttp:- match:- gateways:- meshport: 80route:- destination:host: istio-egressgateway.istio-system.svc.cluster.localsubset: cnnport:number: 80weight: 100- match:- gateways:- istio-egressgatewayport: 80route:- destination:host: edition.cnn.comport:number: 443weight: 100---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: originate-tls-for-edition-cnn-comspec:host: edition.cnn.comtrafficPolicy:loadBalancer:simple: ROUND_ROBINportLevelSettings:- port:number: 443tls:mode: SIMPLE # initiates HTTPS for connections to edition.cnn.comEOF
发送一个 HTTP 请求至 http://edition.cnn.com/politics。
$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politicsHTTP/1.1 200 OK...content-length: 150793...
输出将与在示例为 Egress 流量发起 TLS 连接中显示的一样,发起 TLS 连接后,不再显示 301 Moved Permanently 消息。
检查
istio-egressgatewaypod 的日志,将看到一行与请求相关的记录。 若 Istio 部署在istio-system命名空间中,打印日志的命令为:$ kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail
将看到类似如下一行:
"[2018-06-14T13:49:36.340Z] "GET /politics HTTP/1.1" 200 - 0 148528 5096 90 "172.30.146.87" "curl/7.35.0" "c6bfdfc3-07ec-9c30-8957-6904230fd037" "edition.cnn.com" "151.101.65.67:443"
清除 TLS 启动实例
删除创建的 Istio 配置项:
$ kubectl delete gateway istio-egressgateway$ kubectl delete serviceentry cnn$ kubectl delete virtualservice direct-cnn-through-egress-gateway$ kubectl delete destinationrule originate-tls-for-edition-cnn-com$ kubectl delete destinationrule egressgateway-for-cnn
通过 egress 网关发起双向 TLS 连接
与前一章节类似,本章节描述如何配置一个 egress 网关,为外部服务发起 TLS 连接,只是这次服务要求双向 TLS。
本示例要求更高的参与性,首先需要:
- 生成客户端和服务器证书
- 部署一个支持双向 TLS 的外部服务
- 使用所需的证书重新部署 egress 网关
然后才可以配置出口流量流经 egress 网关,egress 网关将发起 TLS 连接。
生成客户端和服务器的证书与密钥
克隆示例代码库 https://github.com/nicholasjackson/mtls-go-example:
$ git clone https://github.com/nicholasjackson/mtls-go-example
进入克隆的代码库目录:
$ cd mtls-go-example
为
nginx.example.com生成证书。 使用任意 password 执行如下命令:$ ./generate.sh nginx.example.com <password>
所有出现的提示,均选择
y。将证书迁移至
nginx.example.com目录:$ mkdir ../nginx.example.com && mv 1_root 2_intermediate 3_application 4_client ../nginx.example.com
返回至上一级目录:
$ cd ..
部署一个双向 TLS 服务器
为了模拟一个真实的支持双向 TLS 协议的外部服务, 在 Kubernetes 集群中部署一个 NGINX 服务器,该服务器运行在 Istio 服务网格之外,譬如:运行在一个没有开启 Istio sidecar proxy 注入的命名空间中。
创建一个命名空间,表示 Istio 网格之外的服务,
mesh-external。注意在这个命名空间中,sidecar 自动注入是没有开启的,不会在 pods 中自动注入 sidecar proxy。$ kubectl create namespace mesh-external
创建 Kubernetes Secrets ,保存服务器和 CA 的证书。
$ kubectl create -n mesh-external secret tls nginx-server-certs --key nginx.example.com/3_application/private/nginx.example.com.key.pem --cert nginx.example.com/3_application/certs/nginx.example.com.cert.pem$ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=nginx.example.com/2_intermediate/certs/ca-chain.cert.pem
生成 NGINX 服务器的配置文件:
$ cat <<EOF > ./nginx.confevents {}http {log_format main '$remote_addr - $remote_user [$time_local] $status ''"$request" $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log /var/log/nginx/access.log main;error_log /var/log/nginx/error.log;server {listen 443 ssl;root /usr/share/nginx/html;index index.html;server_name nginx.example.com;ssl_certificate /etc/nginx-server-certs/tls.crt;ssl_certificate_key /etc/nginx-server-certs/tls.key;ssl_client_certificate /etc/nginx-ca-certs/ca-chain.cert.pem;ssl_verify_client on;}}EOF
生成 Kubernetes ConfigMap 保存 NGINX 服务器的配置文件:
$ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
部署 NGINX 服务器:
$ kubectl apply -f - <<EOFapiVersion: v1kind: Servicemetadata:name: my-nginxnamespace: mesh-externallabels:run: my-nginxspec:ports:- port: 443protocol: TCPselector:run: my-nginx---apiVersion: apps/v1kind: Deploymentmetadata:name: my-nginxnamespace: mesh-externalspec:selector:matchLabels:run: my-nginxreplicas: 1template:metadata:labels:run: my-nginxspec:containers:- name: my-nginximage: nginxports:- containerPort: 443volumeMounts:- name: nginx-configmountPath: /etc/nginxreadOnly: true- name: nginx-server-certsmountPath: /etc/nginx-server-certsreadOnly: true- name: nginx-ca-certsmountPath: /etc/nginx-ca-certsreadOnly: truevolumes:- name: nginx-configconfigMap:name: nginx-configmap- name: nginx-server-certssecret:secretName: nginx-server-certs- name: nginx-ca-certssecret:secretName: nginx-ca-certsEOF
为
nginx.example.com定义一个ServiceEntry和一个VirtualService,指示 Istio 引导目标为nginx.example.com的流量流向 NGINX 服务器:$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: ServiceEntrymetadata:name: nginxspec:hosts:- nginx.example.comports:- number: 80name: httpprotocol: HTTP- number: 443name: httpsprotocol: HTTPSresolution: DNSendpoints:- address: my-nginx.mesh-external.svc.cluster.localports:https: 443---apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: nginxspec:hosts:- nginx.example.comtls:- match:- port: 443sni_hosts:- nginx.example.comroute:- destination:host: nginx.example.comport:number: 443weight: 100EOF
部署一个容器测试 nginx 部署
生成 Kubernetes Secrets ,保存客户端和 CA 的证书:
$ kubectl create secret tls nginx-client-certs --key nginx.example.com/4_client/private/nginx.example.com.key.pem --cert nginx.example.com/4_client/certs/nginx.example.com.cert.pem$ kubectl create secret generic nginx-ca-certs --from-file=nginx.example.com/2_intermediate/certs/ca-chain.cert.pem
基于挂载的客户端和 CA 证书,部署 sleep 样本应用,测试发送请求至 NGINX 服务器:
$ kubectl apply -f - <<EOF# Copyright 2017 Istio Authors## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.################################################################################################### Sleep service##################################################################################################apiVersion: v1kind: Servicemetadata:name: sleeplabels:app: sleepspec:ports:- port: 80name: httpselector:app: sleep---apiVersion: apps/v1kind: Deploymentmetadata:name: sleepspec:replicas: 1template:metadata:labels:app: sleepspec:containers:- name: sleepimage: tutum/curlcommand: ["/bin/sleep","infinity"]imagePullPolicy: IfNotPresentvolumeMounts:- name: nginx-client-certsmountPath: /etc/nginx-client-certsreadOnly: true- name: nginx-ca-certsmountPath: /etc/nginx-ca-certsreadOnly: truevolumes:- name: nginx-client-certssecret:secretName: nginx-client-certs- name: nginx-ca-certssecret:secretName: nginx-ca-certsEOF
定义一个环境变量保存
sleeppod 的名称:$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
使用部署的 sleep pod 向 NGINX 服务器发送请求。 由于
nginx.example.com不是真实存在的,DNS 无法解析,后面的curl命令使用--resolve选项手动解析主机名。 –resolve 选项传递的 IP 值(下方所示,1.1.1.1)没有意义。除 127.0.0.1 之外的任意值都可以使用。 一般情况下,目标主机名对应着一个 DNS 项,无需使用curl的--resolve选项。$ kubectl exec -it $SOURCE_POD -c sleep -- curl -v --resolve nginx.example.com:443:1.1.1.1 --cacert /etc/nginx-ca-certs/ca-chain.cert.pem --cert /etc/nginx-client-certs/tls.crt --key /etc/nginx-client-certs/tls.key https://nginx.example.com...Server certificate:subject: C=US; ST=Denial; L=Springfield; O=Dis; CN=nginx.example.comstart date: 2018-08-16 04:31:20 GMTexpire date: 2019-08-26 04:31:20 GMTcommon name: nginx.example.com (matched)issuer: C=US; ST=Denial; O=Dis; CN=nginx.example.comSSL certificate verify ok.> GET / HTTP/1.1> User-Agent: curl/7.35.0> Host: nginx.example.com...< HTTP/1.1 200 OK< Server: nginx/1.15.2...<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...
验证服务器要求客户端的证书:
$ kubectl exec -it $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl -k --resolve nginx.example.com:443:1.1.1.1 https://nginx.example.com<html><head><title>400 No required SSL certificate was sent</title></head><body bgcolor="white"><center><h1>400 Bad Request</h1></center><center>No required SSL certificate was sent</center><hr><center>nginx/1.15.2</center></body></html>
使用客户端证书重新部署 egress 网关
生成 Kubernetes Secrets 保存客户端和 CA 的证书。
$ kubectl create -n istio-system secret tls nginx-client-certs --key nginx.example.com/4_client/private/nginx.example.com.key.pem --cert nginx.example.com/4_client/certs/nginx.example.com.cert.pem$ kubectl create -n istio-system secret generic nginx-ca-certs --from-file=nginx.example.com/2_intermediate/certs/ca-chain.cert.pem
部署
istio-egressgateway挂载新生成的 secrets 的 volume。使用的参数选项与生成istio.yaml中的一致:$ istioctl manifest generate --set values.gateways.istio-ingressgateway.enabled=false \--set values.gateways.istio-egressgateway.enabled=true \--set 'values.gateways.istio-egressgateway.secretVolumes[0].name'=egressgateway-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[0].secretName'=istio-egressgateway-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[0].mountPath'=/etc/istio/egressgateway-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[1].name'=egressgateway-ca-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[1].secretName'=istio-egressgateway-ca-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[1].mountPath'=/etc/istio/egressgateway-ca-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[2].name'=nginx-client-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[2].secretName'=nginx-client-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[2].mountPath'=/etc/nginx-client-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[3].name'=nginx-ca-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[3].secretName'=nginx-ca-certs \--set 'values.gateways.istio-egressgateway.secretVolumes[3].mountPath'=/etc/nginx-ca-certs > \./istio-egressgateway.yaml
重新部署
istio-egressgateway:$ kubectl apply -f ./istio-egressgateway.yamldeployment "istio-egressgateway" configured
验证密钥和证书被成功装载入
istio-egressgatewaypod:$ kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=egressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/nginx-client-certs /etc/nginx-ca-certs
tls.crt与tls.key在/etc/istio/nginx-client-certs中,而ca-chain.cert.pem在/etc/istio/nginx-ca-certs中。
为 egress 流量配置双向 TLS
为
nginx.example.com创建一个 egressGateway端口为 443,以及目标规则和虚拟服务来引导流量流经 egress 网关并从 egress 网关流向外部服务。$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata:name: istio-egressgatewayspec:selector:istio: egressgatewayservers:- port:number: 443name: httpsprotocol: HTTPShosts:- nginx.example.comtls:mode: MUTUALserverCertificate: /etc/certs/cert-chain.pemprivateKey: /etc/certs/key.pemcaCertificates: /etc/certs/root-cert.pem---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: egressgateway-for-nginxspec:host: istio-egressgateway.istio-system.svc.cluster.localsubsets:- name: nginxtrafficPolicy:loadBalancer:simple: ROUND_ROBINportLevelSettings:- port:number: 443tls:mode: ISTIO_MUTUALsni: nginx.example.comEOF
定义一个
VirtualService引导流量流经 egress 网关,一个DestinationRule发起双向 TLS 连接:$ kubectl apply -f - <<EOFapiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: direct-nginx-through-egress-gatewayspec:hosts:- nginx.example.comgateways:- istio-egressgateway- meshhttp:- match:- gateways:- meshport: 80route:- destination:host: istio-egressgateway.istio-system.svc.cluster.localsubset: nginxport:number: 443weight: 100- match:- gateways:- istio-egressgatewayport: 443route:- destination:host: nginx.example.comport:number: 443weight: 100---apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: originate-mtls-for-nginxspec:host: nginx.example.comtrafficPolicy:loadBalancer:simple: ROUND_ROBINportLevelSettings:- port:number: 443tls:mode: MUTUALclientCertificate: /etc/nginx-client-certs/tls.crtprivateKey: /etc/nginx-client-certs/tls.keycaCertificates: /etc/nginx-ca-certs/ca-chain.cert.pemsni: nginx.example.comEOF
发送一个 HTTP 请求至
http://nginx.example.com:$ kubectl exec -it $SOURCE_POD -c sleep -- curl -s --resolve nginx.example.com:80:1.1.1.1 http://nginx.example.com<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...
检查
istio-egressgatewaypod 日志,有一行与请求相关的日志记录。 如果 Istio 部署在命名空间istio-system中,打印日志的命令为:$ kubectl logs -l istio=egressgateway -n istio-system | grep 'nginx.example.com' | grep HTTP
将显示类似如下的一行:
[2018-08-19T18:20:40.096Z] "GET / HTTP/1.1" 200 - 0 612 7 5 "172.30.146.114" "curl/7.35.0" "b942b587-fac2-9756-8ec6-303561356204" "nginx.example.com" "172.21.72.197:443"
清除双向 TLS 连接示例
删除创建的 Kubernetes 资源:
$ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external$ kubectl delete secret nginx-client-certs nginx-ca-certs$ kubectl delete secret nginx-client-certs nginx-ca-certs -n istio-system$ kubectl delete configmap nginx-configmap -n mesh-external$ kubectl delete service my-nginx -n mesh-external$ kubectl delete deployment my-nginx -n mesh-external$ kubectl delete namespace mesh-external$ kubectl delete gateway istio-egressgateway$ kubectl delete serviceentry nginx$ kubectl delete virtualservice direct-nginx-through-egress-gateway$ kubectl delete destinationrule originate-mtls-for-nginx$ kubectl delete destinationrule egressgateway-for-nginx
删除用于生成证书和仓库的路径:
$ rm -rf nginx.example.com mtls-go-example
删除生成并应用于示例中的配置文件
$ rm -f ./nginx.conf ./istio-egressgateway.yaml
清除
删除 sleep 服务和部署:
$ kubectl delete service sleep$ kubectl delete deployment sleep
相关内容
管控出口流量的备选方案比较,包括性能因素。
使用 Istio 的出口流量管控来阻止相关出口流量攻击。
涉及出口流量攻击和出口流量管控要求。
评估加入 Egress gateway 对性能造成的影响。
描述了一个基于 Istio 的 Bookinfo 示例的简单场景。
描述如何配置 Istio 进行 HTTP Egress 流量监控和访问策略。