hugo-teek is loading...

6、Istio安全

最后更新于:

6、Istio 安全

img

目录

image-20231125080337605

[toc]

本节实战

实战名称
🚩 实战:对等认证(默认的宽容模式)-2023.11.24(测速成功)
🚩 实战:对等认证(全局严格 mTLS 模式)-2023.11.24(测速成功)
🚩 实战:对等认证(命名空间级别策略)-2023.11.24(测速成功)
🚩 实战:对等认证(为每个工作负载启用双向 TLS)-2023.11.24(测速成功)
🚩 实战:对等认证(为每个端口配置不同的对等认证策略)-2023.11.24(测速成功)
🚩 实战:请求认证-2023.11.25(测试成功)
🚩 实战:HTTP 请求授权-20223.11.27(测试成功)
🚩 实战:TCP 请求授权-20223.11.28(测试成功)

1、安全概述

安全是一个非常重要的话题,但也是平时容易被忽略的一个话题,我们在开发应用的时候,往往会忽略安全,但是当应用上线后,安全问题就会暴露出来,这时候就会造成很大的损失。Istio 通过在服务之间注入 Sidecar 代理,来实现对服务之间的流量进行控制和监控,从而实现服务之间的安全通信。

接下来我们将从**证书管理、认证、授权**等几个方面来学习 Istio 的安全机制。

将单一应用程序拆分为微服务可提供各种好处,包括更好的灵活性、可伸缩性以及服务复用的能力。但是,微服务也有特殊的安全需求:

  • 为了抵御中间人攻击,需要流量加密。
  • 为了提供灵活的服务访问控制,需要双向 TLS 和细粒度的访问策略。
  • 要确定谁在什么时候做了什么,需要审计工具。

Istio 尝试提供全面的安全解决方案来解决所有这些问题,Istio 安全性可以减轻针对你的数据、端点、通信和平台的内外威胁。

img

Istio 为微服务提供了无侵入,可插拔的安全框架。应用不需要修改代码,就可以利用 Istio 提供的双向 TLS 认证实现服务身份认证,并基于服务身份信息提供细粒度的访问控制。Istio 安全的高层架构如下图所示:

img

上图展示了 Istio 中的服务认证授权两部分。服务认证是通过控制面和数据面一起实现的:

  • 控制面:Istiod 中实现了一个 CA (Certificate Authority,证书机构) 服务器。该 CA 服务器负责为网格中的各个服务签发证书,并将证书分发给数据面的各个服务的边车代理。
  • 数据面:在网格中的服务相互之间发起 plain(原始的,未加密的) HTTP/TCP 通信时,和服务同一个 pod 中的边车代理会拦截服务请求,采用证书和对端服务的边车代理进行双向 TLS 认证并建立一个 TLS 连接,使用该 TLS 连接来在网络中传输数据。

Istio 的授权功能为网格中的工作负载提供网格、命名空间和工作负载级别的访问控制,这种控制层级提供了许多优点:

  • 工作负载到工作负载以及最终用户到工作负载的授权。
  • 一个简单的 API:它包括一个单独的并且很容易使用和维护的 AuthorizationPolicy CRD。
  • 灵活的语义:运维人员可以在 Istio 属性上定义自定义条件,并使用 DENYALLOW 动作。
  • 高性能:Istio 授权是在 Envoy 本地强制执行的。
  • 高兼容性:原生支持 HTTP、HTTPS 和 HTTP2,以及任意普通 TCP 协议。

**授权策略对服务器端 Envoy 代理的入站流量实施访问控制。**每个 Envoy 代理都运行一个授权引擎,该引擎在运行时授权请求。当请求到达代理时,授权引擎根据当前授权策略评估请求上下文,并返回授权结果 ALLOWDENY。运维人员可以通过 YAML 资源清单文件来指定 Istio 授权策略。

2、证书签发流程

默认情况下,Istio CA 生成自签名根证书和密钥,并使用它们来签署工作负载证书。为了保护根 CA 密钥,我们应该使用在安全机器上离线运行的根 CA(比如使用 Hashicorp Vault 进行管理),并使用根 CA 向每个集群中运行的 Istio CA 颁发中间证书。Istio CA 可以使用管理员指定的证书和密钥对工作负载证书进行签名,并将管理员指定的根证书作为信任根分发给工作负载。

img

1.签发证书

当我们有了 Istio CA 根证书后就可以使用它来签发工作负载证书了,那么整个的证书签发流程又是怎样的呢?如下图所示:

img

  • Envoy 向 pilot-agent 发起一个 SDS (Secret Discovery Service) 请求(启动的时候),要求获取自己的证书和私钥。
  • pilot-agent 生成私钥和 CSR 证书签名请求,向 Istiod 发送证书签发请求,请求中包含 CSR 和该 Pod 中服务的身份信息。
  • Istiod 提供 gRPC 服务以接受证书签名请求,根据请求中服务的身份信息(如果是 Kubernetes 则使用 Service Account)为其签发证书,将证书返回给 pilot-agent
  • pilot-agent 将证书和私钥通过 SDS 接口返回给 Envoy。
  • pilot-agent 还会监控工作负载证书的过期时间,上述过程会定期重复进行证书和密钥轮换。

这个流程和我们自己用手动方式去进行证书签发是一样的,所以我们需要先了解下证书签发的流程。Istio CA 根证书就是一个证书颁发机构,平时如果要为我们自己的网站申请 HTTPS 证书我们也是去一些正规的 CA 机构进行申请,而这个申请的信息就是生成的 CSR 证书签名请求,然后我们将这个 CSR 和身份信息提交给 CA 机构,CA 机构根据这些信息为我们签发证书,然后我们就可以使用这个证书了,只是我们这里在不同的组件上来执行这些操作,整体流程是一样的。

istio-ca-root-cert

 1[root@master1 ~]#kubectl get cm -nistio-system
 2NAME                                  DATA   AGE
 3……
 4istio-ca-root-cert                    1      15d
 5……
 6[root@master1 ~]#
 7
 8
 9[root@master1 ~]#kubectl get po istiod-644f5d55fc-gs96f -nistio-system -oyaml
10……
11    - mountPath: /var/run/secrets/istiod/ca
12      name: istio-csr-ca-configmap
13      readOnly: true
14
15……
16  - configMap:
17      defaultMode: 420
18      name: istio-ca-root-cert
19      optional: true
20    name: istio-csr-ca-configmap

image-20231123122545714

2.身份认证

另外要通过服务证书来实现网格中服务的身份认证,必须首先确保服务从控制面获取自身证书的流程是安全的。Istio 通过 Istiod 和 pilog-agent 之间的 gRPC 通道传递 CSR 和证书,因此在这两个组件进行通信时,双方需要先验证对方的身份,以避免恶意第三方伪造 CSR 请求或者假冒 Istiod CA 服务器。Istio 中主要包含下面两种认证方式:

  • Istiod 身份认证

    • Istiod 采用其内置的 CA 服务器为自身签发一个服务器证书,并采用该服务器证书对外提供基于 TLS 的 gPRC 服务。

    • Istiod 调用 Kubernetes APIServer 生成一个名为 istio-ca-root-cert 的 ConfigMap 对象, 在该 ConfigMap 中放入了 Istiod 的 CA 根证书。

    • 该 ConfigMap 被 Mount 到 istio-proxy 容器中,被 pilot-agent 用于验证 Istiod 的服务器证书。

    • pilot-agent 和 Istiod 建立 gRPC 连接时,pilot-agent 采用标准的 TLS 服务器认证流程对 Istiod 的服务器证书进行认证。

  • pilot-agent 身份认证

    • 在 Kubernetes 中可以为每一个 Pod 关联一个 ServiceAccount,以表明该 Pod 中运行的服务的身份信息。

    • Kubernetes 会为该 ServiceAccount 生成一个 jwt token,并将该 token 通过 secret 加载到 pod 中的一个文件。

    • pilot-agent 在向 Istiod 发送 CSR 时,将其所在 Pod 的 service account token 也随请求发送给 Istiod。

    • Istiod 调用 Kube-apiserver 接口验证请求中附带的 service account token,以确认请求证书的服务身份是否合法。

