3

Istio(九):istio安全之授权 - 人生的哲理

 1 year ago
source link: https://www.cnblogs.com/renshengdezheli/p/16840393.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一.模块概览

在Kubernetes集群中,可以对用户进行RBAC授权role,rolebinding,clusterrole,clusterrolebinding;对于istio来说,可以对资源定义AuthorizationPolicy授权策略,执行拒绝、允许或审计动作。

在 Istio 中,有多个组件参与提供安全功能:

  • 用于管理钥匙和证书的证书颁发机构(CA)。
  • Sidecar 和周边代理:实现客户端和服务器之间的安全通信,它们作为政策执行点(Policy Enforcement Point,简称PEP)工作
  • Envoy 代理扩展:管理遥测和审计
  • 配置 API 服务器:分发认证、授权策略和安全命名信息

istio授权是在认证通过之后进行的,关于istio认证,可以查看博客《Istio(八):istio安全之认证,启用mTLS》https://www.cnblogs.com/renshengdezheli/p/16840382.html

使用istio授权的前提是已经安装好了istio,关于istio的安装部署,请查看博客《Istio(二):在Kubernetes(k8s)集群上安装部署istio1.14》https://www.cnblogs.com/renshengdezheli/p/16836404.html

二.系统环境

服务器版本 docker软件版本 Kubernetes(k8s)集群版本 Istio软件版本 CPU架构
CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 Istio1.14 x86_64

Kubernetes集群架构:k8scloude1作为master节点,k8scloude2,k8scloude3作为worker节点

服务器 操作系统版本 CPU架构 进程 功能描述
k8scloude1/192.168.110.130 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master节点
k8scloude2/192.168.110.129 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点
k8scloude3/192.168.110.128 CentOS Linux release 7.4.1708 (Core) x86_64 docker,kubelet,kube-proxy,calico k8s worker节点

三.istio授权

3.1 istio授权

授权是对访问控制问题中访问控制部分的响应。某个(经过认证的)主体是否被允许操作某个对象?用户 A 能否向服务 B 的路径 /hello 发送一个 GET 请求?

请注意,尽管主体可以被认证,但它可能不被允许执行某个动作。你的公司 ID 卡可能是有效的、真实的,但我不能用它来进入另一家公司的办公室。如果我们继续之前的海关官员的比喻,我们可以说授权类似于你护照上的签证章。

这就引出了下一个问题 —— 有认证而无授权(反之亦然)对我们没有什么好处。对于适当的访问控制,我们需要两者。让我给你举个例子:如果我们只认证主体而不授权他们,他们就可以做任何他们想做的事,对任何对象执行任何操作。相反,如果我们授权了一个请求,但我们没有认证它,我们就可以假装成其他人,再次对任何对象执行任何操作

Istio 允许我们使用 AuthorizationPolicy 资源在网格、命名空间和工作负载层面定义访问控制。AuthorizationPolicy 支持 DENY、ALLOW、AUDIT 和 CUSTOM 操作

每个 Envoy 代理实例都运行一个授权引擎,在运行时对请求进行授权。当请求到达代理时,引擎会根据授权策略评估请求的上下文,并返回 ALLOW 或 DENY。AUDIT 动作决定是否记录符合规则的请求。注意,AUDIT 策略并不影响请求被允许或拒绝

没有必要明确地启用授权功能。为了执行访问控制,我们可以创建一个授权策略来应用于我们的工作负载。

AuthorizationPolicy 资源是我们可以利用 PeerAuthentication 策略和 RequestAuthentication 策略中的主体的地方。

在定义 AuthorizationPolicy 的时候,我们需要考虑三个部分

  1. 选择要应用该策略的工作负载
  2. 要采取的行动(拒绝、允许或审计)
  3. 采取该行动的规则

让我们看看下面这个例子如何与 AuthorizationPolicy 资源中的字段相对应。

 apiVersion: security.istio.io/v1beta1
 kind: AuthorizationPolicy
 metadata:
  name: customers-deny
  namespace: default
 spec:
  selector:
    matchLabels:
      app: customers
      version: v2
  action: DENY
  rules:
  - from:
    - source:
        notNamespaces: ["default"]

使用 selectormatchLabels,我们可以选择策略所适用的工作负载。在我们的案例中,我们选择的是所有设置了 app: customersversion: v2 标签的工作负载。action 字段被设置为 DENY

最后,我们在 rules 字段中定义所有规则。我们例子中的规则是说,当请求来自 default 命名空间之外时,拒绝对 customers v2 工作负载的请求(action)

除了规则中的 from 字段外,我们还可以使用 towhen 字段进一步定制规则。让我们看一个使用这些字段的例子。

 apiVersion: security.istio.io/v1beta1
 kind: AuthorizationPolicy
 metadata:
  name: customers-deny
  namespace: default
 spec:
  selector:
    matchLabels:
      app: customers
      version: v2
  action: DENY
  rules:
  - from:
    - source:
        notNamespaces: ["default"]
  - to:
     - operation:
         methods: ["GET"]
  - when:
     - key: request.headers [User-Agent]
       values: ["Mozilla/*"]

