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})
对于 macOS 用户,确认您使用的是
openssl
版本 1.1 或更高版本:$ openssl version -a | grep OpenSSL
OpenSSL 1.1.1g 21 Apr 2020
如果前面的命令输出的是版本
1.1
或更高版本,如图所示,则您的openssl
命令应该正确执行此任务中的指示。否则,升级您的openssl
或尝试openssl
的不同实现,像在 Linux 机器上一样。
通过 egress 网关发起 TLS 连接
本节描述如何使用 egress 网关发起与示例为 Egress 流量发起 TLS 连接中一样的 TLS。注意,这种情况下,TLS 的发起过程由 egress 网关完成,而不是像之前示例演示的那样由 Sidecar 完成。
为
edition.cnn.com
定义一个ServiceEntry
:$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: cnn
spec:
hosts:
- edition.cnn.com
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS
EOF
发送一个请求至 http://edition.cnn.com/politics, 验证
ServiceEntry
已被正确应用。$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...
command terminated with exit code 35
如果在输出中看到
301 Moved Permanently
,说明ServiceEntry
配置正确。为
edition.cnn.com
创建一个 egressGateway
,端口 443,以及一个 Sidecar 请求的目标规则,Sidecar 请求被直接导向 egress 网关。$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 80
name: https-port-for-tls-origination
protocol: HTTPS
hosts:
- edition.cnn.com
tls:
mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 80
tls:
mode: ISTIO_MUTUAL
sni: edition.cnn.com
EOF
定义一个
VirtualService
来引导流量流经 egress 网关, 以及一个DestinationRule
为访问edition.cnn.com
的请求发起 TLS 连接:$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: direct-cnn-through-egress-gateway
spec:
hosts:
- edition.cnn.com
gateways:
- istio-egressgateway
- mesh
http:
- match:
- gateways:
- mesh
port: 80
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
subset: cnn
port:
number: 80
weight: 100
- match:
- gateways:
- istio-egressgateway
port: 80
route:
- destination:
host: edition.cnn.com
port:
number: 443
weight: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: originate-tls-for-edition-cnn-com
spec:
host: edition.cnn.com
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: SIMPLE # initiates HTTPS for connections to edition.cnn.com
EOF
发送一个 HTTP 请求至 http://edition.cnn.com/politics。
$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 200 OK
...
输出将与在示例为 Egress 流量发起 TLS 连接中显示的一样,发起 TLS 连接后,不再显示 301 Moved Permanently 消息。
检查
istio-egressgateway
Pod 的日志,将看到一行与请求相关的记录。 若 Istio 部署在istio-system
命名空间中,可以通过下面的命令打印日志:$ kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail
将看到类似如下一行的输出:
[2020-06-30T16:17:56.763Z] "GET /politics HTTP/2" 200 - "-" "-" 0 1295938 529 89 "10.244.0.171" "curl/7.64.0" "cf76518d-3209-9ab7-a1d0-e6002728ef5b" "edition.cnn.com" "151.101.129.67:443" outbound|443||edition.cnn.com 10.244.0.170:54280 10.244.0.170:8080 10.244.0.171:35628 - -
清除 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 连接。
生成客户端和服务器的证书与密钥
对于此任务,您可以使用自己喜欢的工具来生成证书和密钥。以下命令使用 openssl。
为您的服务签名证书创建根证书和私钥:
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
为
my-nginx.mesh-external.svc.cluster.local
创建证书和私钥:$ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization"
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt
生成客户端证书和私钥:
$ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt
部署一个双向 TLS 服务器
为了模拟一个真实的支持双向 TLS 协议的外部服务, 在 Kubernetes 集群中部署一个 NGINX 服务器, 该服务器运行在 Istio 服务网格之外,譬如:运行在一个没有开启 Istio Sidecar proxy 注入的命名空间中。
创建一个命名空间,表示 Istio 网格之外的服务,
mesh-external
。 注意在这个命名空间中,Sidecar 自动注入是没有开启的, 不会在 Pod 中自动注入 Sidecar proxy。$ kubectl create namespace mesh-external
创建 Kubernetes Secret, 保存服务器和 CA 的证书。
$ kubectl create -n mesh-external secret tls nginx-server-certs --key my-nginx.mesh-external.svc.cluster.local.key --cert my-nginx.mesh-external.svc.cluster.local.crt
$ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
生成 NGINX 服务器的配置文件:
$ cat <<\EOF > ./nginx.conf
events {
}
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 my-nginx.mesh-external.svc.cluster.local;
ssl_certificate /etc/nginx-server-certs/tls.crt;
ssl_certificate_key /etc/nginx-server-certs/tls.key;
ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
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 - <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-nginx
namespace: mesh-external
labels:
run: my-nginx
spec:
ports:
- port: 443
protocol: TCP
selector:
run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
namespace: mesh-external
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 443
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
- name: nginx-server-certs
mountPath: /etc/nginx-server-certs
readOnly: true
- name: nginx-ca-certs
mountPath: /etc/nginx-ca-certs
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-configmap
- name: nginx-server-certs
secret:
secretName: nginx-server-certs
- name: nginx-ca-certs
secret:
secretName: nginx-ca-certs
EOF
为
nginx.example.com
定义一个ServiceEntry
和一个VirtualService
, 指示 Istio 引导目标为nginx.example.com
的流量流向 NGINX 服务器:$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: nginx
spec:
hosts:
- nginx.example.com
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS
endpoints:
- address: my-nginx.mesh-external.svc.cluster.local
ports:
https: 443
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: nginx
spec:
hosts:
- nginx.example.com
tls:
- match:
- port: 443
sni_hosts:
- nginx.example.com
route:
- destination:
host: nginx.example.com
port:
number: 443
weight: 100
EOF
为 egress 流量配置双向 TLS
创建 Kubernetes Secret 保存客户端证书:
$ kubectl create secret -n istio-system generic client-credential --from-file=tls.key=client.example.com.key \
--from-file=tls.crt=client.example.com.crt --from-file=ca.crt=example.com.crt
Secret 所在的命名空间必须与出口网关部署的位置一只,在本例中为
istio-system
命名空间。为了支持与各种工具的集成,Istio 支持多种 Secret 格式。
在本例中,使用了一个具有关键字
tls.key
、tls.crt
和ca.crt
的通用 Secret。为
my-nginx.mesh-external.svc.cluster.local
创建一个 egressGateway
端口为 443,以及目标规则和虚拟服务来引导流量流经 egress 网关并从 egress 网关流向外部服务。$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- my-nginx.mesh-external.svc.cluster.local
tls:
mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: egressgateway-for-nginx
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: nginx
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: ISTIO_MUTUAL
sni: my-nginx.mesh-external.svc.cluster.local
EOF
定义一个
VirtualService
引导流量流经 egress 网关:$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: direct-nginx-through-egress-gateway
spec:
hosts:
- my-nginx.mesh-external.svc.cluster.local
gateways:
- istio-egressgateway
- mesh
http:
- match:
- gateways:
- mesh
port: 80
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
subset: nginx
port:
number: 443
weight: 100
- match:
- gateways:
- istio-egressgateway
port: 443
route:
- destination:
host: my-nginx.mesh-external.svc.cluster.local
port:
number: 443
weight: 100
EOF
添加
DestinationRule
执行双向 TLS$ kubectl apply -n istio-system -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: originate-mtls-for-nginx
spec:
host: my-nginx.mesh-external.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings:
- port:
number: 443
tls:
mode: MUTUAL
credentialName: client-credential # 这必须与之前创建的用于保存客户端证书的 Secret 相匹配
sni: my-nginx.mesh-external.svc.cluster.local
EOF
发送一个 HTTP 请求至
http://my-nginx.mesh-external.svc.cluster.local
:$ kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})" -c sleep -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
检查
istio-egressgateway
Pod 日志,有一行与请求相关的日志记录。 如果 Istio 部署在命名空间istio-system
中,打印日志的命令为:$ kubectl logs -l istio=egressgateway -n istio-system | grep 'my-nginx.mesh-external.svc.cluster.local' | 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" "my-nginx.mesh-external.svc.cluster.local" "172.21.72.197:443"
清除双向 TLS 连接示例
删除创建的 Kubernetes 资源:
$ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
$ kubectl delete secret client-credential -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 virtualservice direct-nginx-through-egress-gateway
$ kubectl delete destinationrule -n istio-system originate-mtls-for-nginx
$ kubectl delete destinationrule egressgateway-for-nginx
删除证书和私钥:
$ rm example.com.crt example.com.key my-nginx.mesh-external.svc.cluster.local.crt my-nginx.mesh-external.svc.cluster.local.key my-nginx.mesh-external.svc.cluster.local.csr client.example.com.crt client.example.com.csr client.example.com.key
删除生成并应用于示例中的配置文件
$ rm ./nginx.conf
$ rm ./gateway-patch.json
清除
删除 sleep
的 Service 和 Deployment:
$ kubectl delete service sleep
$ kubectl delete deployment sleep