侧边栏壁纸
博主头像
船长博主等级

专注于云原生运维,致敬每个爱学习的你

  • 累计撰写 35 篇文章
  • 累计创建 10 个标签
  • 累计收到 10 条评论

Istio 南北向流量管理

船长
2022-02-13 / 0 评论 / 3 点赞 / 1,509 阅读 / 38,940 字
温馨提示:
本文最后更新于 2022-02-13,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Istio 网关

Istio Gateway 在网格边缘运行的负载均衡器,接收入口或出口的流量到集群内部、

Istio网关新的Gateway资源和VirtualService资源来控制入口或出口流量,它们协同工作将流量路由到网格中。

  • 客户端通过url发送请求
  • LoadBalancer会进行监听接收到请求,并将请求转发到集群中
  • 在集群内部,请求被路由到Istio IngressGateway服务的负载均衡器转发过来的端口上
  • Istio IngressGateway服务将请求转发到对应的Pod上
  • 在IngressGateway Pod上会根据Gateway资源和VirtualService资源定义的配置找到正确的服务
  • Istio IngressGateway Pod会根据路由配置信息将请求路由到对应的应用Service上
  • 应用Service将请求路由到对应的应用Pod上

Ingress Gateway

Ingress Gateway是一个基于Envoy代理的封装,它的配置方式与网格服务中使用的Sidecar配置相同(实际上是同样的容器镜像)。当我们创建或更改一个Gateway或VirtualService时,Istio Pilot控制器会检测到这些变更,并将这些变更信息转换为Envoy配置,然后将Envoy配置发送给相关Envoy代理,包括内部的Envoy和Ingress Gateway中的Envoy。

部署示例应用

