Ingress安全网关

Ingress安全网关_第1张图片

目录

Ingress安全网关_第2张图片

文章目录

    • 目录
    • 本节实战
    • TCP 流量拆分
      • == 实战:TCP 流量拆分-2023.11.15(测试成功)==
    • Ingress安全网关
      • Kubernetes Ingress
        • == 实战:Kubernetes Ingress-2023.11.15(测试成功)==
      • Ingress Gateway
        • Ingress Gateway
          • == 实战:Ingress Gateway-2023.11.15(测试成功)==
        • 安全网关
          • == 实战:istio安全网关-2023.11.16(测试成功)==
            • 1.单主机配置 TLS Ingress Gateway
            • 2.多个主机配置 TLS Ingress Gateway
            • 3.双向 TLS Ingress Gateway
        • Ingress Gateway TLS 透传
          • 1.TLS Termination
          • 2.TLS Origination
          • 3.TLS 透传
            • == 实战:TLS 透传-2023.11.16(测试成功)==
    • 关于我
    • 最后

本节实战

实战名称
实战:TCP 流量拆分-2023.11.15(测试成功)
实战:Kubernetes Ingress-2023.11.15(测试成功)
实战:Ingress Gateway-2023.11.15(测试成功)
实战:istio安全网关-2023.11.16(测试成功)
实战:TLS 透传-2023.11.16(测试成功)

TCP 流量拆分

实战:TCP 流量拆分-2023.11.15(测试成功)

实验环境:

k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)

[root@master1 ~]#istioctl version
client version: 1.19.3
control plane version: 1.19.3
data plane version: 1.19.3 (8 proxies)

实验软件:

链接:https://pan.baidu.com/s/1pMnJxgL63oTlGFlhrfnXsA?pwd=7yqb
提取码:7yqb
2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功) --本节测试yaml在此目录里

image-20231105111842627

实验步骤:

Ingress安全网关_第3张图片

graph LR
	A[实战步骤] -->B(1、 部署应用)
	A[实战步骤] -->C(2、 创建Gatewy,vs,rs)
	A[实战步骤] -->D(3、 配置TCP 流量拆分)
	A[实战步骤] -->E(4、 验证)

前面我们试验了 HTTP 的流量拆分,那么 TCP 流量拆分如何实现呢?TCP 流量拆分是指将来自客户端的 TCP 流量分发到多个后端服务的过程。在 Istio 中,TCP 流量拆分是通过 VirtualServiceDestinationRule 对象来实现的。

这里我们来演示如何将 TCP 流量从微服务的一个版本迁移到另一个版本。我们将会把 100% 的 TCP 流量分配到 tcp-echo:v1,接着,再通过配置 Istio 路由权重把 20% 的 TCP 流量分配到 tcp-echo:v2

  • 首先部署 tcp-echo 服务的 v1v2 两个版本:
kubectl apply -f samples/tcp-echo/tcp-echo-services.yaml

查看:

[root@master1 istio-1.19.3]#kubectl get po -l app=tcp-echo
NAME                           READY   STATUS    RESTARTS   AGE
tcp-echo-v1-64c9b4bc95-hc5z7   2/2     Running   0          69s
tcp-echo-v2-5b5657b486-dlsqf   2/2     Running   0          69s
[root@master1 istio-1.19.3]#kubectl get svc tcp-echo
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
tcp-echo   ClusterIP   10.99.200.47   <none>        9000/TCP,9001/TCP   71s

该资源对象中包含 tcp-echo 的两个 Deployment 对象,包含 9000、9001、9002 三个端口,但是通过 Service 只暴露了 9000 和 9001 两个端口,省略了 9002 端口是为了测试透传能力,内容如下所示:

apiVersion: v1
kind: Service
metadata:
  name: tcp-echo
  labels:
    app: tcp-echo
    service: tcp-echo
spec:
  ports:
    - name: tcp
      port: 9000
    - name: tcp-other
      port: 9001
  # Port 9002 is omitted intentionally for testing the pass through filter chain.
  selector:
    app: tcp-echo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tcp-echo-v1
  labels:
    app: tcp-echo
    version: v1
spec:
  selector:
    matchLabels:
      app: tcp-echo
      version: v1
  template:
    metadata:
      labels:
        app: tcp-echo
        version: v1
    spec:
      containers:
        - name: tcp-echo
          image: docker.io/istio/tcp-echo-server:1.2
          imagePullPolicy: IfNotPresent
          args: ["9000,9001,9002", "one"]
          ports:
            - containerPort: 9000
            - containerPort: 9001
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tcp-echo-v2
  labels:
    app: tcp-echo
    version: v2
spec:
  selector:
    matchLabels:
      app: tcp-echo
      version: v2
  template:
    metadata:
      labels:
        app: tcp-echo
        version: v2
    spec:
      containers:
        - name: tcp-echo
          image: docker.io/istio/tcp-echo-server:1.2
          imagePullPolicy: IfNotPresent
          args: ["9000,9001,9002", "two"]
          ports:
            - containerPort: 9000
            - containerPort: 9001
  • 然后同样再部署一个 sleep 示例应用,作为发送请求的测试源:
kubectl apply -f samples/sleep/sleep.yaml

对应的 Pod 如下所示:

[root@master1 istio-1.19.3]#kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
……
sleep-9454cc476-nx668            2/2     Running   0          11s
tcp-echo-v1-64c9b4bc95-hc5z7     2/2     Running   0          27m
tcp-echo-v2-5b5657b486-dlsqf     2/2     Running   0          27m
  • 接下来我们将所有 TCP 流量路由到微服务 tcp-echov1 版本,应用下面的资源清单即可:
$ kubectl apply -f samples/tcp-echo/tcp-echo-all-v1.yaml

查看:

[root@master1 istio-1.19.3]#kubectl get gateway
NAME               AGE
bookinfo-gateway   2d16h
tcp-echo-gateway   41s
[root@master1 istio-1.19.3]#kubectl get vs
NAME          GATEWAYS               HOSTS             AGE
bookinfo      ["bookinfo-gateway"]   ["*"]             2d16h
……
tcp-echo      ["tcp-echo-gateway"]   ["*"]             46s
[root@master1 istio-1.19.3]#kubectl get dr
NAME                   HOST          AGE

tcp-echo-destination   tcp-echo      57s