这里需要注意的是不同版本的 Kubernetes 集群下面的 ServiceAccount Token 生成方式并不一样,但最终都是通过改 Token 来进行身份认证的。

  • 1.20(含 1.20)之前的版本,在创建 sa 时会自动创建一个 secret,然后这个会把这个 secret 通过投射卷挂载到 pod 里,该 secret 里面包含的 token 是永久有效的。
  • 1.21~1.23 版本,在创建 sa 时也会自动创建 secret,但是在 pod 里并不会使用 secret 里的 token,而是由 kubelet 到 TokenRequest API 去申请一个 token,该 token 默认有效期为一年,但是 pod 每一个小时会更新一次 token。
  • 1.24 版本及以上,在创建 sa 时不再自动创建 secret 了,只保留由 kubelet 到 TokenRequest API 去申请 token。

3、认证

Istio 提供两种类型的认证用于管控网格服务间的双向 TLS 和终端用户的身份认证。:

  • 对等认证:用于服务到服务的认证,以验证建立连接的客户端。Istio 提供双向 TLS 作为传输认证的全栈解决方案,无需更改服务代码就可以启用它。这个解决方案:

    • 为每个服务提供代表其角色的强大身份,以实现跨集群和云的互操作性。

    • 确保服务间通信的安全。

    • 提供密钥管理系统,以自动进行密钥和证书的生成、分发和轮换。

  • 请求认证:用于终端用户认证,以验证附加到请求的凭据。Istio 使用 JSON Web Token(JWT)验证启用请求级认证,并使用自定义认证实现或任何 OpenID Connect 的认证实现来简化的开发人员体验。

1.对等认证

a.默认的宽容模式

🚩 实战:对等认证(默认的宽容模式)-2023.11.24(测速成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

  • 下面我们来创建几个示例服务来对认证配置进行测试。

这里我们将在 foobar 命名空间下各自创建带有 Envoy 代理(Sidecar)的 httpbin 和 sleep 服务。还将在 legacy 命名空间下创建不带 Envoy 代理(Sidecar)的 httpbin 和 sleep 服务:

 1kubectl create ns foo
 2kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo
 3kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo
 4kubectl create ns bar
 5kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n bar
 6kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n bar
 7
 8kubectl create ns legacy
 9kubectl apply -f samples/httpbin/httpbin.yaml -n legacy
10kubectl apply -f samples/sleep/sleep.yaml -n legacy

查看:

 1[root@master1 istio-1.19.3]#kubectl get po -nfoo
 2NAME                       READY   STATUS    RESTARTS   AGE
 3httpbin-5c44d89fd9-pmzkk   2/2     Running   0          3m43s
 4sleep-844db44b8c-49br9     2/2     Running   0          3m42s
 5[root@master1 istio-1.19.3]#kubectl get po -nbar
 6NAME                       READY   STATUS    RESTARTS   AGE
 7httpbin-5c44d89fd9-zpdww   2/2     Running   0          3m43s
 8sleep-844db44b8c-q688w     2/2     Running   0          3m42s
 9[root@master1 istio-1.19.3]#kubectl get po -nlegacy
10NAME                       READY   STATUS    RESTARTS   AGE
11httpbin-86869bccff-2cmj2   1/1     Running   0          3m47s
12sleep-9454cc476-vvr4q      1/1     Running   0          3m44s

现在可以在 foobarlegacy 三个命名空间下的任意 sleep Pod 中使用 curl 向 httpbin.foohttpbin.barhttpbin.legacy 发送 HTTP 请求来验证部署结果,所有请求都应该成功并返回 HTTP 200。

  • 例如验证 sleep.barhttpbin.foo 可达性如下:
1$ kubectl exec "$(kubectl get pod -l app=sleep -n bar -o jsonpath={.items..metadata.name})" -c sleep -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
2200
  • 也可以使用一行指令检查所有可能的组合:
 1$ 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
 2sleep.foo to httpbin.foo: 200
 3sleep.foo to httpbin.bar: 200
 4sleep.foo to httpbin.legacy: 200
 5sleep.bar to httpbin.foo: 200
 6sleep.bar to httpbin.bar: 200
 7sleep.bar to httpbin.legacy: 200
 8sleep.legacy to httpbin.foo: 200
 9sleep.legacy to httpbin.bar: 200
10sleep.legacy to httpbin.legacy: 200

这样保证了我们的服务之间是可以互相访问的。

接下来我们就来看下如何对服务进行认证。

  • 默认情况下,在 Istio 网格内部的服务之间的所有流量都是通过双向 TLS 进行加密的,不需要做额外的操作,当使用双向 TLS 时,代理会将 X-Forwarded-Client-Cert 这个 Header 头注入到后端的上游请求,这个头信息的存在就是启用双向 TLS 的证据。例如:
 1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl -s http://httpbin.foo:8000/headers -s
 2{
 3  "headers": {
 4    "Accept": "*/*",
 5    "Host": "httpbin.foo:8000",
 6    "User-Agent": "curl/7.81.0-DEV",
 7    "X-B3-Parentspanid": "a59be7609a15c41f",
 8    "X-B3-Sampled": "1",
 9    "X-B3-Spanid": "034af52cfc2286b4",
10    "X-B3-Traceid": "eb07847c5c14c17fa59be7609a15c41f",
11    "X-Envoy-Attempt-Count": "1",
12    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=bb907e90c93bc3f1dd22763f952746e7d2b8c5ad7903ecbcc64324f3b5e55179;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"
13  }
14}

可以看到上面我们的请求中包含了 X-Forwarded-Client-Cert 这个 Header 头,这就是启用双向 TLS 的证据,得到的这个值是一个 spiffe:// 开头的字符串,这个字符串就是 SPIFFE ID,这个 SPIFFE ID 就是用来表示服务的身份的,后面我们会详细介绍 SPIFFE。

**零信任架构**下,需要严格区分工作负载的识别和信任,而签发 X.509 证书是推荐的一种认证方式,在 Kubernetes 集群中,服务间是通过 DNS 名称互相访问的,而网络流量可能被 DNS 欺骗、BGP/路由劫持、ARP 欺骗等手段劫持,为了将服务名称(DNS 名称)与服务身份强关联起来,Istio 使用置于 X.509 证书中的安全命名(Secure naming)机制。

SPIFFE 是 Istio 所采用的安全命名的规范,它也是云原生定义的一种标准化的、可移植的工作负载身份规范。Secure Production Identity Framework For Everyone (SPIFFE) 是一套服务之间相互进行身份识别的标准,主要包含以下内容:

  • SPIFFE ID 标准,SPIFFE ID 是服务的唯一标识,具体实现使用 URI 资源标识符。
  • SPIFFE Verifiable Identity Document (SVID) 标准,将 SPIFFE ID 编码到一个加密的可验证的数据格式中。
  • 颁发与撤销 SVID 的 API 标准。

SPIFFE ID 规定了形如 spiffe://<trust domain>/<workload identifier> 的 URI 格式,作为工作负载(Workload)的唯一标识。Istio 使用形如 spiffe://<trust_domain>/ns/<namespace>/sa/<service_account> 格式的 SPIFFE ID 作为安全命名,注入到 X.509 证书的 subjectAltName 扩展中。其中的 trust domain 参数通过 Istiod 环境变量 TRUST_DOMAIN 注入,用于在多集群环境中交互,比如我们这里就是 cluster.local,所以其实最终在 Envoy 的配置中可以看到匹配证书的 subjectAltName 值也是这个格式:

 1{
 2  "combined_validation_context": {
 3    "default_validation_context": {
 4      "match_subject_alt_names": [
 5        {
 6          "exact": "spiffe://cluster.local/ns/istio-system/sa/istiod"
 7        }
 8      ]
 9    },
10    "validation_context_sds_secret_config": {
11      "name": "ROOTCA",
12      "sds_config": {
13        "api_config_source": {
14          "api_type": "GRPC",
15          "grpc_services": [
16            {
17              "envoy_grpc": {
18                "cluster_name": "sds-grpc"
19              }
20            }
21          ],
22          "set_node_on_first_message_only": true,
23          "transport_api_version": "V3"
24        },
25        "initial_fetch_timeout": "0s",
26        "resource_api_version": "V3"
27      }
28    }
29  }
30}
  • 当服务器没有 Sidecar 时,X-Forwarded-Client-Cert 这个 Header 头将不会存在,这意味着请求是明文的,比如我们请求 httpbin.legacy 服务:
 1[root@master1 istio-1.19.3]#kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.legacy:8000/headers -s
 2{
 3  "headers": {
 4    "Accept": "*/*", 
 5    "Host": "httpbin.legacy:8000", 
 6    "User-Agent": "curl/7.81.0-DEV", 
 7    "X-B3-Sampled": "1", 
 8    "X-B3-Spanid": "1385a68dd52bc759", 
 9    "X-B3-Traceid": "94d64875182fd6101385a68dd52bc759", 
10    "X-Envoy-Attempt-Count": "1", 
11    "X-Envoy-Decorator-Operation": "httpbin.legacy.svc.cluster.local:8000/*", 
12    "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwodCgxJTlNUQU5DRV9JUFMSDRoLMTAuMjQ0LjIuMjIKGQoNSVNUSU9fVkVSU0lPThIIGgYxLjE5LjMKoQEKBkxBQkVMUxKWASqTAQoOCgNhcHASBxoFc2xlZXAKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKIAoETkFNRRIYGhZzbGVlcC04NDRkYjQ0YjhjLTQ5YnI5ChIKCU5BTUVTUEFDRRIFGgNmb28=", 
13    "X-Envoy-Peer-Metadata-Id": "sidecar~10.244.2.22~sleep-844db44b8c-49br9.foo~foo.svc.cluster.local"
14  }
15}
16
17[root@master1 istio-1.19.3]#kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.legacy:8000/headers -s | grep X-Forwarded-Client-Cert

b.全局严格 mTLS 模式

🚩 实战:对等认证(全局严格 mTLS 模式)-2023.11.24(测速成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

事实上当 Istio 自动将代理和工作负载之间的所有流量升级到双向 TLS 时,工作负载仍然可以接收明文流量,如果想要禁用非 mTLS 的通信流量,我们可以使用一个 PeerAuthentication 资源对象来进行配置,只需要将整个网格的对等认证策略设置为 STRICT 模式,作用域为整个网格范围的对等认证策略不设置 selector 即可,这种认证策略必须应用于根命名空间(istiod 所在的命名空间),例如:

  • 创建下资源
1#peer-strict-global.yaml
2apiVersion: security.istio.io/v1beta1
3kind: PeerAuthentication # 对等认证策略
4metadata:
5  name: default
6  namespace: istio-system
7spec:
8  mtls:
9    mode: STRICT # STRICT 模式表示只允许 mTLS

对等认证策略指定 Istio 对目标工作负载实施的双向 TLS 模式。支持以下模式:

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

这个对等认证策略将工作负载配置为仅接受 mTLS 加密的请求。由于未对 selector 字段指定值,因此该策略适用于网格中的所有工作负载。

  • 直接应用上面这个对等认证策略后,我们再次发送请求来进行测试:
1[root@master1 istio-1.19.3]#kubectl apply -f peer-strict-global.yaml 
2peerauthentication.security.istio.io/default created
3[root@master1 istio-1.19.3]#kubectl get PeerAuthentication -nistio-system
4NAME      MODE     AGE
5default   STRICT   14s
 1$ 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
 2sleep.foo to httpbin.foo: 200
 3sleep.foo to httpbin.bar: 200
 4sleep.foo to httpbin.legacy: 200
 5sleep.bar to httpbin.foo: 200
 6sleep.bar to httpbin.bar: 200
 7sleep.bar to httpbin.legacy: 200
 8sleep.legacy to httpbin.foo: 000
 9command terminated with exit code 56
10sleep.legacy to httpbin.bar: 000
11command terminated with exit code 56
12sleep.legacy to httpbin.legacy: 200

我们可以发现从 sleep.legacyhttpbin.foohttpbin.bar 的请求都失败了,其他依然是成功的,这是因为我们现在配置了 STRICT 严格要求使用 mTLS,由于 sleep.legacy 没有 Envoy Sidecar,所以无法满足这一要求,所以要访问网格内部的工作负载是不被允许的。**那为什么可以访问 httpbin.legacy 呢?**这是因为我们在 legacy 命名空间下的 httpbin 服务没有 Envoy Sidecar,所以它不会被 Istio 管理,也就不会被强制要求使用 mTLS 了,所以我们可以直接访问它。

c.命名空间级别策略

🚩 实战:对等认证(命名空间级别策略)-2023.11.24(测速成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

  • 上面我们是在根命名空间(istiod 所在的命名空间)下配置的对等认证策略,这样会影响到整个网格,如果我们只想对某个命名空间下的服务进行配置,那么我们可以使用命名空间级别的对等认证策略,该策略的规范与整个网格级别的规范相同,但是可以在 metadata 字段指定具体的命名空间的名称。比如我们要在 foo 命名空间上启用严格的双向 TLS 对等策略,可以创建如下所示的资源对象:
1#peer-strict-ns.yaml 
2apiVersion: security.istio.io/v1beta1
3kind: PeerAuthentication
4metadata:
5  name: default
6  namespace: foo # 命名空间级别
7spec:
8  mtls:
9    mode: STRICT
  • 直接应用上面的资源对象,然后再次发送请求来进行测试:
 1# 首先删除上面创建的全局对等认证策略
 2[root@master1 istio-1.19.3]#kubectl delete  -f peer-strict-global.yaml 
 3
 4#部署:
 5[root@master1 istio-1.19.3]#kubectl apply -f peer-strict-ns.yaml
 6
 7$ 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 "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
 8sleep.foo to httpbin.foo: 200
 9sleep.foo to httpbin.bar: 200
10sleep.foo to httpbin.legacy: 200
11sleep.bar to httpbin.foo: 200
12sleep.bar to httpbin.bar: 200
13sleep.bar to httpbin.legacy: 200
14sleep.legacy to httpbin.foo: 000
15command terminated with exit code 56
16sleep.legacy to httpbin.bar: 200
17sleep.legacy to httpbin.legacy: 200

由于这些策略只应用于命名空间 foo 中的服务,正常我们会看到只有从没有 Sidecar 的客户端(sleep.legacy)到有 Sidecar 的客户端(httpbin.foo)的请求会失败,其余都是成功的。

测试结束。😘

d.为每个工作负载启用双向 TLS

🚩 实战:对等认证(为每个工作负载启用双向 TLS)-2023.11.24(测速成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

  • 要为特定工作负载设置对等认证策略,我们就必须配置 selector 字段指定与所需工作负载匹配的标签。比如我们只想要为 httpbin.bar 服务启用严格模式的 mTLS,则可以创建如下所示的资源对象:
 1#peer-strict-pod.yaml 
 2apiVersion: security.istio.io/v1beta1
 3kind: PeerAuthentication
 4metadata:
 5  name: "httpbin"
 6  namespace: "bar"
 7spec:
 8  selector:
 9    matchLabels:
10      app: httpbin # 匹配 httpbin 应用的标签
11  mtls:
12    mode: STRICT
  • 上面的资源对象将为 bar 命名空间中的 httpbin 应用启用严格模式的 mTLS,其他工作负载不受影响。直接应用上面的资源对象,然后再次发送请求来进行测试:
 1#部署
 2[root@master1 istio-1.19.3]#kubectl apply -f peer-strict-pod.yaml
 3
 4$ 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 "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
 5sleep.foo to httpbin.foo: 200
 6sleep.foo to httpbin.bar: 200
 7sleep.foo to httpbin.legacy: 200
 8sleep.bar to httpbin.foo: 200
 9sleep.bar to httpbin.bar: 200
10sleep.bar to httpbin.legacy: 200
11sleep.legacy to httpbin.foo: 000
12command terminated with exit code 56
13sleep.legacy to httpbin.bar: 000
14command terminated with exit code 56
15sleep.legacy to httpbin.legacy: 200

跟预期一样,从 sleep.legacyhttpbin.bar 的请求因为同样的原因失败。

测试结束。😘


为每个端口配置不同的对等认证策略

🚩 实战:对等认证(为每个端口配置不同的对等认证策略)-2023.11.24(测速成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

  • 除此之外我们还可以为每个端口配置不同的对等认证策略,例如,以下对等认证策略要求在除 80 端口以外的所有端口上都使用双向 TLS:
 1#peer-strict-pod-port.yaml
 2apiVersion: security.istio.io/v1beta1
 3kind: PeerAuthentication
 4metadata:
 5  name: "httpbin"
 6  namespace: "bar"
 7spec:
 8  selector:
 9    matchLabels:
10      app: httpbin
11  mtls:
12    mode: STRICT
13  portLevelMtls:
14    80:
15      mode: DISABLE

上面的资源对象中我们配置了一个 portLevelMtls 字段,该字段用于配置端口级别的对等认证策略,这里我们配置了 80 端口的对等认证策略为 DISABLE,即禁用双向 TLS,其他端口的对等认证策略为 STRICT,即启用双向 TLS,也就是说我们只允许 httpbin 应用的 80 端口接收明文流量,其他端口都必须使用双向 TLS。

  • 直接应用上面的资源对象,然后再次发送请求来进行测试:
 1#[root@master1 istio-1.19.3]#kubectl apply -f peer-strict-pod-port.yaml
 2
 3$ 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 "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
 4sleep.foo to httpbin.foo: 200
 5sleep.foo to httpbin.bar: 200
 6sleep.foo to httpbin.legacy: 200
 7sleep.bar to httpbin.foo: 200
 8sleep.bar to httpbin.bar: 200
 9sleep.bar to httpbin.legacy: 200
10sleep.legacy to httpbin.foo: 000
11command terminated with exit code 56
12sleep.legacy to httpbin.bar: 200
13sleep.legacy to httpbin.legacy: 200

可以看到现在我们从 sleep.legacyhttpbin.bar 的请求成功了,因为我们禁用了 80 端口的双向 TLS,所以 sleep.legacy 可以访问到 httpbin.bar 的服务了。

  • 测试完成后记得删除上面创建的对等认证策略:
1kubectl delete peerauthentication default -n foo
2kubectl delete peerauthentication httpbin -n bar

测试结束。😘

2.请求认证

Istio 的请求认证用于终端用户认证,以验证附加到请求的凭据。Istio 使用 JWT 验证启用请求级认证,并使用自定义认证实现或任何 OpenID Connect 的认证实现来进行认证简化。

要在 Istio 中进行请求认证,可以通过一个 RequestAuthentication 资源对象来进行配置,如果请求包含无效的认证信息,它将根据配置的认证规则拒绝该请求。不包含任何认证凭证的请求将被接受,但不会有任何认证的身份。

🚩 实战:请求认证-2023.11.25(测试成功)

a.JWK 与 JWKS 概述

Istio 使用 JWT 对终端用户进行身份验证,Istio 要求提供 JWKS 格式的信息,用于 JWT 签名验证。因此这里得先介绍一下 JWKJWKS

JWKJSON Web Key,是 JWT 的秘钥,它描述了一个加密密钥(公钥或私钥)的值及其各项属性。JWKS 描述一组 JWK 密钥,JWKS 的 JSON 文件格式如下:

1{
2"keys": [
3  <jwk-1>,
4  <jwk-2>,
5  ...
6]}

Istio 使用 JWK 描述验证 JWT 签名所需要的信息。在使用 RSA 签名算法时,JWK 描述的应该是用于验证的 RSA 公钥。一个 RSA 公钥的 JWK 描述如下:

1{
2    "alg": "RS256",  # 算法「可选参数」
3    "kty": "RSA",    # 密钥类型
4    "use": "sig",    # 被用于签名「可选参数」
5    "kid": "DHFxxxxx_-envvQ",  # key 的唯一 id
6    "n": "xAExxxxMQ", 公钥的指数(exponent)
7    "e": "AQAB"  # 公钥的模数(modulus)
8}

那么需要如何生成 JWK 呢?我们可以使用 https://github.com/lestrrat-go/jwx 这个命令行工具,这是一个用 Go 语言开发的命令行工具,内置了对各种 JWx(JWT, JWK, JWA, JWS, JWE) 的支持。

  • 我们可以使用下面的命令来安装 jwx 命令行工具:
 1$ export GOPROXY="https://goproxy.io"   #"https://goproxy.io" 或者 "https://goproxy.cn"都行的。
 2$ git clone https://github.com/lestrrat-go/jwx.git
 3$ cd jwx
 4$ make jwx
 5go: downloading github.com/lestrrat-go/jwx/v2 v2.0.11
 6go: downloading github.com/urfave/cli/v2 v2.24.4
 7# ......
 8go: downloading github.com/russross/blackfriday/v2 v2.1.0
 9go: downloading golang.org/x/sys v0.8.0
10Installed jwx in /root/go/bin/jwx

注意:这里是要具有Go环境的:

image-20231124074547767

image-20231124074724282

 1[root@master1 ~]#cp /opt/goDir/bin/jwx /usr/local/bin/
 2[root@master1 ~]#cd /tmp/
 3[root@master1 tmp]#jwx 
 4NAME:
 5   jwx - Tools for various JWE/JWK/JWS/JWT operations
 6
 7USAGE:
 8   jwx [global options] command [command options] [arguments...]
 9
10COMMANDS:
11   jwa      List available algorithms and types
12   jwe      Work with JWE messages
13   jwk      Work with JWK and JWK sets
14   jws      Work with JWS messages
15   help, h  Shows a list of commands or help for one command
16
17GLOBAL OPTIONS:
18   --help, -h  show help
19[root@master1 tmp]#

报错

1/opt/goDir/pkg/mod/github.com/lestrrat-go/jwx/v2@v2.0.11/cert/cert.go:9:2: //go:build comment without // +build comment
2/opt/goDir/pkg/mod/github.com/lestrrat-go/jwx/v2@v2.0.11/x25519/x25519.go:10:2: //go:build comment without // +build comment
3make: *** [jwx] Error 1

image-20231125065257703

/opt/goDir/pkg/mod/github.com/lestrrat-go/jwx/v2@v2.0.11/cert/cert.go:9:2: //go:build comment without // +build comment /opt/goDir/pkg/mod/github.com/lestrrat-go/jwx/v2@v2.0.11/x25519/x25519.go:10:2: //go:build comment without // +build comment make: *** [jwx] Error 1

这个错误看起来是与 Go 的构建标记(build tags)相关的问题。错误信息指出在两个文件(cert.gox25519.go)的第 9 行和第 10 行有 //go:build 注释,但是缺少了 // +build 注释。

// +build 注释是用于控制构建的,指示在何种条件下构建或不构建文件。如果这些文件是条件性地构建的,你需要确保这些注释的格式是正确的。

在这两个文件中,你可能需要添加 // +build 注释,例如:

1go// +build some_tag

或者,如果这些文件是通用的,不受构建标记的影响,你可以直接删除这些 //go:build 注释。

请注意,如果你在使用某些构建工具,比如 make,确保你的构建脚本和构建过程正确处理了这些文件,并且使用了正确的构建标记。

🍀

排查过程:

image-20231125065644901

image-20231125065554371

image-20231125065718822

🍀

感觉是自己的go版本问题……

重新换装了go版本后,果然好了;

1[root@master1 ~]#go version
2go version go1.17.13 linux/amd64
3
4
5#原来自己go版本是:go1.16.2

image-20231125070247577

  • 下面我们使用 jwx 命令行工具生成一个 JWK,通过模板指定 kidyoudianzhishi-key:
 1$ jwx jwk generate --keysize 4096 --type RSA  --template '{"kid":"youdianzhishi-key"}' -o rsa.jwk
 2$ cat rsa.jwk
 3{
 4  "d": "AxxxwBw6Jok",
 5  "dp": "j3xxxuvQ",
 6  "dq": "zzxxxqQ",
 7  "e": "AQAB",
 8  "kid": "youdianzhishi-key",
 9  "kty": "RSA",
10  "n": "5sxxxwV8",
11  "p": "-yxxxQ",
12  "q": "6zkC_xxxxKw",
13  "qi": "LExxxTw"
14}
  • 然后从 rsa.jwk 中提取 JWK 公钥:
1$ jwx jwk fmt --public-key -o rsa-public.jwk rsa.jwk
2$ cat rsa-public.jwk
3{
4  "e": "AQAB",
5  "kid": "youdianzhishi-key",
6  "kty": "RSA",
7  "n": "5sxxxV8"
8}

上面生成的 JWK 其实就是 RSA 公钥私钥换了一种存储格式而已。

  • 我们可以使用下面的命令将它们转换成 PEM 格式的公钥和私钥:
 1$ jwx jwk fmt -I json -O pem rsa.jwk
 2-----BEGIN PRIVATE KEY-----
 3MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCym3O0Ik5QGZ8i
 4......
 5-----END PRIVATE KEY-----
 6
 7$ jwx jwk fmt -I json -O pem rsa-public.jwk
 8-----BEGIN PUBLIC KEY-----
 9MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsptztCJOUBmfIqSE8LR5
10......
11-----END PUBLIC KEY-----
  • 接下来我们就可以使用 jwx 命令行签发 JWT Token 并验证其有效性了:
 1jwx jws sign --key rsa.jwk --alg RS256 --header '{"typ":"JWT"}' -o token.txt - <<EOF
 2{
 3  "iss": "testing@secure.istio.io",
 4  "sub": "cnych001",
 5  "iat": 1700648397,
 6  "exp": 1700686042,
 7  "name": "Yang Ming"
 8}
 9EOF
10
11
12jwx jws sign --key rsa.jwk --alg RS256 --header '{"typ":"JWT"}' -o token.txt - <<EOF
13{
14  "iss": "testing@secure.istio.io",
15  "sub": "cnych001",
16  "iat": 1700858397,
17  "exp": 1700886042,
18  "name": "Yang Ming"
19}
20EOF

image-20231125071635661

1[root@master1 istio]#date +%s
21700867814
  • 然后查看生成的 Token 文件内容:
1$ cat token.txt
2eyJhbGciOiJSUzI1NiIsImtpZCI6InlvdWRpYW56aGlzaGkta2V5In0......
  • 上面生成 JWT Token 实际上是由下面的算法生成的:
1base64url_encode(Header) + '.' + base64url_encode(Claims) + '.' + base64url_encode(Signature)
  • 我们可以将该 Token 粘贴到 jwt.io 网站上来解析:

img

先看一下 Headers 部分,包含了一些元数据:

  • alg: 所使用的签名算法,这里是 RSA256
  • kid: JWKkid

然后是 Payload(Claims) 部分,payload 包含了这个 token 的数据信息,JWT 标准规定了一些字段,另外还可以加入一些承载额外信息的字段。

  • iss: issuer,token 是谁签发的
  • sub: token 的主体信息,一般设置为 token 代表用户身份的唯一 id 或唯一用户名
  • exp: token 过期时间,Unix 时间戳格式
  • iat: token 创建时间, Unix 时间戳格式

最后看一下签名 Signature 信息,签名是基于 JSON Web Signature (JWS) 标准来生成的,签名主要用于验证 token 是否有效,是否被篡改。签名支持很多种算法,这里使用的是 RSASHA256,具体的签名算法如下:

1RSASHA256(
2  base64UrlEncode(header) + "." +
3  base64UrlEncode(payload),
4  <rsa-public-key>,
5  <rsa-private-key>
  • 最后可以使用 RSA Public Key 验证 JWT Token 的有效性:
1$ jwx jws verify --alg RS256 --key rsa-public.jwk token.txt
2{
3  "iss": "testing@secure.istio.io",
4  "sub": "cnych001",
5  "iat": 1700648397,
6  "exp": 1700656042,
7  "name": "Yang Ming"
8}

b.配置 JWT 终端用户认证

上面我们了解了 JWT、JWK、JWKS 这些概念,接下来我们来配置 Istio 的认证策略使用我们自己创建的 JWKS

  • 为了方便访问,我们这里通过 Ingress 网关来暴露 httpbin.foo 服务,为其创建一个 Gateway 对象:
1[root@master1 ~]#cd istio-1.19.3/
2[root@master1 istio-1.19.3]#kubectl apply -f samples/httpbin/httpbin-gateway.yaml -n foo
  • 可以通过如下命令获取 HTTP 的访问端口:
1export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
  • 然后获取集群中任意一个节点的 IP 地址:
1export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
  • 然后可以通过下面的命令来测试 httpbin.foo 服务是否可以正常访问:
1[root@master1 istio-1.19.3]#curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
2200

如果上面命令返回 200,则表示 httpbin.foo 服务可以正常访问。

  • 接下来我们就可以添加一个请求认证策略对象,该策略要求 Ingress 网关指定终端用户的 JWT。
 1#jwt-examplt.yaml
 2apiVersion: "security.istio.io/v1"
 3kind: RequestAuthentication
 4metadata:
 5  name: jwt-example
 6  namespace: istio-system
 7spec:
 8  selector:
 9    matchLabels:
10      istio: ingressgateway #匹配我们的入口网关
11  jwtRules:
12    - issuer: "testing@secure.istio.io" # 签发者,需要和 JWT payload 中的 iss 属性完全一致。
13      # forwardOriginalToken: true
14      jwks: |
15        {
16            "keys": [
17              {
18                "e": "AQAB",
19                "kid": "youdianzhishi-key",
20                "kty": "RSA",
21                "n": "5sbkUCkQDuM3hiw9UTxmxO2wUYOT69IZje7M6O_R-ApJ3KkrhQS1C2SJelBTLgbaWhAsO4jBYOmFfWGLBA-XxrhoB9KxWCGA4EXf6fukp0ljGTYE6Th6r393jIJGDFUt8vQCjj5ivmBAQHLjwmnWiG6I93mrTQhoNHQWAde21O7yYNpg6fvjVJgRFqeAtpieA-5f2sQ8fBkefM0RFgQTqWPGfLHse5nqRWY4hG_gb23GzCo_Ti2h9wJZNuTfdK8hitahOq3eLlDVVvCu8hx-8BEs5APCj54gtqePswHeRXZi_03ccNii5CnW7Y1rHiL8LHKNHhY5tD2iZByh4YrnhkUWD-CXNqyUKx90de0R9H1ON6pqsmdEx4iAMx2xvnZ0S9NbKlk3glw_AvudjJUHa41xx7qy9OZ7QV6cB_ntwLtw513lk5Tfm-RDlVgyU-EO2jKXbOeiDpnb8kgRNBMKDqY9mgLfISW54N-LBjyVwVVHOvICWo0oPJypRgPRWD8f25wHqzjlsB8nIqJkj_e9p2c5WnAGiZWuZjm6t94IfFq9lWYUsSn-JtJvh3ATsv7ptDKFz2Ko82r1uD3446mr4I0J56C-7WOHGchlSOrDWKErwkIGxyrQ_3GEkUhkSxfArAv0bajmcMCu1_j8Eekqk7Fnm5QqytCFmFevzIJkwV8"
22              }
23            ]
24        }

在上面的资源对象中我们通过 selector 匹配了 istio-ingressgateway 服务,因为我们要为 Ingress 网关添加请求认证,具体的请求认证规则通过 jwtRules 来进行配置,这里我们配置了一个 issuer 字段,该字段用于指定 JWT 的 Issuer 发行人,然后配置了一个 jwks 字段,该字段用于指定 JWT 的公钥集数据,我们也可以通过 jwksUri 来指向一个公钥集地址。对同一个 issuers(jwt 签发者),可以设置多个公钥,以实现 JWT 签名密钥的轮转。JWT 的验证规则是:

  • JWT 的 payload 中有 issuer 属性,首先通过 issuer 匹配到对应的 istio 中配置的 jwks
  • JWT 的 header 中有 kid 属性,第二步在 jwks 的公钥列表中,中找到 kid 相同的公钥。
  • 使用找到的公钥进行 JWT 签名验证。

配置中的 spec.selector 可以省略,这样会直接在整个命名空间中生效,比如在 istio-system 命名空间,该配置将在全集群的所有 sidecar/ingressgateway 上生效!

默认情况下,Istio 在完成了身份验证之后,会去掉 Authorization 请求头再进行转发。这将导致我们的后端服务获取不到对应的 Payload,无法判断终端用户的身份。因此我们需要启用 Istio 的 Authorization 请求头的转发功能,只需要在上面的资源对象中添加一个 forwardOriginalToken: true 字段即可。

  • 直接应用上面的资源对象,然后再次发送请求来进行测试:
1#kubectl apply -f jwt-example.yaml
2
3[root@master1 istio]#curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
4200

可以看到现在依然可以正常访问,但是如果我们请求的时候带上一个无效的 JWT Token,则会返回 401 错误:

1[root@master1 istio]#curl --header "Authorization: Bearer abcdef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
2401
  • 要想正常访问,我们需要使用上面生成的 JWT Token 来进行访问:
1[root@master1 istio]#TOKEN=$(cat token.txt)
2[root@master1 istio]#curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
3200

可以看到就可以正常访问了。

c.设置强制认证规则

从上面的测试可以看出 Istio 的 JWT 验证规则,默认情况下会直接忽略不带 Authorization 请求头(即 JWT)的流量,因此这类流量能直接进入网格内部。通常这是没问题的,因为没有 Authorization 的流量即使进入到内部,也会因为无法通过 payload 判别身份而被拒绝操作。但是如果我们需要禁止不带 JWT 的流量,那么可以通过一个 AuthorizationPolicy 对象来进行配置了。

  • 比如拒绝任何 JWT 无效的请求,则可以创建如下的资源对象:
 1#ap.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: deny-requests-without-authorization
 6  namespace: istio-system
 7spec:
 8  selector:
 9    matchLabels:
10      istio: ingressgateway
11  action: DENY # 拒绝
12  rules:
13    - from:
14        - source:
15            notRequestPrincipals: ["*"] # 不存在任何请求身份(Principal)的 requests

上面的资源对象中我们配置的 action: DENY 表示拒绝,然后通过 rules 字段来配置拒绝的规则,这里我们配置了一个 from 字段,该字段用于指定请求的来源,这里我们配置了一个 notRequestPrincipals 字段,该字段用于指定请求的身份,这里我们配置为 *,表示任何请求身份都不允许。

  • 应用上面的资源对象后,重新发送没有令牌的请求,请求失败并返回错误码 403:
1#kubectl apply -f ap.yaml
2
3[root@master1 istio]#curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
4403
  • 如果仅希望强制要求对部分 path 的请求必须带有 Authorization Header,可以这样设置:
 1apiVersion: security.istio.io/v1beta1
 2kind: AuthorizationPolicy
 3metadata:
 4  name: deny-requests-without-authorization
 5  namespace: istio-system
 6spec:
 7  selector:
 8    matchLabels:
 9      istio: ingressgateway
10  action: DENY # 拒绝
11  rules:
12    - from:
13        - source:
14            notRequestPrincipals: ["*"] # 不存在任何请求身份的 requests
15      # 仅强制要求如下 host/path 相关的请求,必须带上 JWT token
16      to:
17        - operation:
18            hosts: ["another-host.com"]
19            paths: ["/headers"]

需要注意的是 RequestsAuthenticationAuthorizationPolicy 这两个对象返回的错误码是不同的:

  • RequestsAuthentication 验证失败的请求,Istio 会返回 401 状态码。
  • AuthorizationPolicy 验证失败的请求,Istio 会返回 403 状态码。

最后,记得删除刚创建的资源

1[root@master1 ~]#cd istio
2[root@master1 istio]#ls
3ap.yaml  jwt-example.yaml  rsa.jwk  rsa-public.jwk  token.txt
4[root@master1 istio]#kubectl delete -f ./

测试结束。😘

到这里我们就实现了对 JWT 的验证,当然除了验证之外,我们还需要授权,这个我们在下面的章节中来实现。

4、授权

img

前言

Istio 的授权功能为网格中的工作负载提供网格、命名空间和工作负载级别的访问控制。授权策略是对服务器端 Envoy 代理的入站流量实施访问控制。每个 Envoy 代理都运行一个授权引擎,该引擎在运行时授权请求。当请求到达代理时,授权引擎根据当前授权策略评估请求上下文,并返回授权结果 ALLOWDENY

要对工作负载实施访问控制,需要应用授权策略。对于没有应用授权策略的工作负载,Istio 允许所有请求。

授权策略支持 ALLOWDENYCUSTOM 操作。我们可以根据需要应用多个策略,每个策略具有不同的操作, 以确保对工作负载的访问安全。

Istio 按以下顺序检查层中的匹配策略:CUSTOM -> DENY -> ALLOW。对于每种类型的操作,Istio 首先检查是否有策略的操作已被应用,然后检查请求是否匹配策略的规则。如果请求与其中一层中的策略不匹配, 则检查将继续到下一层。

img

当将多个授权策略应用于同一工作负载时,Istio 会累加地应用它们。

要配置授权策略,只需要创建一个 AuthorizationPolicy 自定义资源,其中包括:选择器(selector)、动作(action)和一个规则(rules)列表:

  • selector 字段指定策略的目标

  • action 字段指定允许还是拒绝请求

  • rules 指定何时触发动作

  • rules 下的 from 字段指定请求的来源

  • rules 下的 to 字段指定请求的操作

  • rules 下的 when 字段指定应用规则所需的条件

比如有一个如下所示的授权策略对象:

 1apiVersion: security.istio.io/v1
 2kind: AuthorizationPolicy
 3metadata:
 4  name: httpbin
 5  namespace: foo
 6spec:
 7  selector:
 8    matchLabels:
 9      app: httpbin
10      version: v1
11  action: ALLOW
12  rules:
13    - from:
14        - source:
15            principals: ["cluster.local/ns/default/sa/sleep"]
16        - source:
17            namespaces: ["dev"]
18      to:
19        - operation:
20            methods: ["GET"]
21      when:
22        - key: request.auth.claims[iss]
23          values: ["https://accounts.google.com"]
24          # notValues: ["https://accounts.google.com"]

上面这个授权策略表示允许两个源(服务帐户 cluster.local/ns/default/sa/sleep 和命名空间 dev),在使用有效的 JWT 令牌发送请求时,可以访问命名空间 foo 中带有标签 app: httpbinversion: v1 的工作负载。

在上面的资源对象中配置了一个 when 字段,该字段是可选的,当配置后,指定了请求的附加条件列表,如果未设置,则允许任何条件。其中指定的 key 表示 Istio 属性的名称,目前支持的属性名称如下表所示:

名称描述支持的协议示例
request.headersHTTP 请求头。头部名称用 [] 包围,不使用任何引号仅 HTTPkey: request.headers[User-Agent]``values: ["Mozilla/*"]
source.ip来源工作负载实例的 IP 地址,支持单个 IP 或 CIDRHTTP 和 TCPkey: source.ip``values: ["10.1.2.3", "10.2.0.0/16"]
remote.ip根据 X-Forwarded-For 头或代理协议确定的原始客户端 IP 地址,支持单个 IP 或 CIDRHTTP 和 TCPkey: remote.ip``values: ["10.1.2.3", "10.2.0.0/16"]
source.namespace来源工作负载实例的命名空间,需要启用相互 TLSHTTP 和 TCPkey: source.namespace``values: ["default"]
source.principal来源工作负载的身份,需要启用相互 TLSHTTP 和 TCPkey: source.principal``values: ["cluster.local/ns/default/sa/productpage"]
request.auth.principal经过认证的 JWT 令牌的主体,根据 JWT 声明以 /格式构建,需要应用请求认证策略仅 HTTPkey: request.auth.principal``values: ["issuer.example.com/subject-admin"]
request.auth.audiences经过认证的 JWT 令牌的预期受众,根据 JWT 声明 `` 构建,需要应用请求认证策略仅 HTTPkey: request.auth.audiences``values: ["example.com"]
request.auth.presenter经过认证的 JWT 令牌的授权展示者,根据 JWT 声明 `` 构建,需要应用请求认证策略仅 HTTPkey: request.auth.presenter``values: ["123456789012.example.com"]
request.auth.claims经过认证的 JWT 令牌的原始声明。声明名称用 []包围,不使用任何引号,也可以使用嵌套声明,需要应用请求认证策略。注意仅支持字符串类型或字符串列表类型的声明仅 HTTPkey: request.auth.claims[iss]``values: ["*@foo.com"]key: request.auth.claims[nested1][nested2]``values: ["some-value"]
destination.ip目的地工作负载实例的 IP 地址,支持单个 IP 或 CIDRHTTP 和 TCPkey: destination.ip``values: ["10.1.2.3", "10.2.0.0/16"]
destination.port目的地工作负载实例端口,必须在 [0, 65535] 范围内。注意这不是服务端口HTTP 和 TCPkey: destination.port``values: ["80", "443"]
connection.sni服务器名称指示,需要启用 TLSHTTP 和 TCPkey: connection.sni``values: ["www.example.com"]
experimental.envoy.filters.*过滤器的实验性元数据匹配,[] 中包裹的值作为列表进行匹配HTTP 和 TCPkey: experimental.envoy.filters.network.mysql_proxy[db.table]``values: ["[update]"]

我们这里配置的 key: request.auth.claims[iss] 表示请求的 JWT 中的 iss 字段,然后通过 values 字段指定了 iss 字段的值,这里我们指定了 https://accounts.google.com,表示只有当 iss 字段的值为 https://accounts.google.com 时才允许访问。

除了可以配置 ALLOW 之外也可以配置 DENY,比如下面的配置:

 1apiVersion: security.istio.io/v1
 2kind: AuthorizationPolicy
 3metadata:
 4  name: httpbin-deny
 5  namespace: foo
 6spec:
 7  selector:
 8    matchLabels:
 9      app: httpbin
10      version: v1
11  action: DENY
12  rules:
13    - from:
14        - source:
15            notNamespaces: ["foo"]

这个授权策略表示如果请求来源不是命名空间 foo,请求将被拒绝。

⚠️ 注意 ⚠️:拒绝策略优先于允许策略,如果请求同时匹配上允许策略和拒绝策略,请求将被拒绝。Istio 首先评估拒绝策略,以确保允许策略不能绕过拒绝策略。

除了 values 之外也可以使用 notValues 字段,比如下面的授权策略对象:

 1apiVersion: security.istio.io/v1
 2kind: AuthorizationPolicy
 3metadata:
 4  name: deny-method-get
 5  namespace: foo
 6spec:
 7  selector:
 8    matchLabels:
 9      app: httpbin
10  action: DENY
11  rules:
12    - to:
13        - operation:
14            methods: ["GET"]
15            # paths: ["/ip"] # 也可以指定 path
16      when:
17        - key: request.headers[x-token]
18          notValues: ["admin"]

该对象表示只有当 HTTP 头中 x-token 值不是 admin 时才会拒绝 GET 请求。

image-20231128073540116

1.HTTP 请求授权

🚩 实战:HTTP 请求授权-20223.11.27(测试成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

  • 了解了授权策略的基本概念后,接下来我们通过一些示例来了解下具体的使用。首先来看一下如何为 HTTP 流量授权。
 1[root@master1 ~]#kubectl get po 
 2NAME                             READY   STATUS    RESTARTS   AGE
 3details-v1-5f4d584748-6fzzm      2/2     Running   0          14d
 4fortio-deploy-5cd456bbdb-dfdn7   2/2     Running   0          14d
 5productpage-v1-564d4686f-krxns   2/2     Running   0          14d
 6ratings-v1-686ccfb5d8-zmvtr      2/2     Running   0          14d
 7reviews-v1-86896b7648-vclhs      2/2     Running   0          14d
 8reviews-v2-b7dcd98fb-nzljg       2/2     Running   0          14d
 9reviews-v3-5c5cc7b6d-dpnlg       2/2     Running   0          14d
10[root@master1 ~]#kubectl get gateway
11NAME                  AGE
12bookinfo-gateway      14d
13
14
15[root@master1 istio-1.19.3]#kubectl get vs bookinfo -oyaml
16……
17spec:
18  gateways:
19  - bookinfo-gateway
20  hosts:
21  - '*'
22  http:
23  - match:
24    - uri:
25        exact: /productpage
26    - uri:
27        prefix: /static
28    - uri:
29        exact: /login
30    - uri:
31        exact: /logout
32    - uri:
33        prefix: /api/v1/products
34    route:
35    - destination:
36        host: productpage
37        port:
38          number: 9080
39[root@master1 istio-1.19.3]#
  • 这里还是以前面的 Bookinfo 应用为例进行说明:
1export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
2export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
3
4echo $INGRESS_PORT
5echo $SECURE_INGRESS_PORT
  • 然后获取集群中任意一个节点的 IP 地址:
1export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
2export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
3
4echo $INGRESS_HOST
5echo $GATEWAY_URL
  • 部署 Bookinfo 应用后通过 http://$GATEWAY_URL/productpage 访问 product 页面,可以看到如下内容:

image-20231127070719520

在左下方有数据信息,包括:图书类型,页数,出版社等。

在页面右下方是数据评论数据。

当刷新页面时,应用会在 product 页面中以轮询的方式显示不同版本的评论:如红色星标,黑色星标,或者没有星标。

在 Istio 中我们可以很轻松地为网格中的工作负载配置访问控制。

我们这里首先配置一个简单的 allow-nothing 的策略,来拒绝工作负载的所有请求,然后我们再逐渐添加更多的访问权限。

  • 首先在 default 命名空间中创建一个如下所示的授权策略对象:
1#deny-all.yaml
2apiVersion: security.istio.io/v1
3kind: AuthorizationPolicy
4metadata:
5  name: allow-nothing
6  namespace: default
7spec: {}

可以看到上面的这个 AuthorizationPolicy 策略没有配置任何规则,spec: 字段为空值 {},意思是不允许任何流量,拒绝所有请求,它会把该策略应用于 default 命名空间中的每个工作负载。

  • 直接应用该策略,然后打开浏览器访问 Bookinfo 的 productpage 页面,我们将会看到 RBAC: access denied 这样的信息,该错误表明配置的 deny-all 策略生效了,现在在网格内没有任何规则允许对 default 命名空间中的工作负载进行任何访问。
1[root@master1 istio]#kubectl apply -f deny-all.yaml 
2authorizationpolicy.security.istio.io/allow-nothing created

image-20231127071742380

  • 接下来我们配置一个授权策略,允许通过 GET 方法访问 productpage 工作负载,如下所示:
 1#productpage-viewer.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: productpage-viewer
 6  namespace: default
 7spec:
 8  selector:
 9    matchLabels:
10      app: productpage
11  action: ALLOW
12  rules:
13    - to:
14        - operation:
15            methods: ["GET"]

可以看到上面的这个授权策略对象中我们并没有在 rules 字段中配置 from 字段,这意味着所有的请求源都被允许访问。

  • 同样直接应用该资源对象后,在浏览器里访问 Bookinfo 的 productpage 页面,现在我们可以访问到 Bookinfo Sample 页面了,但会发现页面中有如下的错误:
1[root@master1 istio]#vim productpage-viewer.yaml
2[root@master1 istio]#kubectl apply -f productpage-viewer.yaml 
3authorizationpolicy.security.istio.io/productpage-viewer created
4[root@master1 istio]#kubectl get AuthorizationPolicy
5NAME                 AGE
6allow-nothing        5m55s
7productpage-viewer   9s

image-20231127072346610

这些错误是符合预期的,因为我们没有授权 productpage 工作负载去访问 details 和 reviews 工作负载,所以当 productpage 工作负载去访问 details 和 reviews 工作负载时,就会出现错误。

  • 接下来,我们再配置一个授权策略来容许访问其他工作负载,比如下面的策略对象:
 1#details-viewer.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: details-viewer
 6  namespace: default
 7spec:
 8  selector:
 9    matchLabels:
10      app: details
11  action: ALLOW
12  rules:
13    - from:
14        - source:
15            principals: ["cluster.local/ns/default/sa/bookinfo-productpage"]
16      to:
17        - operation:
18            methods: ["GET"]

该策略对象表示允许请求身份为 cluster.local/ns/default/sa/bookinfo-productpage 的工作负载以 GET 方式访问 details 工作负载,这个身份对应的是 productpage 工作负载的 ServiceAccount,所以应用该资源对象后 productpage 工作负载就可以访问 details 工作负载了。

1[root@master1 istio]#kubectl get po productpage-v1-564d4686f-krxns -oyaml|grep serviceAccountName
2          fieldPath: spec.serviceAccountName
3  serviceAccountName: bookinfo-productpage
4[root@master1 istio]#
  • 用同样的方式为 reviews 工作负载也配置一个类似的授权策略,如下所示:
 1#reviews-viewer
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: reviews-viewer
 6  namespace: default
 7spec:
 8  selector:
 9    matchLabels:
10      app: reviews
11  action: ALLOW
12  rules:
13    - from:
14        - source:
15            principals: ["cluster.local/ns/default/sa/bookinfo-productpage"]
16      to:
17        - operation:
18            methods: ["GET"]
  • 应用上面的这两个资源对象后 productpage 服务就可以访问 detailsreviews 服务了。现在在浏览器访问 Bookinfo productpage 页面我们就可以看到 Bookinfo Sample 页面、Book Details 在左下方、Book Reviews 在右下方。
1kubectl apply -f details-viewer.yaml
2kubectl appply -f reviews-viewer.yaml
  • 但是在 Book Reviews 部分偶尔会有 Ratings service currently unavailable 的错误。

img

这是因为 reviews 工作负载没有权限访问 ratings 工作负载,要修复这个问题我们只需要授权 reviews 工作负载可以访问 ratings 工作负载即可。

  • 创建一个如下所示的 AuthorizationPolicy 授权策略对象即可:
 1#ratings-viewer.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: "ratings-viewer"
 6  namespace: default
 7spec:
 8  selector:
 9    matchLabels:
10      app: ratings
11  action: ALLOW
12  rules:
13    - from:
14        - source:
15            principals: ["cluster.local/ns/default/sa/bookinfo-reviews"]
16      to:
17        - operation:
18            methods: ["GET"]

需要注意这里配置的 principals 身份要是 reviews 的服务身份。

  • 应该该资源对象后再次在浏览器访问 Bookinfo productpage 页面就可以得到正常结果了。
1[root@master1 istio]#kubectl apply -f ratings-viewer.yaml

image-20231127073712615

  • 测试完成后记得清理上面的资源对象:
1kubectl delete authorizationpolicy allow-nothing
2kubectl delete authorizationpolicy productpage-viewer
3kubectl delete authorizationpolicy details-viewer
4kubectl delete authorizationpolicy reviews-viewer
5kubectl delete authorizationpolicy ratings-viewer

测试结束。😘

2.TCP 请求授权

🚩 实战:TCP 请求授权-20223.11.28(测试成功)

实验环境:

1k8s v1.27.6(containerd://1.6.20)(cni:flannel:v0.22.2)
2istio v1.19.3(--set profile=demo)

实验软件:

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

image-20231105111842627

上面我们学习了如何为 HTTP 请求添加授权策略,接下来我们来了解下如何为 TCP 流量设置授权策略。

首先在命名空间 foo 中部署两个 TCP 工作负载:sleeptcp-echotcp-echo 工作负载会监听 9000、9001 和 9002 端口,并以 hello 为前缀输出它收到的所有流量。比如我们发送 worldtcp-echo,那么它将会回复 hello worldtcp-echo 的 Kubernetes Service 对象只声明了 9000 和 9001 端口。

  • 使用以下命令部署示例命名空间和工作负载:
1kubectl create ns foo
2kubectl apply -f <(istioctl kube-inject -f samples/tcp-echo/tcp-echo.yaml) -n foo
3kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo
  • 我们可以使用以下命令验证 sleep 是否可以成功与 tcp-echo 的 9000 和 9001 端口通信:
 1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
 2 -c sleep -n foo -- sh -c \
 3 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
 4hello port 9000
 5connection succeeded
 6
 7$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
 8 -c sleep -n foo -- sh -c \
 9 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
10hello port 9001
11connection succeeded

配置 ALLOW 授权策略

  • 接下来我们为 tcp-echo 应用创建一个授权策略,该策略将允许请求到 9000 和 9001 端口,资源对象如下所示:
 1#tcp-policy.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: tcp-policy
 6  namespace: foo
 7spec:
 8  selector:
 9    matchLabels:
10      app: tcp-echo
11  action: ALLOW
12  rules:
13    - to:
14        - operation: # 仅允许请求到 9000 和 9001 端口
15            ports: ["9000", "9001"]

部署上面资源:

1kubectl apply -f tcp-policy.yaml
  • 使用以下命令验证是否允许请求 9000 端口:
1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
2 -c sleep -n foo -- sh -c \
3 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
4hello port 9000
5connection succeeded

使用以下命令验证是否允许请求 9001 端口:

1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
2 -c sleep -n foo -- sh -c \
3 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
4hello port 9001
5connection succeeded

这里需要注意下:

我们此时测试pod的9002端口,看还会通吗?

image-20231127215048785

1[root@master1 istio-author]#kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})"  -c sleep -n foo -- sh -c  'echo "port 9002" | nc 10.244.1.23  9002' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
2connection rejected
3
4#可以看到,9002端口不通,符合预期。
  • 如果现在我们为 9000 端口添加一个名为 methods 的 HTTP-only 字段来更新策略,如下所示:
 1#tcp-policy2.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: tcp-policy
 6  namespace: foo
 7spec:
 8  selector:
 9    matchLabels:
10      app: tcp-echo
11  action: ALLOW
12  rules:
13    - to:
14        - operation:
15            methods: ["GET"]
16            ports: ["9000"]

上面的策略规则在对 TCP 流量使用了 HTTP-only 字段(methods),这会导致规则无效,Istio 会忽略无效的 ALLOW 规则。最终结果是该请求被拒绝,因为它与任何 ALLOW 规则都不匹配,所以我们现在验证 9000 端口会被拒绝:

1#部署
2[root@master1 istio-author]#kubectl apply -f tcp-policy2.yaml
3
4$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
5 -c sleep -n foo -- sh -c \
6 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
7connection rejected

当然同样 9001 端口请求与任何 ALLOW 规则也不匹配,请求也会被拒绝:

1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
2 -c sleep -n foo -- sh -c \
3 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
4connection rejected

配置 DENY 授权策略

上面我们是配置的 ALLOW 授权策略,接下来我们来看下如何配置 DENY 授权策略。

  • 如下所示:
 1#tcp-policy3.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: tcp-policy
 6  namespace: foo
 7spec:
 8  selector:
 9    matchLabels:
10      app: tcp-echo
11  action: DENY
12  rules:
13    - to:
14        - operation:
15            methods: ["GET"]

这个授权策略对象为 TCP 端口创建 DENY 规则时会不理解 HTTP-only 字段 methods,由于这个规则的限制性质,将拒绝所有到 tcp 端口的流量:

1#部署
2[root@master1 istio-author]#kubectl apply -f tcp-policy3.yaml
3authorizationpolicy.security.istio.io/tcp-policy configured
4Warning: configured AuthorizationPolicy will deny all traffic to TCP ports under its scope due to the use of only HTTP attributes in a DENY rule; it is recommended to explicitly specify the port
5
6$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
7 -c sleep -n foo -- sh -c \
8 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
9connection rejected

同样 9001 端口的请求也会被拒绝:

1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
2 -c sleep -n foo -- sh -c \
3 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
4connection rejected
  • 如果我们明确指定一个 TCP 端口来进行 DENY,那么会怎样呢?如下所示:
 1#tcp-policy4.yaml
 2apiVersion: security.istio.io/v1
 3kind: AuthorizationPolicy
 4metadata:
 5  name: tcp-policy
 6  namespace: foo
 7spec:
 8  selector:
 9    matchLabels:
10      app: tcp-echo
11  action: DENY
12  rules:
13    - to:
14        - operation:
15            methods: ["GET"]
16            ports: ["9000"]

比如我们请求 9000 端口,请求会被拒绝,这是因为此类请求与上述 DENY 策略中的 ports 是匹配的:

1#部署
2[root@master1 istio-author]#kubectl apply -f tcp-policy4.yaml
3
4$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
5 -c sleep -n foo -- sh -c \
6 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
7connection rejected

但是现在对于 9001 端口的请求,请求会被允许,这是因为此类请求与上述 DENY 策略中的 ports 不匹配:

1$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" \
2 -c sleep -n foo -- sh -c \
3 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected'
4hello port 9001
5connection succeeded

到这里我们就验证了为 TCP 流量进行授权策略配置。

  • 回收测试资源
1[root@master1 istio-author]#pwd
2/root/istio/istio-author
3[root@master1 istio-author]#ls
4tcp-policy2.yaml  tcp-policy3.yaml  tcp-policy4.yaml  tcp-policy.yaml
5[root@master1 istio-author]#kubectl delete -f .
6authorizationpolicy.security.istio.io "tcp-policy" deleted
7Error from server (NotFound): error when deleting "tcp-policy2.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found
8Error from server (NotFound): error when deleting "tcp-policy3.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found
9Error from server (NotFound): error when deleting "tcp-policy4.yaml": authorizationpolicies.security.istio.io "tcp-policy" not found

测试结束。😘

关于我

我的博客主旨:

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

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

image-20230107215114763

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

image-20230107215126971

🍀 个人博客站点

http://onedayxyy.cn/

image-20231113073017981

image-20231113073039083

🍀 语雀

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

image-20231113073101271

🍀 csdn

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

image-20230107215149885

🍀 知乎

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

image-20230107215203185

最后

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

image-20231125080418779

推荐使用微信支付
微信支付二维码
推荐使用支付宝
支付宝二维码
最新文章

文档导航