云原生 Istio 微服务网络安全体系

少年 · 2022年11月23日 · 最后由 tangoliver 回复于 2022年11月25日 · 1526 次阅读

前言

集群流量的入与出,是安全管控里面最重要的一环,对于集群的所有流量,如果我们都能管控起来,那么很多的风险,都可以防患于未然。

比如,入口流量,在内测期间,需要做到隔离外网,或者仅放通内测客户方 CIDR,减小风险,并给自己预留更多的时间来进行安全优化。

比如,出口流量,需要做到隔离内网,如果涉及到多租户的业务模式,还需要 namespace 甚至 pod 层级的网络隔离。

看到这里,大家第一反应可能就是,不是有安全组吗,配置一下安全组不就可以了吗?

首先,安全组只能提供一个大范围的出入限制,但是更精细的 namespace 或者 pod 层级的网络隔离,安全组是没有办法做到的。

其次,一般安全组的出口流量,不会做限制,这也就导致了如果 pod 被入侵,很容易被利用去访问第三方的恶意网站和挖矿。

再者,如果 pod 被入侵以后,没有精细的流量管控,也可以轻易对集群内部的服务发起进攻,比如数据库 mysql redis 等等,也容易造成重大损失。

基于以上这些风险情况,我基于 istio 进行了深入的思考和探索,并分享其微服务网络安全体系,希望能对大家有帮助。

安全架构核心 - istio

在流量管控体系中,我采取了 istio 作为核心,去管控集群的入和出。

使用 istio 的原因主要是为了跨集群的服务网格搭建,详情在这里 Istio 跨集群网络通信的落地实践

既然用了,那么基于 sidecar 去做文章,去管控所有 pod 的流量就顺理成章。

统一的出入口

入口 istio-ingress

统一入口的好处就是,集群里面只有 istio-ingress 是 LoadBalancer 状态,其他的服务全部统一都设置成 ClusterIP。

每个服务只需要编写对应的 Virtual Service,ingress 会自动根据 Virtual Service 来找到服务,并转发流量。

现在举例一个场景:

我在 test namespace 下部署了一个 nginx 的服务,只部署成 ClusterIP,如何把这个服务对外访问呢?

  1. 先给 test namespace 配置专属的 test-ingress-gateway,用以代理 test namespace 下的所有服务,并绑定唯一的出口 istio-ingress 。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: test-ingress-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"
  1. 配置 virtual service,告诉 istio-ingress,如果有匹配到 /nginx/ 的路由,重写成 /,并转发到 nginx.test.svc.cluster.local:80
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: test-nginx
spec:
  hosts:
    - "*"
  gateways:
    - ingress-gateway
  http:
    - match:
      - uri:
          prefix: "/nginx/"
      rewrite:
        uri: "/"
      route:
        - destination:
            port:
              number: 80
            host: nginx.test.svc.cluster.local

这样就实现 nginx 本身是 ClusterIP,却能够通过 istio-ingress 的入口进来访问,确保了入口的统一。

出口 istio-egress

统一出口的好处就是,能够对出站的流量进行管控,以防访问第三方危险网站,或者挖矿。

istio-egress 提供了 Register Only 的能力,即出口的流量,必须现在 isito 集群内部注册了服务,才能够访问,否则无法访问。

这时候可能大家会有个疑惑,如果有业务需求访问非集群内的服务怎么办呢?

但是注意了,istio-egress 规则是服务要在集群内注册,而不是服务一定要在集群内。

所以这里配合服务发现和注册系统即可。

举一个最简单的例子,比如我要访问一个云数据库mongodb

因为 istio-egress 开启了 Register Only,所以我们没办法直接访问的,需要先把 mongo 这个服务注册到集群内。

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: mongo
spec:
  hosts:
  - my-mongo.tcp.svc
  addresses:
  - $MONGODB_IP/32
  ports:
  - number: $MONGODB_PORT
    name: tcp
    protocol: TCP
  location: MESH_EXTERNAL
  resolution: STATIC
  endpoints:
  - address: $MONGODB_IP

此时再访问 mongo,即可调通,此时流量在集群中的关系如下图。

但是这个出口,是 pod 直接对外访问的出口,并不是从 istio-egress 出来的,如果我们需要让流量从 isito-egress 出来,还需要这样做。

  1. 为 mongo 这个 service entry 写明流量流向规则。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-mongo
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: mongo
  1. 再对流量转发编写 virtual service。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-mongo-through-egress-gateway