该资源对象中包含了 tcp-echo 服务的 GatewayDestinationRuleVirtualService 对象,内容如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: tcp-echo-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 31400 # istio-ingressgateway 包含该端口
        name: tcp
        protocol: TCP
      hosts:
        - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: tcp-echo-destination
spec:
  host: tcp-echo
  subsets:
    - name: v1
      labels:
        version: v1 # 匹配 tcp-echo-v1 的标签
    - name: v2
      labels:
        version: v2 # 匹配 tcp-echo-v2 的标签
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tcp-echo
spec:
  hosts:
    - "*"
  gateways:
    - tcp-echo-gateway # 关联上面的 Gateway
  tcp:
    - match:
        - port: 31400 # 匹配上面 Gateway 的端口
      route:
        - destination: # 路由导 tcp-echo 服务
            host: tcp-echo
            port:
              number: 9000
            subset: v1 # 全部路由到 v1 子集

上面的资源清单中单独定义了一个 Gateway 对象,用来暴露 TCP 流量的端口,然后通过 VirtualService 对象将 TCP 流量路由到 tcp-echo 服务的 v1 版本,这样所有的流量都会被路由到 tcp-echo:v1,需要注意的是 VirtualService 对象中配置的是 tcp 协议,而不是 http 协议了,因为我们这里要处理的是一个 TCP 服务,其他方式方法都是一样的。

查看下istio-ingressgateway内容:

[root@master1 istio-1.19.3]#kubectl get svc istio-ingressgateway -nistio-system -oyaml
……
 ports:
  - name: status-port
    nodePort: 31410
    port: 15021
    protocol: TCP
    targetPort: 15021
  - name: http2
    nodePort: 31666
    port: 80
    protocol: TCP
    targetPort: 8080
  - name: https
    nodePort: 32213
    port: 443
    protocol: TCP
    targetPort: 8443
  - name: tcp
    nodePort: 30291 ##
    port: 31400 ##
    protocol: TCP
    targetPort: 31400 ##证明容器里也有一个应用监听31400端口
  - name: tls
    nodePort: 31787
    port: 15443
    protocol: TCP
    targetPort: 15443
……
[root@master1 istio-1.19.3]#kubectl get po istio-ingressgateway-9c8b9b586-vj44l  -nistio-system -oyaml
……
image: docker.io/istio/proxyv2:1.19.3
    imagePullPolicy: IfNotPresent
    name: istio-proxy
    ports:
    - containerPort: 15021
      protocol: TCP
    - containerPort: 8080 #http
      protocol: TCP
    - containerPort: 8443 #https
      protocol: TCP
    - containerPort: 31400 #TCP流量
      protocol: TCP
    - containerPort: 15443
      protocol: TCP
    - containerPort: 15090
      name: http-envoy-prom
      protocol: TCP
……
  • 我们可以用下面的方式来测试,首先获得 Ingress Gateway 的访问入口地址和端口:
export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

查看:

echo $TCP_INGRESS_PORT #istio-ingressgateway svc的nodePort(监听器端口),因为这里的SVC是LoadBalancer类型的,因为我们通过NodePort来访问就行。
echo $INGRESS_HOST

[root@master1 istio-1.19.3]#echo $TCP_INGRESS_PORT #svc的nodePort
30291
[root@master1 istio-1.19.3]#echo $INGRESS_HOST
172.29.9.63
  • 然后通过 sleep 容器中的 nc 命令来发送 TCP 流量:
[root@master1 istio-1.19.3]#export SLEEP=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
[root@master1 istio-1.19.3]#echo $SLEEP
sleep-9454cc476-nx668

$ for i in {1..20}; do
  kubectl exec "$SLEEP" -c sleep -- sh -c "(date; sleep 1) | nc $INGRESS_HOST $TCP_INGRESS_PORT";
done
one Mon Nov 13 03:23:59 UTC 2023
one Mon Nov 13 03:24:00 UTC 2023
one Mon Nov 13 03:24:01 UTC 2023
# ......

可以看到所有时间戳都有一个前缀 one,说明所有流量都被路由到 tcp-echov1 版本,当然我们也可以在 v1 版本的 Pod 中查看到相应的日志。

Ingress安全网关_第4张图片

查看v1 版本的 Pod 中查看到相应的日志:

[root@master1 istio-1.19.3]#kubectl logs -f tcp-echo-v1-64c9b4bc95-hc5z7
listening on [::]:9002, prefix: one
listening on [::]:9000, prefix: one
listening on [::]:9001, prefix: one


request: Tue Nov 14 23:44:32 UTC 2023
response: one Tue Nov 14 23:44:32 UTC 2023
request: Tue Nov 14 23:44:34 UTC 2023
response: one Tue Nov 14 23:44:34 UTC 2023
request: Tue Nov 14 23:44:35 UTC 2023
response: one Tue Nov 14 23:44:35 UTC 2023
request: Tue Nov 14 23:44:36 UTC 2023
response: one Tue Nov 14 23:44:36 UTC 2023
request: Tue Nov 14 23:44:37 UTC 2023
……
  • 接下来我们将 20% 的 TCP 流量路由到 tcp-echo:v2 版本,应用下面的资源清单即可:
kubectl apply -f samples/tcp-echo/tcp-echo-20-v2.yaml

该清单文件内容如下所示:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tcp-echo
spec:
  hosts:
    - "*"
  gateways:
    - tcp-echo-gateway
  tcp:
    - match:
        - port: 31400
      route:
        - destination:
            host: tcp-echo
            port:
              number: 9000
            subset: v1
          weight: 80 # 80% 的流量路由到 v1
        - destination:
            host: tcp-echo
            port:
              number: 9000
            subset: v2
          weight: 20 # 20% 的流量路由到 v2

这里我们更新了 VirtualService 对象,将 tcp-echo 服务的 v1 版本和 v2 版本的权重分别设置为 80% 和 20%,这样 20% 的流量就会被路由到 tcp-echo:v2 版本了,这和前面的 HTTP 基于权重的流量拆分是一样的方式。

  • 应用成功后,我们再次发送 TCP 流量来验证:
$ export SLEEP=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ for i in {1..20}; do
  kubectl exec "$SLEEP" -c sleep -- sh -c "(date; sleep 1) | nc $INGRESS_HOST $TCP_INGRESS_PORT";