[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/httpbin/httpbin.yaml
serviceaccount/httpbin created
service/httpbin configured
deployment.apps/httpbin created
 
 
[root@k8s-master01 istio-1.10.6]# cat  samples/httpbin/httpbin.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80

配置 Ingress

创建Istio Gateway:

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway      # Ingress Gateway名称
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80     # 定义端口
      name: http
      protocol: HTTP     # 定义协议
    hosts:
    - "httpbin.kubesre.com"   # 定义域名
EOF

为通过 Gateway 的入口流量配置路由:

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.kubesre.com"    # 定义域名
  gateways:
  - httpbin-gateway    # 关联Gateway
  http:
  - match:     # 定义路由
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin   
EOF

使用curl命令访问httpbin服务:

[root@k8s-master01 istio-1.10.6]# curl -s -I -HHost:httpbin.kubesre.com "http://10.96.1.69/status/200"
HTTP/1.1 200 OK
server: istio-envoy
date: Sat, 12 Feb 2022 12:30:06 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 44
 
 
# 访问其他没有被显式暴露的 URL 时,将看到 HTTP 404 错误:
[root@k8s-master01 istio-1.10.6]# curl -s -I -HHost:httpbin.kubesre.com "http://10.96.1.69/head"     
HTTP/1.1 404 Not Found
date: Sat, 12 Feb 2022 12:31:14 GMT
server: istio-envoy
transfer-encoding: chunked

创建证书

# 创建一个根证书和私钥以为您的服务所用的证书签名:
[root@k8s-master01 istio-1.10.6]# 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
Generating a 2048 bit RSA private key
................................+++
...............................................................................+++
writing new private key to 'example.com.key'
-----
# 为 httpbin.example.com 创建一个证书和私钥:
[root@k8s-master01 istio-1.10.6]# openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt
Generating a 2048 bit RSA private key
...................................+++
.......................+++
writing new private key to 'httpbin.example.com.key'
-----
[root@k8s-master01 istio-1.10.6]# openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt
Signature ok
subject=/CN=httpbin.example.com/O=httpbin organization
Getting CA Private Key

配置单向TLS Ingress网关

创建一个 Kubernetes secret 以保存服务器的证书和私钥

[root@k8s-master01 istio-1.10.6]# kubectl create -n istio-system secret tls istio-ingressgateway-certs --key httpbin.example.com.key --cert httpbin.example.com.crt
secret/istio-ingressgateway-certs created

为Ingress Gateway配置证书

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key
    hosts:
    - "httpbin.example.com"
EOF

配置VirtualService:

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

使用curl命令发送一个https请求进行验证:

[root@k8s-master01 istio-1.10.6]# curl -v -HHost:httpbin.example.com --resolve httpbin.example.com:443:10.96.1.69 --cacert example.com.crt https://httpbin.example.com:443/status/418
* Added httpbin.example.com:443:10.96.1.69 to DNS cache
* About to connect() to httpbin.example.com port 443 (#0)
*   Trying 10.96.1.69...
* Connected to httpbin.example.com (10.96.1.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=httpbin organization,CN=httpbin.example.com
*       start date: Feb 12 12:36:54 2022 GMT
*       expire date: Feb 12 12:36:54 2023 GMT
*       common name: httpbin.example.com
*       issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Sat, 12 Feb 2022 12:50:03 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 34
<
 
    -=[ teapot ]=-
 
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
* Connection #0 to host httpbin.example.com left intact

PS: --resolve标志让 curl 在通过 TLS 访问网关 IP 时支持 SNI 值 httpbin.example.com。 --cacert 选项则让 curl 使用您创建的证书来验证服务器。

配置双向TLS Ingress 网关

创建一个 Kubernetes Secret以保存服务端将用来验证它的客户端的CA证书:

[root@k8s-master01 istio-1.10.6]# kubectl create -n istio-system secret generic istio-ingressgateway-ca-certs --from-file=example.com.crt
secret/istio-ingressgateway-ca-certs created

修改Ingress Gateway配置:

# 修改 TLS 模式为 MUTUAL,并指定 caCertificates
[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key
      caCertificates: /etc/istio/ingressgateway-ca-certs/example.com.crt
    hosts:
    - "httpbin.example.com"
EOF

创建客户端证书:

[root@k8s-master01 istio-1.10.6]# openssl req -out httpbin-client.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin-client.example.com.key -subj "/CN=httpbin-client.example.com/O=httpbin's client organization"
openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin-client.example.com.csr -out httpbin-client.example.com.crt
Generating a 2048 bit RSA private key
..........+++
.......................+++
writing new private key to 'httpbin-client.example.com.key'
-----
[root@k8s-master01 istio-1.10.6]# openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin-client.example.com.csr -out httpbin-client.example.com.crt
Signature ok
subject=/CN=httpbin-client.example.com/O=httpbin's client organization
Getting CA Private Key

使用curl命令发送一个https请求进行验证:

[root@k8s-master01 istio-1.10.6]# curl -v -HHost:httpbin.example.com --resolve httpbin.example.com:443:10.96.1.69 --cacert example.com.crt --cert ./httpbin-client.example.com.crt --key ./httpbin-client.example.com.key https://httpbin.example.com/status/418
* Added httpbin.example.com:443:10.96.1.69 to DNS cache
* About to connect() to httpbin.example.com port 443 (#0)
*   Trying 10.96.1.69...
* Connected to httpbin.example.com (10.96.1.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=httpbin organization,CN=httpbin.example.com
*       start date: Feb 12 12:36:54 2022 GMT
*       expire date: Feb 12 12:36:54 2023 GMT
*       common name: httpbin.example.com
*       issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Sat, 12 Feb 2022 12:50:03 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 34
<
 
    -=[ teapot ]=-
 
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
* Connection #0 to host httpbin.example.com left intact

为多主机配置TLS Ingress网关

bookinfo.com 创建服务器证书和私钥:

[root@k8s-master01 istio-1.10.6]# openssl req -out bookinfo.com.csr -newkey rsa:2048 -nodes -keyout bookinfo.com.key -subj "/CN=bookinfo.com/O=bookinfo organization"
Generating a 2048 bit RSA private key
......+++
.....+++
writing new private key to 'bookinfo.com.key'
-----
[root@k8s-master01 istio-1.10.6]# openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in bookinfo.com.csr -out bookinfo.com.crt
Signature ok
subject=/CN=bookinfo.com/O=bookinfo organization
Getting CA Private Key

使用新证书重新部署 istio-ingressgateway

# 创建一个新的 secret 以保存 bookinfo.com 的证书:
[root@k8s-master01 istio-1.10.6]# kubectl create -n istio-system secret tls istio-ingressgateway-bookinfo-certs --key bookinfo.com.key --cert bookinfo.com.crt
secret/istio-ingressgateway-bookinfo-certs created
 
 
# 更新 istio-ingressgateway deployment 以挂载新创建的 secret。创建如下 gateway-patch.json 文件以更新 istio-ingressgateway deployment:
[root@k8s-master01 istio-1.10.6]# cat > gateway-patch.json <<EOF
[{
  "op": "add",
  "path": "/spec/template/spec/containers/0/volumeMounts/0",
  "value": {
    "mountPath": "/etc/istio/ingressgateway-bookinfo-certs",
    "name": "ingressgateway-bookinfo-certs",
    "readOnly": true
  }
},
{
  "op": "add",
  "path": "/spec/template/spec/volumes/0",
  "value": {
  "name": "ingressgateway-bookinfo-certs",
    "secret": {
      "secretName": "istio-ingressgateway-bookinfo-certs",
      "optional": true
    }
  }
}]
EOF
 
 
# 使用以下命令应用 istio-ingressgateway deployment 更新:
[root@k8s-master01 istio-1.10.6]#  kubectl -n istio-system patch --type=json deploy istio-ingressgateway -p "$(cat gateway-patch.json)"
deployment.apps/istio-ingressgateway patched
 
 
# 验证 istio-ingressgateway pod 已成功加载私钥和证书:
[root@k8s-master01 istio-1.10.6]# kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-bookinfo-certs
total 0
drwxrwsrwt. 3 root istio-proxy 120 Feb 12 13:14 .
drwxr-xr-x. 1 root root        140 Feb 12 13:14 ..
drwxr-sr-x. 2 root istio-proxy  80 Feb 12 13:14 ..2022_02_12_13_14_13.407844833
lrwxrwxrwx. 1 root root         31 Feb 12 13:14 ..data -> ..2022_02_12_13_14_13.407844833
lrwxrwxrwx. 1 root root         14 Feb 12 13:14 tls.crt -> ..data/tls.crt
lrwxrwxrwx. 1 root root         14 Feb 12 13:14 tls.key -> ..data/tls.key

配置 bookinfo.com 主机的流量:

# 部署Bookinfo示例应用
[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
service/details unchanged
serviceaccount/bookinfo-details unchanged
deployment.apps/details-v1 unchanged
service/ratings unchanged
serviceaccount/bookinfo-ratings unchanged
deployment.apps/ratings-v1 unchanged
service/reviews unchanged
serviceaccount/bookinfo-reviews unchanged
deployment.apps/reviews-v1 unchanged
deployment.apps/reviews-v2 unchanged
deployment.apps/reviews-v3 unchanged
service/productpage unchanged
serviceaccount/bookinfo-productpage unchanged
deployment.apps/productpage-v1 unchanged
 
 
# 配置网关
[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https-bookinfo
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-bookinfo-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-bookinfo-certs/tls.key
    hosts:
    - "bookinfo.com"
EOF
 
# 配置VirtualService
[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "bookinfo.com"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
EOF

发送到 Bookinfo productpage 的进行验证:

[root@k8s-master01 istio-1.10.6]# curl -o /dev/null -s -v -w "%{http_code}\n" -HHost:bookinfo.com --resolve bookinfo.com:443:10.96.1.69 --cacert example.com.crt -HHost:bookinfo.com https://bookinfo.com:443/productpage
* Added bookinfo.com:443:10.96.1.69 to DNS cache
* About to connect() to bookinfo.com port 443 (#0)
*   Trying 10.96.1.69...
* Connected to bookinfo.com (10.96.1.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=bookinfo organization,CN=bookinfo.com
*       start date: Feb 12 13:10:28 2022 GMT
*       expire date: Feb 12 13:10:28 2023 GMT
*       common name: bookinfo.com
*       issuer: CN=example.com,O=example Inc.
> GET /productpage HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:bookinfo.com
> Host:bookinfo.com
>
...
200

修改Ingress Gateway:

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key
    hosts:
    - "httpbin.example.com"
EOF

发送到httpbin.example.com进行验证访问:

[root@k8s-master01 istio-1.10.6]# curl -v -HHost:httpbin.example.com --resolve httpbin.example.com:443:10.96.1.69 --cacert example.com.crt https://httpbin.example.com/status/418
* Added httpbin.example.com:443:10.96.1.69 to DNS cache
* About to connect() to httpbin.example.com port 443 (#0)
*   Trying 10.96.1.69...
* Connected to httpbin.example.com (10.96.1.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=httpbin organization,CN=httpbin.example.com
*       start date: Feb 12 12:36:54 2022 GMT
*       expire date: Feb 12 12:36:54 2023 GMT
*       common name: httpbin.example.com
*       issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Sat, 12 Feb 2022 13:31:06 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 46
<
 
    -=[ teapot ]=-
 
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
* Connection #0 to host httpbin.example.com left intact

SNI透传TLS

生成证书:

# 创建根证书和私钥来为您的服务签名证书
[root@k8s-master01 istio-1.10.6]# 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
Generating a 2048 bit RSA private key
...+++
.....+++
writing new private key to 'example.com.key'
-----
 
 
# 为 nginx.example.com 创建证书和私钥:
[root@k8s-master01 istio-1.10.6]# openssl req -out nginx.example.com.csr -newkey rsa:2048 -nodes -keyout nginx.example.com.key -subj "/CN=nginx.example.com/O=some organization"
Generating a 2048 bit RSA private key
.......+++
.......+++
writing new private key to 'nginx.example.com.key'
-----
[root@k8s-master01 istio-1.10.6]# openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in nginx.example.com.csr -out nginx.example.com.crt
Signature ok
subject=/CN=nginx.example.com/O=some organization
Getting CA Private Key

部署一个Nginx服务:

# 创建一个 Kubernetes 的 Secret 资源来保存服务的证书:
[root@k8s-master01 istio-1.10.6]# kubectl create secret tls nginx-server-certs --key nginx.example.com.key --cert nginx.example.com.crt
secret/nginx-server-certs created
 
 
# 为 NGINX 服务创建一个配置文件:
[root@k8s-master01 istio-1.10.6]# 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 服务的配置:
[root@k8s-master01 istio-1.10.6]#  kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf
 
# 部署 NGINX 服务
[root@k8s-master01 istio-1.10.6]#  cat <<EOF | istioctl kube-inject -f - | 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
    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

配置Gateway:

PASSTHROUGH tls TLS 模式,该模式指示 Gateway 以 AS IS 方式传递入口流量,而不终止 TLS。

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
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

为通过 Gateway 进入的流量配置路由:

[root@k8s-master01 istio-1.10.6]#  kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
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

访问验证:

[root@k8s-master01 istio-1.10.6]# curl -v --resolve "nginx.example.com:443:10.96.1.69" --cacert example.com.crt "https://nginx.example.com:443"
* Added nginx.example.com:443:10.96.1.69 to DNS cache
* About to connect() to nginx.example.com port 443 (#0)
*   Trying 10.96.1.69...
* Connected to nginx.example.com (10.96.1.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
*       subject: O=some organization,CN=nginx.example.com
*       start date: Feb 12 16:23:43 2022 GMT
*       expire date: Feb 12 16:23:43 2023 GMT
*       common name: nginx.example.com
*       issuer: CN=example.com,O=example Inc.
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: nginx.example.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.21.6
< Date: Sat, 12 Feb 2022 16:35:08 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT
< Connection: keep-alive
< ETag: "61f01158-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host nginx.example.com left intact

Egress Gateway

由于默认情况下,来自 Istio-enable Pod 的所有出站流量都会重定向到其 Sidecar 代理,集群外部 URL 的可访问性取决于代理的配置。默认情况下,Istio 将 Envoy 代理配置为允许传递未知服务的请求。尽管这为入门 Istio 带来了方便,但是,通常情况下,配置更严格的控制是更可取的。

这个任务向你展示了三种访问外部服务的方法:

  1. 允许 Envoy 代理将请求传递到未在网格内配置过的服务。
  2. 配置 service entries 以提供对外部服务的受控访问。
  3. 对于特定范围的 IP,完全绕过 Envoy 代理。

访问外部服务

部署示例应用:

[root@k8s-master01 istio-1.10.6]# cat  samples/sleep/sleep.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sleep
---
apiVersion: v1
kind: Service
metadata:
  name: sleep
  labels:
    app: sleep
    service: sleep
spec:
  ports:
  - port: 80
    name: http
  selector:
    app: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      terminationGracePeriodSeconds: 0
      serviceAccountName: sleep
      containers:
      - name: sleep
        image: curlimages/curl
        command: ["/bin/sleep", "3650d"]
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - mountPath: /etc/sleep/tls
          name: secret-volume
      volumes:
      - name: secret-volume
        secret:
          secretName: sleep-secret
          optional: true
---
[root@k8s-master01 istio-1.10.6]# kubectl apply -f samples/sleep/sleep.yaml
serviceaccount/sleep unchanged
service/sleep created
deployment.apps/sleep created

Envoy 转发流量到外部服务

开启Egress并配置不控制对外部服务的访问:

[root@k8s-master01 istio-1.10.6]# istioctl install --set meshConfig.outboundTrafficPolicy.mode=ALLOW_ANY
[root@k8s-master01 istio-1.10.6]# kubectl get istiooperator installed-state -n istio-system -o jsonpath='{.spec.meshConfig.outboundTrafficPolicy.mode}'
ALLOW_ANY

SOURCE_POD 向外部 HTTPS 服务发出两个请求,确保能够得到状态码为 200 的响应:


[root@k8s-master01 istio-1.10.6]# export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
[root@k8s-master01 istio-1.10.6]# kubectl exec "$SOURCE_POD" -c sleep -- curl -sSI https://www.baidu.com | grep  "HTTP/"; kubectl exec "$SOURCE_POD" -c sleep -- curl -sI https://edition.cnn.com | grep "HTTP/"
HTTP/1.1 200 OK
HTTP/2 200

恭喜!你已经成功地从网格中发送了 egress 流量。

控制对外部服务的访问

执行以下命令来将 global.outboundTrafficPolicy.mode 选项改为 REGISTRY_ONLY

[root@k8s-master01 istio-1.10.6]# istioctl install --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

SOURCE_POD 向外部 HTTPS 服务发出几个请求,验证它们现在是否被阻止:

[root@k8s-master01 istio-1.10.6]# kubectl exec "$SOURCE_POD" -c sleep -- curl -sSI https://www.baidu.com | grep  "HTTP/"; kubectl exec "$SOURCE_POD" -c sleep -- curl -sI https://edition.cnn.com | grep "HTTP/"
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.baidu.com:443
command terminated with exit code 35
command terminated with exit code 35

访问一个外部的 HTTP 服务

创建一个 ServiceEntry,以允许访问一个外部的 HTTP 服务:

[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL
EOF

SOURCE_POD 向外部的 HTTP 服务发出一个请求:

[root@k8s-master01 istio-1.10.6]#  kubectl exec -it $SOURCE_POD -c sleep -- curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.81.0-DEV",
    "X-Amzn-Trace-Id": "Root=1-6207e915-6fea3ceb26d238311ca6d68e",
    "X-B3-Sampled": "0",
    "X-B3-Spanid": "6afb8a8e94eeade9",
    "X-B3-Traceid": "f216a91c0454a97a6afb8a8e94eeade9",
    "X-Envoy-Attempt-Count": "1",
    "X-Envoy-Decorator-Operation": "httpbin.org:80/*",
    "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwoZCg1JU1RJT19WRVJTSU9OEggaBjEuMTAuNgrfAQoGTEFCRUxTEtQBKtEBCg4KA2FwcBIHGgVzbGVlcAoZCgxpc3Rpby5pby9yZXYSCRoHZGVmYXVsdAohChFwb2QtdGVtcGxhdGUtaGFzaBIMGgo1NTc3NDc0NTVmCiQKGXNlY3VyaXR5LmlzdGlvLmlvL3Rsc01vZGUSBxoFaXN0aW8KKgofc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtbmFtZRIHGgVzbGVlcAovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgZsYXRlc3QKGgoHTUVTSF9JRBIPGg1jbHVzdGVyLmxvY2FsCiAKBE5BTUUSGBoWc2xlZXAtNTU3NzQ3NDU1Zi00OGJkNQoWCglOQU1FU1BBQ0USCRoHZGVmYXVsdApJCgVPV05FUhJAGj5rdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvZGVmYXVsdC9kZXBsb3ltZW50cy9zbGVlcAoXChFQTEFURk9STV9NRVRBREFUQRICKgAKGAoNV09SS0xPQURfTkFNRRIHGgVzbGVlcA==",
    "X-Envoy-Peer-Metadata-Id": "sidecar~100.125.152.48~sleep-557747455f-48bd5.default~default.svc.cluster.local"
  }
}

注意由 Istio sidecar 代理添加的 headers: X-Envoy-Decorator-Operation

访问外部 HTTPS 服务

# 创建一个 ServiceEntry,允许对外部服务的访问
[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: baidu
spec:
  hosts:
  - www.baidu.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL
EOF
 
# 从 SOURCE_POD 往外部 HTTPS 服务发送请求
[root@k8s-master01 istio-1.10.6]# kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.baidu.com | grep  "HTTP/"     
HTTP/1.1 200 OK

管理到外部服务的流量

# 设置对 httpbin.org 服务访问的超时规则(请求大约在 5 秒内返回 200 (OK))
[root@k8s-master01 istio-1.10.6]# kubectl exec "$SOURCE_POD" -c sleep -- time curl -o /dev/null -sS -w "%{http_code}\n" http://httpbin.org/delay/5
200
real    0m 5.45s
user    0m 0.00s
sys     0m 0.00s
 
 
# 退出测试源 pod,使用 kubectl 设置调用外部服务 httpbin.org 的超时时间为 3 秒。
[root@k8s-master01 istio-1.10.6]# kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-ext
spec:
  hosts:
    - httpbin.org
  http:
  - timeout: 3s
    route:
      - destination:
          host: httpbin.org
        weight: 100
EOF
 
# 重新发出 curl 请求:
[root@k8s-master01 istio-1.10.6]# kubectl exec "$SOURCE_POD" -c sleep -- time curl -o /dev/null -sS -w "%{http_code}\n" http://httpbin.org/delay/5
504
real    0m 3.04s
user    0m 0.00s
sys     0m 0.00s

通过 egress 网关发起 TLS 连接

TLS 的发起过程由 egress 网关完成,而不是像之前示例演示的那样由 sidecar 完成

# 为 edition.cnn.com 定义一个 ServiceEntry:
[root@k8s-master01 istio-1.10.6]# 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 已被正确应用。
[root@k8s-master01 istio-1.10.6]# kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 301 Moved Permanently
server: envoy
retry-after: 0
content-length: 0
cache-control: public, max-age=300
location: https://edition.cnn.com/politics
accept-ranges: bytes
date: Sat, 12 Feb 2022 17:26:14 GMT
via: 1.1 varnish
set-cookie: countryCode=CN; Domain=.cnn.com; Path=/; SameSite=Lax
set-cookie: stateCode=SH; Domain=.cnn.com; Path=/; SameSite=Lax
set-cookie: geoData=shanghai|SH|200000|CN|AS|800|broadband|31.240|121.480; Domain=.cnn.com; Path=/; SameSite=Lax
x-served-by: cache-nrt18325-NRT
x-cache: HIT
x-cache-hits: 0
x-envoy-upstream-service-time: 242
 
HTTP/2 200
content-type: text/html; charset=utf-8
x-servedbyhost: ::ffff:127.0.0.1
access-control-allow-origin: *
cache-control: max-age=60
content-security-policy: default-src 'self' blob: https://*.cnn.com:* http://*.cnn.com:* *.cnn.io:* *.cnn.net:* *.turner.com:* *.turner.io:* *.ugdturner.com:* courageousstudio.com *.vgtf.net:*; script-src 'unsafe-eval' 'unsafe-inline' 'self' *; style-src 'unsafe-inline' 'self' blob: *; child-src 'self' blob: *; frame-src 'self' *; object-src 'self' *; img-src 'self' data: blob: *; media-src 'self' data: blob: *; font-src 'self' data: *; connect-src 'self' data: *; frame-ancestors 'self' https://*.cnn.com:* http://*.cnn.com:* https://*.cnn.io:* http://*.cnn.io:* *.turner.com:* https://www.google.com https://news.google.com https://www.google.co.uk https://amp-cnn-com.cdn.ampproject.org courageousstudio.com;
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
via: 1.1 varnish, 1.1 varnish
accept-ranges: bytes
date: Sat, 12 Feb 2022 17:26:15 GMT
age: 273
set-cookie: countryCode=CN; Domain=.cnn.com; Path=/; SameSite=None; Secure
set-cookie: stateCode=SH; Domain=.cnn.com; Path=/; SameSite=None; Secure
set-cookie: geoData=shanghai|SH|200000|CN|AS|800|broadband|31.240|121.480; Domain=.cnn.com; Path=/; SameSite=None; Secure
set-cookie: FastAB=0=6645,1=3296,2=5969,3=0326,4=5810,5=7055,6=7674,7=8126,8=2236,9=5177; Domain=.cnn.com; Path=/; Expires=Sat Jul 01 2023 00:00:00 GMT; SameSite=Lax
x-served-by: cache-iad-kcgs7200115-IAD, cache-hnd18720-HND
x-cache: HIT, MISS
x-cache-hits: 1, 0
x-timer: S1644686775.198386,VS0,VE211
vary: , Accept-Encoding
content-length: 1223432
 
 
# 为 edition.cnn.com 创建一个 egress Gateway,端口 443,以及一个 sidecar 请求的目标规则,sidecar 请求被直接导向 egress 网关。
 
 
[root@k8s-master01 istio-1.10.6]# 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 连接:
[root@k8s-master01 istio-1.10.6]#  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。
 
[root@k8s-master01 istio-1.10.6]#  kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 200 OK
date: Sat, 12 Feb 2022 17:28:51 GMT
server: envoy
content-length: 0

通过 egress 网关发起双向 TLS 连接

配置一个 egress 网关,为外部服务发起 TLS 连接,只是这次服务要求双向 TLS

生成客户端和服务器的证书与密钥

# 为您的服务签名证书创建根证书和私钥
[root@k8s-master01 istio-1.10.6]# 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
Generating a 2048 bit RSA private key
...........+++
.............................................+++
writing new private key to 'example.com.key'
-----
 
 
# 为 my-nginx.mesh-external.svc.cluster.local 创建证书和私钥
[root@k8s-master01 istio-1.10.6]# 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
Generating a 2048 bit RSA private key
...................................................+++
...............................................................................................+++
writing new private key to 'my-nginx.mesh-external.svc.cluster.local.key'
-----
[root@k8s-master01 istio-1.10.6]# 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
Signature ok
subject=/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization
Getting CA Private Key
 
 
# 生成客户端证书和私钥
[root@k8s-master01 istio-1.10.6]# openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
Generating a 2048 bit RSA private key
....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
.........+++
..........................................................................................+++
writing new private key to 'client.example.com.key'
-----
[root@k8s-master01 istio-1.10.6]# 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
Signature ok
subject=/CN=client.example.com/O=client organization
Getting CA Private Key

部署一个双向 TLS 服务器

为了模拟一个真实的支持双向 TLS 协议的外部服务, 在 Kubernetes 集群中部署一个 NGINX 服务器,该服务器运行在 Istio 服务网格之外,譬如:运行在一个没有开启 Istio sidecar proxy 注入的命名空间中。

# 创建一个命名空间,表示 Istio 网格之外的服务,mesh-external。注意在这个命名空间中,sidecar 自动注入是没有开启的,不会在 pods 中自动注入 sidecar proxy
[root@k8s-master01 istio-1.10.6]# kubectl create namespace mesh-external
namespace/mesh-external created
 
 
# 创建 Kubernetes Secrets ,保存服务器和 CA 的证书
[root@k8s-master01 istio-1.10.6]# 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
secret/nginx-server-certs created
[root@k8s-master01 istio-1.10.6]# kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
secret/nginx-ca-certs created
 
 
# 生成 NGINX 服务器的配置文件
[root@k8s-master01 istio-1.10.6]# 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 服务器的配置文件
[root@k8s-master01 istio-1.10.6]# kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
configmap/nginx-configmap created
 
 
# 部署 NGINX 服务器
[root@k8s-master01 istio-1.10.6]# 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 服务器:
 
[root@k8s-master01 istio-1.10.6]# 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 网关

# 生成 Kubernetes Secrets 保存客户端和 CA 的证书
[root@k8s-master01 istio-1.10.6]# kubectl create -n istio-system secret tls nginx-client-certs --key client.example.com.key --cert client.example.com.crt
kubectl create -n istio-system secret generic nginx-ca-certs --from-file=example.com.crt
secret/nginx-client-certs created
[root@k8s-master01 istio-1.10.6]# kubectl create -n istio-system secret generic nginx-ca-certs --from-file=example.com.crt
secret/nginx-ca-certs created
 
 
# 部署 istio-egressgateway 挂载新生成的 Secrets 的 Volume。使用的参数选项与生成 istio.yaml 中的一致,创建下面的 gateway-patch.json 文件:
[root@k8s-master01 istio-1.10.6]# cat > gateway-patch.json <<EOF
[{
  "op": "add",
  "path": "/spec/template/spec/containers/0/volumeMounts/0",
  "value": {
    "mountPath": "/etc/istio/nginx-client-certs",
    "name": "nginx-client-certs",
    "readOnly": true
  }
},
{
  "op": "add",
  "path": "/spec/template/spec/volumes/0",
  "value": {
  "name": "nginx-client-certs",
    "secret": {
      "secretName": "nginx-client-certs",
      "optional": true
    }
  }
},
{
  "op": "add",
  "path": "/spec/template/spec/containers/0/volumeMounts/1",
  "value": {
    "mountPath": "/etc/istio/nginx-ca-certs",
    "name": "nginx-ca-certs",
    "readOnly": true
  }
},
{
  "op": "add",
  "path": "/spec/template/spec/volumes/1",
  "value": {
  "name": "nginx-ca-certs",
    "secret": {
      "secretName": "nginx-ca-certs",
      "optional": true
    }
  }
}]
EOF
 
# 通过以下命令部署应用 istio-egressgateway
[root@k8s-master01 istio-1.10.6]# kubectl -n istio-system patch --type=json deploy istio-egressgateway -p "$(cat gateway-patch.json)"
 
 
# 验证密钥和证书被成功装载入 istio-egressgateway pod:
[root@k8s-master01 istio-1.10.6]# kubectl exec -n istio-system "$(kubectl -n istio-system get pods -l istio=egressgateway -o jsonpath='{.items[0].metadata.name}')" -- ls -al /etc/istio/nginx-client-certs /etc/istio/nginx-ca-certs

为 egress 流量配置双向 TLS

# 为 my-nginx.mesh-external.svc.cluster.local 创建一个 egress Gateway 端口为 443,以及目标规则和虚拟服务来引导流量流经 egress 网关并从 egress 网关流向外部服务。
[root@k8s-master01 istio-1.10.6]# 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 网关
 
[root@k8s-master01 istio-1.10.6]# 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
[root@k8s-master01 istio-1.10.6]#  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
        clientCertificate: /etc/istio/nginx-client-certs/tls.crt
        privateKey: /etc/istio/nginx-client-certs/tls.key
        caCertificates: /etc/istio/nginx-ca-certs/example.com.crt
        sni: my-nginx.mesh-external.svc.cluster.local
EOF
 
# 发送一个 HTTP 请求至 http://my-nginx.mesh-external.svc.cluster.local:
[root@k8s-master01 istio-1.10.6]# 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>

参考:https://istio.io/

3

评论区