spec:
  hosts:
  - my-mongo.tcp.svc
  gateways:
  - mesh
  - istio-egressgateway
  tcp:
  - match:
    - gateways:
      - mesh
      destinationSubnets:
      - $MONGODB_IP/32
      port: $MONGODB_PORT
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: mongo
        port:
          number: $EGRESS_GATEWAY_MONGODB_PORT
  - match:
    - gateways:
      - istio-egressgateway
      port: $EGRESS_GATEWAY_MONGODB_PORT
    route:
    - destination:
        host: my-mongo.tcp.svc
        port:
          number: $MONGODB_PORT
      weight: 100

这次流量在集群中的关系如下。

这样我们就实现了,对外访问,同统一由 istio-egress 去访问。

强制统一出入口

看到这里的标题,大家肯定有个疑惑,前面不是已经统一了出入口吗,为什么这里还需要强制统一出入口?

因为这里其实还有个风险隐患。

istio 之所以能统一所有 pod 的流量出入,主要是基于 sidecar,所以,如果有个违法的 pod 启动不带 sidecar,它就完全可以无视 isito 的规则肆意访问。

istio 本身是不能够保证 istio-ingress 和 istio-egress 是集群唯一的出入口,这就需要我们做一些强制手段,确保集群出入口只有这两个,这样就算 pod 启动绕开了 sidecar,由于集群本身堵死了其他出口,它们也无从发起攻击,进一步降低风险。

这个强制,就需要用到 Network Policy 插件。

namespace 网络隔离

Network Policy 提供了 namespace 甚至 pod 层级力度的网络隔离,对于所有的 namespace,我们可以限制出入口只有 istio-ingress 和 isito-egress,实现强制统一出入口。

  • 强制入口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-networkpolicy
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              istio: system
          podSelector:
            matchLabels:
              app: istio-ingress
  • 强制出口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-networkpolicy
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
      - namespaceSelector:
          matchLabels:
            kube-system: "true"
            ports:
            - protocol: UDP
              port: 53
    - to:
      - namespaceSelector:
          matchLabels:
            istio: system
        podSelector:
          matchLabels:
            app: istio-egress

经过这一层的隔离,大大加强了集群网络的安全。

pod 网络精准控制

上面提到的 Network Policy,对 pod 的隔离的力度是没办法颗粒化的,比如,要么就同个 namespace 下的 pod 都可以相互访问,要么就是同个 namespace 下的 pod 都不可以相互访问。

但是很多时候我们业务场景是这样的,A 服务是前端,B 服务是后端,C 服务是数据库,我希望 A 能访问 B,B 能访问 C,但是 A 不能访问 C。

这就需要对 pod 有更精细的流量权限控制。

apiVersion: "security.istio.io/v1beta1"
kind: AuthorizationPolicy
metadata:
  name: mongodb-policy
  namespace: default
spec:
 selector:
   matchLabels:
     app: mongodb
 action: ALLOW
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/B"]
   to:
   - operation:
       ports: ["27017"]

这样就实现了只允许 B 服务访问数据库,而其他服务无法访问。

tls 双向认证

TLS 加密也是 istio 提供的一个强大能力,主要是预防中间人的攻击,比如入侵了某个注册好的 pod,伪装自己人发起攻击,所以开启双向 TLS 认证,也可以降低这种风险。

apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT

开启 STRICT 模式之后,只允许服务间双向 TLS 通信。

安全架构图

看到这里,相信大家对这个安全架构有了一定的了解,所以我这里可以抛出最后总的架构图,加深大家的印象。

安全架构性能

五大场景

说完了安全,再来说一下性能。很多时候,我们需要结合业务场景,来对安全架构做一些取舍,如果只谈安全不谈性能,无异于耍流氓。

下面我们来比较一下五种情况的出口流量场景:

  • 绕过 istio sidecar 直接访问 mongo

  • 使用 istio sidecar 并通过 service entry 访问 mongo

  • 只允许通过 istio-egress 访问 mongo

  • 只允许通过 istio-egress 访问 mongo,并开启 tls 双向认证

  • 只允许通过 istio-egress 访问 mongo,并开启 tls 双向认证,且通过 sni proxy 适配访问通配符域名

性能测试

针对以上场景,在 istio 第三方测评有如下性能测试数据:

  • 吞吐量

  • 响应时间

  • CPU

可以看到,前三种场景性能上差距不大,而后两种场景,也就是加了双向认证和域名通配代理之后,带来了明显的性能下降。

总结

随着组件和限制的增加,安全性也在增加,但其实成本也在增加。

所以这其实只是一个模板,根据业务的需求及成本去做加减法,优化适配成适合自己业务的架构,才是最好的。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 1 条回复 时间 点赞

👍 支持下,虽然现在还看不上太懂,相信后面会用上

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册