done
one Mon Nov 13 03:29:32 UTC 2023
one Mon Nov 13 03:29:33 UTC 2023
one Mon Nov 13 03:29:34 UTC 2023
one Mon Nov 13 03:29:36 UTC 2023
one Mon Nov 13 03:29:37 UTC 2023
one Mon Nov 13 03:29:38 UTC 2023
one Mon Nov 13 03:29:39 UTC 2023
two Mon Nov 13 03:29:40 UTC 2023
one Mon Nov 13 03:29:41 UTC 2023
one Mon Nov 13 03:29:42 UTC 2023
two Mon Nov 13 03:29:43 UTC 2023
# ......

Ingress安全网关_第5张图片

输出结果中大约有 20% 的时间戳带有前缀 two,说明 80% 的 TCP 流量被路由到 tcp-echov1 版本,而 20% 的流量被路由到 v2 版本,证明我们的 TCP 服务基于权重的流量拆分配置成功了。

  • 当然最后我们同样可以去查看下 Envoy Sidecar 的具体配置,注意我们这里是通过 Istio Ingress Gateway 来暴露 TCP 流量的端口的,所以我们这里直接查看 31400 端口的监听器配置即可:
$ istioctl proxy-config listeners istio-ingressgateway-9c8b9b586-s6s48 -n istio-system --port 31400 -o yaml
- address:
    socketAddress:
      address: 0.0.0.0
      portValue: 31400
  # ......
  filterChains:
  - filters:
    # ......
    - name: envoy.filters.network.tcp_proxy
      typedConfig:
        '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
        # ......
        statPrefix: tcp-echo.default
        weightedClusters:
          clusters:
          - name: outbound|9000|v1|tcp-echo.default.svc.cluster.local
            weight: 80
          - name: outbound|9000|v2|tcp-echo.default.svc.cluster.local
            weight: 20
  name: 0.0.0.0_31400
  trafficDirection: OUTBOUND

我们可以发现上面的监听器配置下面的过滤器链中直接使用的是一个 envoy.filters.network.tcp_proxy 过滤器,用来处理 TCP 流量,而不是 HTTP 流量了,而且直接在该过滤器下面配置了 weightedClusters 权重集群,用来实现基于权重的流量拆分,可以看到 v1v2 版本的权重分别为 80 和 20,符合我们的预期。

  • 最后测试完后可以清理下资源:
kubectl delete -f samples/tcp-echo/tcp-echo-all-v1.yaml
kubectl delete -f samples/sleep/sleep.yaml
kubectl delete -f samples/tcp-echo/tcp-echo-services.yaml

测试结束。

Ingress安全网关

Ingress安全网关_第6张图片

我们知道在 Kubernetes 中我们会使用 Ingress 来暴露 HTTP 流量的入口,在 Istio 中我们是通过 Gateway 来暴露流量入口的,那么我们是否可以使用 Ingress 对象来支持 Istio 的流量入口呢?答案是肯定的,但是我们还是建议使用 Gateway 而不是 Ingress 来使用 Istio 提供的完整功能集,例如丰富的流量管理和安全功能。

k8s gateway api。

Kubernetes Ingress

实战:Kubernetes Ingress-2023.11.15(测试成功)

实验环境:

k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)

[root@master1 ~]#istioctl version
client version: 1.19.3
control plane version: 1.19.3
data plane version: 1.19.3 (8 proxies)

实验软件:

链接:https://pan.baidu.com/s/1VEYZ8jPDGsHXvnxwkKHzZA?pwd=062k
提取码:062k
2023.11.15-实战:Kubernetes Ingress-2023.11.15(测试成功)

Ingress安全网关_第7张图片

应用程序在以下链接里:

链接:https://pan.baidu.com/s/1pMnJxgL63oTlGFlhrfnXsA?pwd=7yqb
提取码:7yqb
2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)

image-20231105111842627

实验步骤:

Ingress安全网关_第8张图片

graph LR
	A[实战步骤] -->B(1、 部署服务端测试应用)
	A[实战步骤] -->C(2、 创建ingress)
	A[实战步骤] -->D(3、 测试)

下面我们来使用 Ingress 资源配置 Istio 的入口网关,使用 Kubernetes Ingress 可以暴露从集群外到集群内服务的 HTTP 和 HTTPS 路由。

  • 这里我们部署一个 httpbin 应用,用来测试:
kubectl apply -f samples/httpbin/httpbin.yaml

查看:

[root@master1 istio-1.19.3]#kubectl get po -l app=httpbin
NAME                       READY   STATUS    RESTARTS   AGE
httpbin-86869bccff-7lpjx   2/2     Running   0          2d14h
[root@master1 istio-1.19.3]#kubectl get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
httpbin       ClusterIP   10.111.57.195    <none>        8000/TCP            2d14h
……
  • 创建后我们就可以创建一个 Ingress 对象来暴露 httpbin 服务了:

⚠️ 特别注意:

Ingress安全网关_第9张图片

#httpbin-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: istio
  name: httpbin
spec:
  rules:
    - host: httpbin.k8s.local
      http:
        paths:
          - path: /status
            pathType: Prefix
            backend:
              service:
                name: httpbin
                port:
                  number: 8000

可以看到这个 Ingress 对象和我们在 Kubernetes 中的使用方法没有什么区别,唯一不同的是我们这里使用了一个 kubernetes.io/ingress.class 注解来告知 Istio Ingress Gateway 它应该处理该 Ingress,否则它将被忽略。

我们应用上面的资源清单:

[root@master1 istio-1.19.3]#kubectl apply -f httpbin-ingress.yaml
ingress.networking.k8s.io/httpbin created
Warning: annotation "kubernetes.io/ingress.class" is deprecated, please use 'spec.ingressClassName' instead

Ingress安全网关_第10张图片

  • 我们可以通过 kubectl get ingress 命令来查看下 Ingress 对象的状态:
$ kubectl get ingress
NAME      CLASS    HOSTS              ADDRESS   PORTS   AGE
httpbin   <none>   httpbin.k8s.local            80      6s
  • 然后我们可以使用 curl 来访问 httpbin 服务:
# 获取 Ingress Gateway 的地址和端口
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export INGRESS_HOST=$(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

echo $INGRESS_PORT
echo $INGRESS_HOST

# 访问 httpbin 服务
$ curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
HTTP/1.1 200 OK
server: istio-envoy
date: Mon, 13 Nov 2023 06:10:13 GMT
content-type: text/html; charset=utf-8
# .......

注意使用 -H 标志将 Host 的 HTTP 头设置为 httpbin.k8s.local, 因为 Ingress 中已经配置为处理访问该域名的请求,但是在测试环境中,该域名并没有相应的 DNS 绑定,当然我们也可以直接在 /etc/hosts 中添加一个映射(当然是istio-ingressgateway-9c8b9b586-vj44lpod所在node节点了)。

Ingress安全网关_第11张图片

  • 访问未指定的其他 URL 时,将返回 HTTP 404 错误:
$ curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/headers"
HTTP/1.1 404 Not Found
#......

image-20231115204427150

  • 注意下:自己当前istio环境
[root@master1 istio-1.19.3]#export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
[root@master1 istio-1.19.3]#export INGRESS_HOST=$(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
[root@master1 istio-1.19.3]#echo $INGRESS_PORT
31666
[root@master1 istio-1.19.3]#echo $INGRESS_HOST
172.29.9.63

[root@master1 istio-1.19.3]#kubectl get po -owide -l istio=ingressgateway -nistio-system
NAME                                   READY   STATUS    RESTARTS   AGE    IP            NODE    NOMINATED NODE   READINESS GATES
istio-ingressgateway-9c8b9b586-vj44l   1/1     Running   0          3d6h   10.244.2.15   node2   <none>           <none>
[root@master1 istio-1.19.3]#kubectl get svc -nistio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
istio-egressgateway    ClusterIP      10.109.85.119    <none>        80/TCP,443/TCP                                                               8d
istio-ingressgateway   LoadBalancer   10.105.233.167   <pending>     15021:31410/TCP,80:31666/TCP,443:32213/TCP,31400:30291/TCP,15443:31787/TCP   8d
istiod                 ClusterIP      10.109.185.251   <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        8d
  • 在 Kubernetes 1.18 中,添加了新资源 IngressClass,以替换 Ingress 资源上的 kubernetes.io/ingress.class 注解,我们也可以使用该资源来替换注解的方式,需要将 controller 字段设置为 istio.io/ingress-controller,如下所示:
#httpbin-ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: istio
spec:
  controller: istio.io/ingress-controller # 指定 Ingress Controller 为 istio
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
spec:
  ingressClassName: istio # 指定 IngressClass 为 istio
  rules:
    - host: httpbin.k8s.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: httpbin
                port:
                  number: 8000

上面这种方式更加简洁,而且可以避免使用注解的方式,但是需要注意的是,该资源对象只能在 Kubernetes 1.18 及以上版本中使用。

  • 应用上面ingress对象
#先删除旧的ingress对象
[root@master1 istio-1.19.3]#kubectl delete -f httpbin-ingress.yaml 
ingress.networking.k8s.io "httpbin" deleted

[root@master1 istio-1.19.3]#kubectl apply -f httpbin-ingress2.yaml 
ingressclass.networking.k8s.io/istio created
ingress.networking.k8s.io/ingress created

#查看
[root@master1 istio-1.19.3]#kubectl get ingressclass
NAME    CONTROLLER                    PARAMETERS   AGE
istio   istio.io/ingress-controller   <none>       27s
[root@master1 istio-1.19.3]#kubectl get ingress
NAME      CLASS   HOSTS               ADDRESS   PORTS   AGE
ingress   istio   httpbin.k8s.local             80      35s
  • 再次测试
[root@master1 istio-1.19.3]#curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
HTTP/1.1 200 OK
server: istio-envoy
date: Wed, 15 Nov 2023 12:51:24 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: 6

[root@master1 istio-1.19.3]#curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/headers"
HTTP/1.1 200 OK
server: istio-envoy
date: Wed, 15 Nov 2023 12:51:29 GMT
content-type: application/json
content-length: 597
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 12

符合预期,测试结束。

  • 记得回收掉刚才创建的资源
[root@master1 istio-1.19.3]#kubectl delete -f httpbin-ingress2.yaml 
ingressclass.networking.k8s.io "istio" deleted
ingress.networking.k8s.io "ingress" deleted

此外 Ingress 还可以进行 TLS 设置,Istio 支持此功能,但是引用的 Secret 必须存在于 istio-ingressgateway 部署的命名空间(通常是 istio-system)中。

通过 Kubernetes Ingress 我们可以将流量导入到 Istio 中,但是对于一些高级的流量管理功能,比如路由、熔断、限流等就很难实现了,所以我们还是建议使用 Istio 的 Gateway 资源来暴露流量入口。

Ingress Gateway

Ingress安全网关_第12张图片

前面我们都是通过 Istio 提供的 Gateway 资源来暴露流量入口,与 Ingress 相比,Gateway 提供了更广泛的自定义和灵活性,并允许将 Istio 功能(例如监控和路由规则)应用于进入集群的流量。

Ingress Gateway
实战:Ingress Gateway-2023.11.15(测试成功)

实验环境:

k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)

[root@master1 ~]#istioctl version
client version: 1.19.3
control plane version: 1.19.3
data plane version: 1.19.3 (8 proxies)

实验软件:

链接:https://pan.baidu.com/s/1C-ZlPHuXzzXGPAlNEYdySA?pwd=lpyx
提取码:lpyx
2023.11.15-实战:Ingress Gateway-2023.11.15(测试成功)

image-20231115215707041

应用程序在以下链接里:

链接:https://pan.baidu.com/s/1pMnJxgL63oTlGFlhrfnXsA?pwd=7yqb
提取码:7yqb
2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)

image-20231105111842627

实验步骤:

Ingress安全网关_第13张图片

graph LR
	A[实战步骤] -->B(1、 部署服务端测试应用)
	A[实战步骤] -->C(2、 创建ingress)
	A[实战步骤] -->D(3、 测试)

Ingress Gateway 描述在网格边界运作的负载均衡器,用于接收传入的 HTTP/TCP 连接。它会配置暴露的端口、协议等,但与 Kubernetes Ingress 资源不同,Gateway 对象不会包括任何流量路由配置,和路由规则相关的配置需要使用 VirtualServiceDestinationRule 资源来实现。

  • 比如上面的 httpbin 应用,如果通过 Gateway 对象来暴露流量入口,那么我们需要创建一个 Gateway 对象,然后再创建一个 VirtualService 对象来配置流量路由,内容如下所示:
#ingress-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "httpbin.k8s.local"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - "httpbin.k8s.local"
  gateways:
    - httpbin-gateway
  http:
    - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
      route:
        - destination:
            port:
              number: 8000
            host: httpbin