我们在规则部分添加了 towhen 字段。如果我们翻译一下上面的规则,我们可以说,当客户的 GET 请求来自 default 命名空间之外,并且 User Agent 头的值与正则表达式 Mozilla/* 相匹配时,我们会拒绝 customers v2 的工作负载

总的来说,to 定义了策略所允许的行动,from 定义了谁可以采取这些行动,when 定义了每个请求必须具备的属性,以便被策略所允许,selector 定义了哪些工作负载将执行该策略

如果一个工作负载有多个策略,则首先评估拒绝的策略。评估遵循这些规则:

  1. 如果有与请求相匹配的 DENY 策略,则拒绝该请求
  2. 如果没有适合该工作负载的 ALLOW 策略,则允许该请求。
  3. 如果有任何 ALLOW 策略与该请求相匹配,则允许该请求。
  4. 拒绝该请求

3.2 来源

我们在上述例子中使用的源是 notNamespaces。我们还可以使用以下任何一个字段来指定请求的来源,如表中所示。

来源 示例 释义
principals principals: ["my-service-account"] 任何是有 my-service-account 的工作负载
notPrincipals notPrincipals: ["my-service-account"] 除了 my-service-account 的任何工作负载
requestPrincipals requestPrincipals: ["my-issuer/hello"] 任何具有有效 JWT 和请求主体 my-issuer/hello 的工作负载
notRequestPrincipals notRequestPrincipals: ["*"] 任何没有请求主体的工作负载(只有有效的 JWT 令牌)。
namespaces namespaces: ["default"] 任何来自 default 命名空间的工作负载
notNamespaces notNamespaces: ["prod"] 任何不在 prod 命名空间的工作负载
ipBlocks ipBlocks: ["1.2.3.4","9.8.7.6/15"] 任何具有 1.2.3.4 的 IP 地址或来自 CIDR 块的 IP 地址的工作负载
notIpBlock notIpBlocks: ["1.2.3.4/24"] Any IP address that's outside of the CIDR block

3.3 操作

操作被定义在 to 字段下,如果多于一个,则使用 AND 语义。就像来源一样,操作是成对的,有正反两面的匹配。设置在操作字段的值是字符串:

  • hostsnotHosts
  • portsnotPorts
  • methodsnotMethods
  • pathsnotPath

所有这些操作都适用于请求属性。例如,要在一个特定的请求路径上进行匹配,我们可以使用路径。paths:["/api/*","/admin"] 或特定的端口 ports: ["8080"],以此类推。

3.4 条件

为了指定条件,我们必须提供一个 key 字段。key 字段是一个 Istio 属性的名称。例如,request.headerssource.ipdestination.port 等等。关于支持的属性的完整列表,请参考 授权政策条件

条件的第二部分是 valuesnotValues 的字符串列表。下面是一个 when 条件的片段:

 ...
  - when:
     - key: source.ip
       notValues: ["10.0.1.1"]

四.实战:授权(访问控制)

4.1 访问控制

在这个实验中,我们将学习如何使用授权策略来控制工作负载之间的访问。

首先部署 Gateway:

 apiVersion: networking.istio.io/v1alpha3
 kind: Gateway
 metadata:
   name: gateway
 spec:
   selector:
     istio: ingressgateway
   servers:
     - port:
         number: 80
         name: http
         protocol: HTTP
       hosts:
         - '*'

将上述 YAML 保存为 gateway.yaml,并使用 kubectl apply -f gateway.yaml 部署网关。

接下来,我们将创建 Web 前端部署、服务账户、服务 和 VirtualService。

 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: web-frontend
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: web-frontend
   template:
     metadata:
       labels:
         app: web-frontend
         version: v1
     spec:
       serviceAccountName: web-frontend
       containers:
         - image: gcr.io/tetratelabs/web-frontend:1.0.0
           imagePullPolicy: Always
           name: web
           ports:
             - containerPort: 8080
           env:
             - name: CUSTOMER_SERVICE_URL
               value: 'http://customers.default.svc.cluster.local'
 ---
 kind: Service
 apiVersion: v1
 metadata:
   name: web-frontend
   labels:
     app: web-frontend
 spec:
   selector:
     app: web-frontend
   ports:
     - port: 80
       name: http
       targetPort: 8080
 ---
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: web-frontend
 spec:
   hosts:
     - '*'
   gateways:
     - gateway
   http:
     - route:
         - destination:
             host: web-frontend.default.svc.cluster.local
             port:
               number: 80

将上述 YAML 保存为 web-frontend.yaml,并使用 kubectl apply -f web-frontend.yaml 创建资源。

最后,我们将部署 customers v1 服务。

 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: customers-v1
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: customers-v1
   labels:
     app: customers
     version: v1
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: customers
       version: v1
   template:
     metadata:
       labels:
         app: customers
         version: v1
     spec:
       serviceAccountName: customers-v1
       containers:
         - image: gcr.io/tetratelabs/customers:1.0.0
           imagePullPolicy: Always
           name: svc
           ports:
             - containerPort: 3000
 ---
 kind: Service
 apiVersion: v1
 metadata:
   name: customers
   labels:
     app: customers
 spec:
   selector:
     app: customers
   ports:
     - port: 80
       name: http
       targetPort: 3000
 ---
 apiVersion: networking.istio.io/v1alpha3
 kind: VirtualService
 metadata:
   name: customers
 spec:
   hosts:
     - 'customers.default.svc.cluster.local'
   http:
     - route:
         - destination:
             host: customers.default.svc.cluster.local
             port:
               number: 80

将上述内容保存为 customers-v1.yaml,并使用 kubectl apply -f customers-v1.yaml 创建部署和服务。如果我们打开 GATEWAY_URL,应该会显示带有 customers v1 服务数据的 web 前端页面。

让我们先创建一个授权策略,拒绝 default 命名空间的所有请求

 apiVersion: security.istio.io/v1beta1
 kind: AuthorizationPolicy
 metadata:
  name: deny-all
  namespace: default
 spec:
   {}

将上述内容保存为 deny-all.yaml,并使用 kubectl apply -f deny-all.yaml 创建该策略。

如果我们尝试访问 GATEWAY_URL,我们将得到以下响应。

 RBAC: access denied

同样,如果我们试图在集群内运行一个 Pod,并从 default 命名空间内向 Web 前端或 customers 服务提出请求,我们会得到同样的错误。

让我们试试吧。

 $ kubectl run curl --image=radial/busyboxplus:curl -i --tty
 If you don't see a command prompt, try pressing enter.
 [ root@curl:/ ]$ curl customers
 RBAC: access denied
 [ root@curl:/ ]$ curl web-frontend
 RBAC: access denied
 [ root@curl:/ ]$

在这两种情况下,我们都得到了拒绝访问的错误。

我们要做的第一件事是使用 ALLOW 动作,允许从入口网关向 web-frontend 应用程序发送请求。在规则中,我们指定了入口网关运行的源命名空间(istio-system)和入口网关的服务账户名称

 apiVersion: security.istio.io/v1beta1
 kind: AuthorizationPolicy
 metadata:
   name: allow-ingress-frontend
   namespace: default
 spec:
   selector:
     matchLabels:
       app: web-frontend
   action: ALLOW
   rules:
     - from:
         - source:
             namespaces: ["istio-system"]
         - source:
             principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]

将上述内容保存为 allow-ingress-frontend.yaml,并使用 kubectl apply -f allow-ingress-frontend.yaml 创建策略。

如果我们尝试从我们的主机向GATEWAY_URL 发出请求,这次我们会得到一个不同的错误。

 $ curl http://$GATEWAY_URL
 "Request failed with status code 403"

请注意,策略需要几秒钟才能分发到所有代理,所以你可能仍然会看到 RBAC:access denied 的消息,时间为几秒钟。

这个错误来自 customers 服务——记得我们允许调用 Web 前端。然而,web-frontend仍然不能调用 customers 服务。

如果我们回到我们在集群内运行的 curl Pod,尝试请求 http://web-frontend,我们会得到一个 RBAC 错误。DENY 策略是有效的,我们只允许从入口网关进行调用。

当我们部署 Web 前端时,我们也为 Pod 创建了一个服务账户(否则,命名空间中的所有 Pod 都被分配了默认的服务账户)。现在我们可以使用该服务账户来指定 customers 服务调用的来源

 apiVersion: security.istio.io/v1beta1
 kind: AuthorizationPolicy
 metadata:
   name: allow-web-frontend-customers
   namespace: default
 spec:
   selector:
     matchLabels:
         app: customers
         version: v1
   action: ALLOW
   rules:
   - from:
     - source:
         namespaces: ["default"]
       source:
         principals: ["cluster.local/ns/default/sa/web-frontend"]

将上述 YAML 保存为 allow-web-frontend-customers.yaml,并使用 kubectl apply -f allow-web-frontend-customers.yaml 创建策略。

一旦策略被创建,我们将看到 Web 前端再次工作——它将获得 customers 服务的回应。

我们使用了多个授权策略,明确地允许从入口到前端以及从前端到 customers 服务的调用。使用 deny-all 策略是一个很好的开始,因为我们可以控制、管理,然后明确地允许我们希望在服务之间发生的通信。

4.2 清理

删除 Deployment、Service、VirtualService 和 Gateway:

 kubectl delete sa customers-v1 web-frontend
 kubectl delete deploy web-frontend customers-v1
 kubectl delete svc customers web-frontend
 kubectl delete vs customers web-frontend
 kubectl delete gateway gateway
 kubectl delete authorizationpolicy allow-ingress-frontend allow-web-frontend-customers deny-all
 kubectl delete pod curl

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK