入口网关,不终止 TLS
在 使用 HTTPS 保护网关 任务中描述了如何配置对 HTTP 服务的 HTTPS 入口访问。本示例描述了如何配置对 HTTPS 服务的 HTTPS 入口访问,即配置入口网关以执行 SNI 传输,而不是对传入请求执行 TLS 终止。
本任务中使用的示例 HTTPS 服务是一个简单的 NGINX 服务器。在以下步骤中,您首先在 Kubernetes 集群中部署 NGINX 服务。然后,您配置网关以通过主机 nginx.example.com
为服务提供入口访问。
开始之前
按照 安装指南 中的说明设置 Istio。
生成客户端和服务器证书和密钥
对于此任务,您可以使用您喜欢的工具来生成证书和密钥。以下命令使用 openssl
创建一个根证书和私钥,用于为您的服务签署证书
$ mkdir example_certs $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs/example.com.key -out example_certs/example.com.crt
为
nginx.example.com
创建一个证书和私钥$ openssl req -out example_certs/nginx.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs/nginx.example.com.key -subj "/CN=nginx.example.com/O=some organization" $ openssl x509 -req -sha256 -days 365 -CA example_certs/example.com.crt -CAkey example_certs/example.com.key -set_serial 0 -in example_certs/nginx.example.com.csr -out example_certs/nginx.example.com.crt
部署 NGINX 服务器
创建一个 Kubernetes Secret 来保存服务器的证书。
$ kubectl create secret tls nginx-server-certs \ --key example_certs/nginx.example.com.key \ --cert example_certs/nginx.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 nginx.example.com; ssl_certificate /etc/nginx-server-certs/tls.crt; ssl_certificate_key /etc/nginx-server-certs/tls.key; } } EOF
创建一个 Kubernetes ConfigMap 来保存 NGINX 服务器的配置
$ kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf
部署 NGINX 服务器
$ cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-nginx spec: ports: - port: 443 protocol: TCP selector: run: my-nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: selector: matchLabels: run: my-nginx replicas: 1 template: metadata: labels: run: my-nginx sidecar.istio.io/inject: "true" 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 volumes: - name: nginx-config configMap: name: nginx-configmap - name: nginx-server-certs secret: secretName: nginx-server-certs EOF
要测试 NGINX 服务器是否成功部署,请从其 sidecar 代理向服务器发送请求,但不检查服务器的证书(使用
curl
的-k
选项)。确保服务器的证书被正确打印,即common name (CN)
等于nginx.example.com
。$ kubectl exec "$(kubectl get pod -l run=my-nginx -o jsonpath={.items..metadata.name})" -c istio-proxy -- curl -sS -v -k --resolve nginx.example.com:443:127.0.0.1 https://nginx.example.com ... SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 ALPN, server accepted to use http/1.1 Server certificate: subject: CN=nginx.example.com; O=some organization start date: May 27 14:18:47 2020 GMT expire date: May 27 14:18:47 2021 GMT issuer: O=example Inc.; CN=example.com SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway. > GET / HTTP/1.1 > User-Agent: curl/7.58.0 > Host: nginx.example.com ... < HTTP/1.1 200 OK < Server: nginx/1.17.10 ... <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
配置入口网关
- 定义一个
Gateway
,它暴露端口 443 并使用直通 TLS 模式。这指示网关按原样传递入站流量,而不终止 TLS
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: mygateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: PASSTHROUGH
hosts:
- nginx.example.com
EOF
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "nginx.example.com"
port: 443
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: All
EOF
- 配置通过
Gateway
输入的流量路由
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: nginx
spec:
hosts:
- nginx.example.com
gateways:
- mygateway
tls:
- match:
- port: 443
sniHosts:
- nginx.example.com
route:
- destination:
host: my-nginx
port:
number: 443
EOF
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: nginx
spec:
parentRefs:
- name: mygateway
hostnames:
- "nginx.example.com"
rules:
- backendRefs:
- name: my-nginx
port: 443
EOF
- 确定入站 IP 和端口
使用以下命令设置 SECURE_INGRESS_PORT
和 INGRESS_HOST
环境变量
$ kubectl wait --for=condition=programmed gtw mygateway
$ export INGRESS_HOST=$(kubectl get gtw mygateway -o jsonpath='{.status.addresses[0].value}')
$ export SECURE_INGRESS_PORT=$(kubectl get gtw mygateway -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')
从集群外部访问 NGINX 服务。请注意,服务器返回了正确的证书,并且它已成功验证(SSL certificate verify ok 已打印)。
$ curl -v --resolve "nginx.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" --cacert example_certs/example.com.crt "https://nginx.example.com:$SECURE_INGRESS_PORT" Server certificate: subject: CN=nginx.example.com; O=some organization start date: Wed, 15 Aug 2018 07:29:07 GMT expire date: Sun, 25 Aug 2019 07:29:07 GMT issuer: O=example Inc.; CN=example.com SSL certificate verify ok. < HTTP/1.1 200 OK < Server: nginx/1.15.2 ... <html> <head> <title>Welcome to nginx!</title>
清理
- 删除网关配置和路由
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice nginx
$ kubectl delete gtw mygateway
$ kubectl delete tlsroute nginx
删除 NGINX 资源和配置文件
$ kubectl delete secret nginx-server-certs $ kubectl delete configmap nginx-configmap $ kubectl delete service my-nginx $ kubectl delete deployment my-nginx $ rm ./nginx.conf
删除证书和密钥
$ rm -rf ./example_certs