上面的资源清单我们就通过 Gateway 对象暴露了 HTTP 流量的入口,然后通过 VirtualService 对象来配置流量路由,一共配置了两个路由规则,允许流量流向路径 /status/delaygateways 列表指定了哪些请求允许通 httpbin-gateway 网关,所有其他外部请求均被拒绝并返回 404 响应。

  • 直接应用上面的资源清单
kubectl apply -f ingress-gateway.yaml
  • 然后我们可以通过 curl 来访问 httpbin 服务:
# 获取 Ingress Gateway 的地址和端口
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export INGRESS_HOST=$(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

# 访问 httpbin 服务
$ curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
HTTP/1.1 200 OK
server: istio-envoy
date: Mon, 13 Nov 2023 06:32:58 GMT
content-type: text/html; charset=utf-8
# .......

$ curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/delay/2" #延迟2s

Ingress安全网关_第14张图片

  • 如果请求 /status/delay 以外的路径,将会返回 404 错误:
$ curl -s -I -HHost:httpbin.k8s.local "http://$INGRESS_HOST:$INGRESS_PORT/headers"
HTTP/1.1 404 Not Found
...

测试结束。

  • 回收刚才创建的资源
kubectl delete -f ingress-gateway.yaml

当然除了这最基本的功能之外,Gateway 还支持很多其他高级功能,比如 TLS、SNI 等。

安全网关

我们已经了解了如何使用 Gateway 对象来对外暴露 HTTP 服务,那么我们如何使用 TLS 或 mTLS 来暴露安全的 HTTPS 服务呢?

实战:istio安全网关-2023.11.16(测试成功)

实验环境:

k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)

[root@master1 ~]#istioctl version
client version: 1.19.3
control plane version: 1.19.3
data plane version: 1.19.3 (8 proxies)

实验软件:

链接:https://pan.baidu.com/s/1jw67Xvj-2ZYl3uuq59kO1g?pwd=p2mr
提取码:p2mr
–来自百度网盘超级会员V8的分享
2023.11.16-实战:istio安全网关-2023.11.16(测试成功)

Ingress安全网关_第15张图片

应用程序在以下链接里:

链接:https://pan.baidu.com/s/1pMnJxgL63oTlGFlhrfnXsA?pwd=7yqb
提取码:7yqb
2023.11.5-实战:BookInfo 示例应用-2023.11.5(测试成功)

image-20231105111842627

前期准备:

  • 这里我们还是使用 httpbin 应用来演示:
kubectl apply -f samples/httpbin/httpbin.yaml

接下来我们需要使用 openssl 命令来生成客户端和服务器证书和密钥。

⚠️ 注意:

以下证书、私钥创建的有点多哦,注意下。

(0)

  • 首先创建用于服务签名的根证书和私钥:
mkdir example_certs1
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs1/example.com.key -out example_certs1/example.com.crt

image-20231116060013152

[root@master1 ~]#ll example_certs1/
total 8
-rw-r--r-- 1 root root 1164 Nov 16 05:59 example.com.crt
-rw-r--r-- 1 root root 1708 Nov 16 05:59 example.com.key

(1)

  • 接着为 httpbin.example.com 创建证书和私钥:
openssl req -out example_certs1/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"

openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 0 -in example_certs1/httpbin.example.com.csr -out example_certs1/httpbin.example.com.crt

image-20231116060451784

Ingress安全网关_第16张图片

  • 创建第二组相同类型的证书和密钥:(用同一组根证书为一个域名签发了2套证书,用于后期测试)
mkdir example_certs2
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs2/example.com.key -out example_certs2/example.com.crt

openssl req -out example_certs2/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs2/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
openssl x509 -req -sha256 -days 365 -CA example_certs2/example.com.crt -CAkey example_certs2/example.com.key -set_serial 0 -in example_certs2/httpbin.example.com.csr -out example_certs2/httpbin.example.com.crt

查看:

[root@master1 ~]#ll example_certs2/
total 20
-rw-r--r-- 1 root root 1164 Nov 16 06:08 example.com.crt
-rw-r--r-- 1 root root 1704 Nov 16 06:08 example.com.key
-rw-r--r-- 1 root root 1054 Nov 16 06:08 httpbin.example.com.crt
-rw-r--r-- 1 root root  948 Nov 16 06:08 httpbin.example.com.csr
-rw-r--r-- 1 root root 1704 Nov 16 06:08 httpbin.example.com.key

(2)

  • helloworld.example.com 生成证书和私钥:
openssl req -out example_certs1/helloworld.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/helloworld.example.com.key -subj "/CN=helloworld.example.com/O=helloworld organization"

openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/helloworld.example.com.csr -out example_certs1/helloworld.example.com.crt

Ingress安全网关_第17张图片

(3)

  • 生成客户端证书和私钥
openssl req -out example_certs1/client.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/client.example.com.key -subj "/CN=client.example.com/O=client organization"

openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/client.example.com.csr -out example_certs1/client.example.com.crt
  • 您可以通过运行以下命令确认您拥有所有需要的文件:
$ ls example_cert*
example_certs1:
client.example.com.crt          example.com.key                 httpbin.example.com.crt
client.example.com.csr          helloworld.example.com.crt      httpbin.example.com.csr
client.example.com.key          helloworld.example.com.csr      httpbin.example.com.key
example.com.crt                 helloworld.example.com.key

example_certs2:
example.com.crt         httpbin.example.com.crt httpbin.example.com.key
example.com.key         httpbin.example.com.csr
1.单主机配置 TLS Ingress Gateway

接下来我们就可以在 Gateway 对象中配置 TLS 了。

  • 首先我们需要创建一个 Secret 对象,用来存储证书和密钥:
$ kubectl create -n istio-system secret tls httpbin-credential \
  --key=example_certs1/httpbin.example.com.key \
  --cert=example_certs1/httpbin.example.com.crt

查看:

[root@master1 ~]#kubectl get secret -nistio-system
NAME                 TYPE                DATA   AGE
httpbin-credential   kubernetes.io/tls   2      11s
……
  • 然后在 Gateway 对象中需要定义一个 443 端口的网关,并将 credentialName 的值设置为 httpbin-credential,该值与 Secret 的名称相同,TLS 模式的值应为 SIMPLE,内容如下所示:
#tls-gateway.yaml
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: SIMPLE
        credentialName: httpbin-credential # 必须和 secret 对象名称一致
      hosts:
        - httpbin.example.com
  • 接下来,通过定义相应的 VirtualService 对象来配置网关的入口流量路由:
#tls-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - "httpbin.example.com"
  gateways:
    - mygateway
  http:
    - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
      route:
        - destination:
            port:
              number: 8000
            host: httpbin
  • 部署上面2个对象

完整清单:

#tls-gateway.yaml
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: SIMPLE
        credentialName: httpbin-credential # 必须和 secret 对象名称一致
      hosts:
        - httpbin.example.com
        
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - "httpbin.example.com"
  gateways:
    - mygateway
  http:
    - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
      route:
        - destination:
            port:
              number: 8000
            host: httpbin

部署:

[root@master1 istio-1.19.3]#kubectl apply -f tls-gateway.yaml 
gateway.networking.istio.io/mygateway created
virtualservice.networking.istio.io/httpbin created

查看:

[root@master1 istio-1.19.3]#kubectl get gateway
NAME               AGE
……
mygateway          26s
[root@master1 istio-1.19.3]#kubectl get vs
NAME          GATEWAYS               HOSTS                     AGE
……
httpbin       ["mygateway"]          ["httpbin.example.com"]   26s
……
  • 应用上面的资源对象后,我们就可以向 httpbin 服务发送 HTTPS 请求了:

这里要用到的是istio-ingressgateway的443端口:

[root@master1 istio-1.19.3]#kubectl get svc -nistio-system
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                                      AGE
istio-egressgateway    ClusterIP      10.109.85.119    <none>        80/TCP,443/TCP                                                               8d
istio-ingressgateway   LoadBalancer   10.105.233.167   <pending>     15021:31410/TCP,80:31666/TCP,443:32213/TCP,31400:30291/TCP,15443:31787/TCP   8d
istiod                 ClusterIP      10.109.185.251   <none>        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        8d
# 获取 Ingress Gateway 的地址和 HTTPS 端口
export INGRESS_HOST=$(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
echo $INGRESS_HOST
echo $SECURE_INGRESS_PORT
#[root@master1 istio-1.19.3]#echo $INGRESS_HOST
#172.29.9.63
#[root@master1 istio-1.19.3]#echo $SECURE_INGRESS_PORT
#32213

# 访问 httpbin 服务
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
# ......
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example_certs1/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: Nov 13 06:58:40 2023 GMT
*       expire date: Nov 12 06:58:40 2024 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
# ......

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

Ingress安全网关_第18张图片

  • 接着我们可以删除网关的 Secret 然后使用不同的证书和密钥重新创建它来更改网关的凭据:
kubectl -n istio-system delete secret httpbin-credential

# 创建新的证书和密钥
kubectl create -n istio-system secret tls httpbin-credential \
  --key=example_certs2/httpbin.example.com.key \
  --cert=example_certs2/httpbin.example.com.crt
  • 使用新的证书链和 curl 来访问 httpbin 服务:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
  --cacert example_certs2/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
...
HTTP/1.1 418 Unknown
...

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

Ingress安全网关_第19张图片

  • 如果使用之前的证书链来访问 httpbin,则会失败:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
 --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
# ......
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example_certs1/example.com.crt
  CApath: none
* Server certificate:
*       subject: O=httpbin organization,CN=httpbin.example.com
*       start date: Nov 13 06:59:17 2023 GMT
*       expire date: Nov 12 06:59:17 2024 GMT
*       common name: httpbin.example.com
*       issuer: CN=example.com,O=example Inc.
* NSS error -8182 (SEC_ERROR_BAD_SIGNATURE)
* Peer's certificate has an invalid signature.
* Closing connection 0
curl: (60) Peer's certificate has an invalid signature.
# ......

测试结束。

2.多个主机配置 TLS Ingress Gateway

上面是为单个主机配置 TLS 入口网关的方法。

此外我们还可以为多个主机(例如 httpbin.example.comhelloworld-v1.example.com)配置入口网关,入口网关配置有与每个主机相对应的唯一凭据。

  • 首先删除并使用原始证书和密钥重新创建 Secret 来恢复上一个示例中的 httpbin 凭据:
kubectl -n istio-system delete secret httpbin-credential

kubectl create -n istio-system secret tls httpbin-credential \
  --key=example_certs1/httpbin.example.com.key \
  --cert=example_certs1/httpbin.example.com.crt
  • 然后我们启动 helloworld-v1 示例:

还可以这样搞哦:

# 创建 service=helloworld 的服务
$ kubectl apply -f samples/helloworld/helloworld.yaml -l service=helloworld

# 创建 version=v1 的应用
$ kubectl apply -f samples/helloworld/helloworld.yaml -l version=v1
  • 然后创建 helloworld-credential Secret:
$ kubectl create -n istio-system secret tls helloworld-credential --key=example_certs1/helloworld.example.com.key --cert=example_certs1/helloworld.example.com.crt
  • 然后使用 httpbin.example.comhelloworld.example.com 主机配置入口网关,创建如下所示的 Gateway 对象:
#tls2-gateway.yaml
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-httpbin
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: httpbin-credential
      hosts:
        - httpbin.example.com
    - port:
        number: 443
        name: https-helloworld
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: helloworld-credential
      hosts:
        - helloworld.example.com

这里我们为 443 端口定义一个具有两个 server 配置的网关,将每个端口上的 credentialName 值分别设置为 httpbin-credentialhelloworld-credential,将 TLS 模式设置为 SIMPLE

  • 然后定义相应的 VirtualService 来配置网关的流量路由。
#tls2-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
    - helloworld.example.com
  gateways:
    - mygateway
  http:
    - match:
        - uri:
            exact: /hello
      route:
        - destination:
            host: helloworld
            port:
              number: 5000
  • 直接应用上面的资源对象即可

完整清单如下:

#tls2-gateway.yaml
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-httpbin
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: httpbin-credential
      hosts:
        - httpbin.example.com
    - port:
        number: 443
        name: https-helloworld
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: helloworld-credential
      hosts:
        - helloworld.example.com
        
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
    - helloworld.example.com
  gateways:
    - mygateway
  http:
    - match:
        - uri:
            exact: /hello
      route:
        - destination:
            host: helloworld
            port:
              number: 5000

部署:

kubectl apply -f tls2-gateway.yaml

查看:

[root@master1 ~]#kubectl get gateway
NAME               AGE
mygateway          20m
[root@master1 ~]#kubectl get vs
NAME          GATEWAYS               HOSTS                        AGE
helloworld    ["mygateway"]          ["helloworld.example.com"]   19s
httpbin       ["mygateway"]          ["httpbin.example.com"]      20m
  • 然后向 helloworld.example.com 发送 HTTPS 请求:
$ curl -v -HHost:helloworld.example.com --resolve "helloworld.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
 --cacert example_certs1/example.com.crt "https://helloworld.example.com:$SECURE_INGRESS_PORT/hello"
# ......
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example_certs1/example.com.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=helloworld organization,CN=helloworld.example.com
*       start date: Nov 13 07:01:57 2023 GMT
*       expire date: Nov 12 07:01:57 2024 GMT
*       common name: helloworld.example.com
*       issuer: CN=example.com,O=example Inc.
# ......
< HTTP/1.1 200 OK
# ......
Hello version: v1, instance: helloworld-v1-b6c45f55-85l49

Ingress安全网关_第20张图片

  • 同样向 httpbin.example.com 发送一个 HTTPS 请求,仍然返回一个茶壶:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
 --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
# ......

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

测试结束。

3.双向 TLS Ingress Gateway

同样我们还可以扩展网关的定义以支持双向 TLS。双向 TLS 简称 mTLS,是一种相互身份验证的方法。mTLS 通过验证他们都拥有正确的私人密钥来确保网络连接两端的各方都是他们声称的身份。他们各自的 TLS 证书中的信息提供了额外的验证。

k8s里很多地方都是一个双向认证。

Ingress安全网关_第21张图片

通常在 TLS 中,服务器有一个 TLS 证书和一个公钥/私钥对,而客户端没有,典型的 TLS 流程是这样运作的:

  • 客户端连接到服务器
  • 服务器出示其 TLS 证书
  • 客户端验证服务器的证书
  • 客户端和服务器通过加密的 TLS 连接交换信息

Ingress安全网关_第22张图片

然而,在 mTLS 中,客户端和服务器都有一个证书,并且双方都使用它们的公钥/私钥对进行身份验证。与常规 TLS 相比,mTLS 中有一些额外步骤来验证双方。

  • 客户端连接到服务器
  • 服务器出示其 TLS 证书
  • 客户端验证服务器的证书
  • 客户端出示其 TLS 证书
  • 服务器验证客户端的证书
  • 服务器授予访问权限
  • 客户端和服务器通过加密的 TLS 连接交换信息

Ingress安全网关_第23张图片

接下来我们就来使用 mTLS 来配置 Ingress Gateway。

  • 首先还是删除其 Secret 并创建一个新的来更改入口网关的凭据,需要注意的是服务器使用 CA 证书来验证其客户端,我们必须使用名称 cacert 来持有 CA 证书。
$ kubectl -n istio-system delete secret httpbin-credential

$ kubectl create -n istio-system secret generic httpbin-credential \
  --from-file=tls.key=example_certs1/httpbin.example.com.key \
  --from-file=tls.crt=example_certs1/httpbin.example.com.crt \
  --from-file=ca.crt=example_certs1/example.com.crt
  • 接下来更改 Gateway 的定义将 TLS 模式设置为 MUTUAL
#tls3-gateway.yaml
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: MUTUAL # 设置为 MUTUAL,双向 TLS
        credentialName: httpbin-credential
      hosts:
        - httpbin.example.com

部署:

kubectl apply -f tls3-gateway.yaml
  • 应用后我们来尝试使用之前的方法发送 HTTPS 请求:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
# ......
*   CAfile: example_certs1/example.com.crt
  CApath: none
* NSS: client certificate not found (nickname not specified)
* NSS error -12227 (SSL_ERROR_HANDSHAKE_FAILURE_ALERT)
* SSL peer was unable to negotiate an acceptable set of security parameters.
* Closing connection 0
curl: (35) NSS: client certificate not found (nickname not specified)

Ingress安全网关_第24张图片

  • 从提示可以看出,客户端证书没有找到,由于我们这里配置的是双向 TLS 的方式,我们没有将客户端证书传递给 curl,我们可以通过 --cert--key 标志来进行传递:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
 --cacert example_certs1/example.com.crt --cert example_certs1/client.example.com.crt --key example_certs1/client.example.com.key \
 "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
# ......
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: example_certs1/example.com.crt
  CApath: none
* NSS: client certificate from file
*       subject: O=client organization,CN=client.example.com
*       start date: Nov 13 07:02:22 2023 GMT
*       expire date: Nov 12 07:02:22 2024 GMT
*       common name: client.example.com
*       issuer: CN=example.com,O=example Inc.
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*       subject: O=httpbin organization,CN=httpbin.example.com
*       start date: Nov 13 06:58:40 2023 GMT
*       expire date: Nov 12 06:58:40 2024 GMT
*       common name: httpbin.example.com
*       issuer: CN=example.com,O=example Inc.
< HTTP/1.1 418 Unknown
# ......
<

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

到这里我们就验证了通过 TLS 或 mTLS 将服务暴露到服务网格外。

  • 测试完成后记得清理下资源:(6)
# 删除网关配置和路由:
kubectl delete gateway mygateway
kubectl delete virtualservice httpbin helloworld

# 删除 Secret、证书和密钥:
kubectl delete -n istio-system secret httpbin-credential helloworld-credential
rm -rf ./example_certs1 ./example_certs2

# 关闭 httpbin 和 helloworld 服务:
kubectl delete -f samples/httpbin/httpbin.yaml
kubectl delete deployment helloworld-v1
kubectl delete service helloworld
Ingress Gateway TLS 透传

前面我们了解了如何为 HTTP 服务配置 HTTPS 访问入口,那如果我们的后台服务本身就是 HTTPS 的,那么如何为 HTTPS 服务配置 HTTPS 访问入口即配置 Ingress Gateway 执行 SNI 透传,而不是对传入请求进行 TLS 终止。

既然提到了 TLS 终止,那么我们可以先了解下什么是 TLS 终止(TLS Termination)。

Ingress安全网关_第25张图片

1.TLS Termination

它的主要作用是,作为一个前置代理服务器接收外部到达的加密 TLS 流量,然后将其解密为 HTTP 明文,最后再将流量转发到内部的某个服务。

Ingress安全网关_第26张图片

在实际应用中,内部的服务通常是以 HTTP 明文的方式通信,然后通过一个边界入口网关(ingress gateway)统一处理所有的 TLS 流量。这样 TLS 对所有的内部服务都是透明的,无需对每个服务去配置证书和私钥。通过一个统一的入口配置,我们还可以做很多事情,如日志,路由,路由策略等。

当然,对于一些安全级别较高的内部服务来说,未加密的流量可能是不可接受的,我们也可以配置来将加密的流量透传到该服务中,也就是这里我们需要的 SNI 透传。

同样的如果反过来,就是 TLS Origination

2.TLS Origination

作为一个代理服务器,接收内部服务的 HTTP 明文流量,然后将其加密,最后转发到一个 HTTPS 服务上,该服务既可以是内部,也可以是外部的,但看起来就像是一个内部的服务,流程如下,

Ingress安全网关_第27张图片

作为与边界入口网关对立的存在,出口网关也通常放置在网络的边界。所有的出口流量都被它接管,在这个节点上我们可以统一实施一些访问控制策略,或监控,或日志等,这和 Ingres Gateway 的功能其实是一样的,最大的不同在于将明文流量加密再转发。

3.TLS 透传
实战:TLS 透传-2023.11.16(测试成功)

实验环境:

k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)

[root@master1 ~]#istioctl version
client version: 1.19.3
control plane version: 1.19.3
data plane version: 1.19.3 (8 proxies)

实验软件:

链接:https://pan.baidu.com/s/1tpP4vWjOUqNyW2rBLqzVZg?pwd=0l54
提取码:0l54
–来自百度网盘超级会员V8的分享
2023.11.16-实战:TLS 透传-2023.11.16(测试成功)

Ingress安全网关_第28张图片

接下来我们用一个 NGINX 服务来演示下 TLS 透传的配置。首先在 Kubernetes 集群中创建一个 NGINX 服务,然后通过 Gateway 给这个服务配置一个域名是 nginx.example.com 的访问入口。

首先生成客户端和服务端的证书和密钥,同样我们这里使用 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
  • nginx.example.com 创建证书和私钥:
openssl req -out nginx.example.com.csr -newkey rsa:2048 -nodes -keyout nginx.example.com.key -subj "/CN=nginx.example.com/O=some organization"

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

查看:

[root@master1 ~]#ll nginx.example.com.*
-rw-r--r-- 1 root root 1050 Nov 16 07:24 nginx.example.com.crt
-rw-r--r-- 1 root root  940 Nov 16 07:24 nginx.example.com.csr
-rw-r--r-- 1 root root 1704 Nov 16 07:24 nginx.example.com.key
  • 接着创建一个 Kubernetes 的 Secret 资源来保存服务的证书:
kubectl create secret tls nginx-server-certs --key nginx.example.com.key --cert 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 <| 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
  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
  • 要测试 NGINX 服务是否已成功部署,需要从其 Sidecar 代理发送请求,并忽略检查服务端的证书(使用 curl-k 选项)。确保正确打印服务端的证书,即 common name (CN) 等于 nginx.example.com 即可:
$ kubectl get pods -l run=my-nginx
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-74df679cd5-5g7ss   2/2     Running   0          5m47s

$ 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: Nov 13 08:27:26 2023 GMT
*  expire date: Nov 12 08:27:26 2024 GMT
*  issuer: O=example Inc.; CN=example.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
} [5 bytes data]

> GET / HTTP/1.1
> User-Agent: curl/7.58.0
> Host: nginx.example.com
# ......
<!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>
# ......

Ingress安全网关_第29张图片

到这里我们的 HTTPS 服务就准备好了。

  • 接下来我们就可以配置 Ingress Gateway 来将流量透传到 NGINX 服务中了,在 Gateway 对象中定义 443 端口的网关,需要注意的是将 TLS 模式设置为 PASSTHROUGH,该模式指示 Gateway 以透传方式传递入口流量,而不终止 TLS,内容如下所示:
#tls4-gateway.yaml
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
  • 然后为通过 Gateway 进入的流量配置路由规则:
#tls4-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx
spec:
  hosts:
    - nginx.example.com
  gateways:
    - mygateway
  tls:
    - match:
        - port: 443
          sniHosts: # 指定 SNI 主机名
            - nginx.example.com
      route:
        - destination:
            host: my-nginx
            port:
              number: 443

需要注意的是,这里的 VirtualService 对象中我们配置的 tls 字段了,描述了用于路由未终止的 TLS 流量(TLS/HTTPS)的匹配条件和动作。它通过 sniHosts 指定了 SNI 主机名,以便 Gateway 可以将流量路由到正确的 VirtualService

  • 部署资源

完整清单如下:

#tls4-gateway.yaml
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
        
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx
spec:
  hosts:
    - nginx.example.com
  gateways:
    - mygateway
  tls:
    - match:
        - port: 443
          sniHosts: # 指定 SNI 主机名
            - nginx.example.com
      route:
        - destination:
            host: my-nginx
            port:
              number: 443

部署:

[root@master1 ~]#kubectl apply -f tls4-gateway.yaml 
gateway.networking.istio.io/mygateway created
virtualservice.networking.istio.io/nginx created
  • 应用上面的资源对象后,我们就可以向 nginx.example.com 发送 HTTPS 请求了:
# 获取 Ingress Gateway 的地址和 HTTPS 端口
export INGRESS_HOST=$(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')


# 访问 nginx 服务
$ curl -v --resolve "nginx.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" --cacert example.com.crt "https://nginx.example.com:$SECURE_INGRESS_PORT"
# ......
* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate:
*       subject: O=some organization,CN=nginx.example.com
*       start date: Nov 13 08:27:26 2023 GMT
*       expire date: Nov 12 08:27:26 2024 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:30808
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.21.5
< Date: Mon, 13 Nov 2023 08:50:27 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT
< Connection: keep-alive
< ETag: "61cb2d26-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>

Ingress安全网关_第30张图片

可以看到,我们成功访问了 NGINX 服务。

测试结束。

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

微信二维码
x2675263825 (舍得), qq:2675263825。

Ingress安全网关_第31张图片

微信公众号
《云原生架构师实战》

Ingress安全网关_第32张图片

个人博客站点

http://onedayxyy.cn/

Ingress安全网关_第33张图片

语雀

https://www.yuque.com/xyy-onlyone

Ingress安全网关_第34张图片

csdn

https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

Ingress安全网关_第35张图片

知乎

https://www.zhihu.com/people/foryouone

Ingress安全网关_第36张图片

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

Ingress安全网关_第37张图片

你可能感兴趣的:(istio)