• 安全保障基于软件全生命周期-Istio的认证机制


    在介绍Istio的认证机制前,先了解一些安全领域的理论知识。传统网络安全架构是基于边界的安全架构,企业构建网络安全体系时,先寻找安全边界,把网络划分为外网、内网、DMZ(DeMilitarized Zone)等不同的区域,然后在边界上部署防火墙、入侵检测等产品。传统的认证是以网络为中心,采用内网信任、边界防护、静态访问控制的机制。


    随着云计算、大数据、物联网等新技术与业务的融合,在安全方面提出了新的概念零信任架构(ZTA Zero Trust Architecture),零信任的核心原则是:从不信任、始终验证。安全架构的核心有如下几点

    • 以身份为中心
    • 以识别、持续认证、动态访问控制、授权、审计以及监测为链条
    • 以最小化实时授权为核心
    • 以多维信任算法为基础
    • 认证达末端

     零信任架构三大技术SIM包括
    软件定义边界(SDP,Software Defined Perimeter)
    SDP旨在使应用程序能在需要时部署安全边界,以便将服务于不安全的网络隔离开。
    身份识别与访问管理(IAM,Identity and Access Management)
    微隔离(MSG,Micro Segmentation)
    微隔离本质上是一种网络安全隔离技术,相比较传统的网络安全隔离,微隔离的区别在于将网络边界分割到尽可能小,这样能缓解传统边界安全理念下的边界过度信任带来的安全风险。

    Istio 中的安全性涉及多个组件:
    用于密钥和证书管理的证书颁发机构(CA
    配置 API 服务器分发给代理:认证策略/授权策略
    安全命名信息
    Sidecar和边缘代理作为Policy Enforcement Points(PEPs) 以保护客户端和服务器间的通信安全。
    一组Envoy代理扩展,用于管理遥测和审计。Istio PKI 使用 X.509 证书为每个工作负载都提供强大的身份标识。伴随着每个 Envoy 代理的 istio-agent(istio-agent是指sidecar容器中的pilot-agent进程)和 istiod 一起协作来大规模进行自动化密钥和证书轮换。如下图所示完成完成身份的供应。

    • istiod 提供 gRPC 服务以接受证书签名请求(CSRs)。当工作负载启动时,Envoy通过秘密发现服务(SDS)API向同容器内的istio-agent发送证书和密钥请求。
    • 在收到SDS请求后,istio-agent 创建私钥和CSR,然后将CSR及其凭据发送到istiod CA签名
    • istiod CA 验证 CSR 中携带的凭据,成功验证后签署 CSR 以生成证书。
    • Istio-agent 通过 Envoy SDS API 将私钥和从 Istio CA 收到的证书发送给 Envoy。
    • Istio-agent会监工作负载证书的有效期。上述CSR过程会周期性地重复以处理证书和密钥轮换

    Istio提供两种类型的认证:
    Peer authentication:用于服务到服务的认证,以验证进行连接的客户端。Istio提供双向TLS 作为传输认证的全栈解决方案,无需更改服务代码就可以启用它。
    Request authentication:用于最终用户认证,以验证附加到请求的凭据。 Istio使用JSON Web Token(JWT)验证启用请求级认证,并使用自定义认证实现或任何 OpenID Connect 的认证实现来简化的开发人员体验。

    安全命名:服务器身份(Server identities)被编码在证书里,但服务名称(service names)通过服务发现或 DNS 被检索。安全命名信息将服务器身份映射到服务名称。身份 A 到服务名称 B 的映射表示“授权 A 运行服务 B“。控制平面监视 apiserver,生成安全命名映射,并将其安全地分发到 PEPs。

    上面介绍了概念,接下来通过实际例子演示Istio如何完成认证的。创建三个namespace foo,bar,lagacy,其中foo,bar注入Istio sidecar,且三个namespace下都部署httpbin和sleep服务,这两个服务都来自Istio官网的sample目录下。创建对象完成后,查看bar下面的应用,可以看到是2/2,说明已经注入了Sidecar。接着尝试通过命令让bar namespace下的sleep应用通过curl命令访问httpbin应用,可以看到成功返回了200.

    接着在三个namespace下让sleep服务访问httpbin服务。访问命令如下,也全部访问成功

    for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
    

     接着查看是否存在peerauthentication对象和destinationRules,都不存在。查看httpbin/headers,可以看到header里面有Client-Cert信息。

    接着直接在主机上通过httpbin pod的IP地址访问headers信息,可以看到无Cert信息。

     

    通过上面的例子可以看到对于注入了Istio sidecard的应用来说,自动启动了mTLS的双向认证机制。接下来创建一个PeerAuthentication对象,模式是STRICT模式,PeerAuthentication的模式有三种,三种模式的区别如下所示:

    PERMISSIVE:工作负载接受双向 TLS 和纯文本流量。此模式在迁移因为没有 sidecar 而无法使用双向 TLS 的工作负载的过程中非常有用。一旦工作负载完成 sidecar 注入的迁移,应将模式切换为 STRICT。
    STRICT: 工作负载仅接收双向 TLS 流量。
    DISABLE:禁用双向 TLS。 从安全角度来看,除非您提供自己的安全解决方案,否则请勿使用此模式。

    1. kubectl apply -f - <
    2. apiVersion: security.istio.io/v1beta1
    3. kind: PeerAuthentication
    4. metadata:
    5. name: "default"
    6. namespace: "istio-system"
    7. spec:
    8. mtls:
    9. mode: STRICT
    10. EOF

    因为上述PeerAuthentication对象是创建在istio-system namespaces下面,即Istio的根namespace下,所以是全局生效。生效后再次用三个namespace下的sleep应用访问httpbin服务。可以看到legacy下的sleep服务访问foo,bar下的httpbin服务都失败了,因为legacy没有注入Istio sidecar,所以是纯文本流量,现在采用STRICT模式,所以会失败。Istio默认是采用PERMISSIVE。

    接着在foo namespace下创建新的PeerAuthentication,对于访问httpbin的流量DISABLE,即禁用双向TLS。

    1. cat <
    2. apiVersion: security.istio.io/v1beta1
    3. kind: PeerAuthentication
    4. metadata:
    5. name: "overwrite-example"
    6. namespace: "foo"
    7. spec:
    8. selector:
    9. matchLabels:
    10. app: httpbin
    11. mtls:
    12. mode: DISABLE
    13. EOF

    再次查看服务之间相互访问情况,可以看到Legacy下面的sleep应用访问foo下面的httpbin应用重新通了,只有bar下面的不同。因为上面的PA是创建在foo namespace。

    通过上面的例子演示,可以看到可以通过配置PeerAuthentication来精细化控制服务间的隔离,即实现微隔离,也符合SDP原则,即应用自身来确定部署的安全边界。

    上面介绍了PeerAuthentication(主要用户客户端和服务端都受Istio管理的场景),接着来看看Request Authentication(用户客户端不受Istio管理,无法使用mTLS的场景).。

    Request 认证策略指定验证 JSON Web Token(JWT)所需的值。 这些值包括:
    token 在请求中的位置
    请求的 issuer
    公共 JSON Web Key Set(JWKS)

    Istio会根据request认证策略中的规则检查提供的令牌(如果已提供),并拒绝令牌无效的请求。当请求不带有令牌时,默认情况下将接受它们。接着通过实际例子来看看Request Authentication如何工作。首先通过gateway把httpbin的服务暴露出来。

    1. kubectl apply -f - <
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: httpbin-gateway
    6. namespace: foo
    7. spec:
    8. selector:
    9. istio: ingressgateway # use Istio default gateway implementation
    10. servers:
    11. - port:
    12. number: 80
    13. name: http
    14. protocol: HTTP
    15. hosts:
    16. - "*"
    17. EOF
    18. kubectl apply -f - <
    19. apiVersion: networking.istio.io/v1alpha3
    20. kind: VirtualService
    21. metadata:
    22. name: httpbin
    23. namespace: foo
    24. spec:
    25. hosts:
    26. - "*"
    27. gateways:
    28. - httpbin-gateway
    29. http:
    30. - route:
    31. - destination:
    32. port:
    33. number: 8000
    34. host: httpbin.foo.svc.cluster.local
    35. EOF

    接着创建RequestAuthentication对象。

    1. kubectl apply -f - <
    2. apiVersion: "security.istio.io/v1beta1"
    3. kind: "RequestAuthentication"
    4. metadata:
    5. name: "jwt-example"
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. istio: ingressgateway
    11. jwtRules:
    12. - issuer: "testing@secure.istio.io"
    13. jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.14/security/tools/jwt/samples/jwks.json"
    14. EOF

     在Istio的官网上提供了生成token的python代码,可以通过这个脚本生成有效的token。通过ingress svc的IP地址访问服务,如果不带token,默认是接收请求。

    可以看到不带token的情况下,访问成功。

    通过Istio官网提供的脚本生成有效的token,生成有效token官网提供了两种方式。

    1. 方式一
    2. TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/demo.jwt -s)
    3. curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
    4. 方式二
    5. pip install jwcrypto
    6. TOKEN=$(./gen-jwt.py key.pem --expire=300 --iss "new-issuer@secure.istio.io")

    带着有效token访问ingress可以看到返回了200.如果把token替换成任意的字符串,返回了401,说明认证不通过。

     上面的例子中如果访问服务不带token则默认接受,这里通过生成AuthoriztionPolicy对象来控制如果不带token则拒绝请求。

    1. kubectl apply -f - <
    2. apiVersion: security.istio.io/v1beta1
    3. kind: AuthorizationPolicy
    4. metadata:
    5. name: "frontend-ingress"
    6. namespace: istio-system
    7. spec:
    8. selector:
    9. matchLabels:
    10. istio: ingressgateway
    11. action: DENY
    12. rules:
    13. - from:
    14. - source:
    15. notRequestPrincipals: ["*"]
    16. EOF

    AuthorizationPolicy对象生效后,不带token访问则refused,带上有效token访问,仍然能成功访问成功。关于AuthorizationPolicy的使用会在下一篇博客中详细介绍。

    通过上面的例子演示 AuthenticationRequest的工作过程。可以看到如果不受Istio管控的服务,通过AuthenticationRequest可以完成认证管控,保证应用安全。

  • 相关阅读:
    python实现的TopologySort
    使用Docker搭建Npm私服Verdaccio
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java新冠疫苗接种管理系统nt3mc
    IDL 文本编码、代码补全快捷方式、IDL doc、格式器、行号显示设置
    openwrt-编译模块-tc、nginx
    centos7离线安装PHP7.4.30
    map容器使用及员工分组实例
    二刷算法训练营Day45 | 动态规划(7/17)
    理想滤波器、巴特沃斯滤波器、高斯滤波器实现(包含低通与高通,代码实现与分析)
    Mac的入坑指南
  • 原文地址:https://blog.csdn.net/qiaotl/article/details/126016914