通过 SDS 进行身份认证

这个任务是讲述 Istio 中如何通过启动 SDS(密钥发现服务)来进行身份认证的。

在 Istio 1.1 之前,Istio workload 的密钥和证书都是由 Citadel 生成的,并且通过挂载 secret-volume 文件的方式下发给 sidecar 上。这种做法有下面一些小缺陷:

  • 证书替换期间的性能下降问题:在证书替换的时候,Envoy 通过热重启来获取新的密钥和证书,这会导致性能下降。

  • 有潜在的安全风险:workload 提供的密钥是通过 Kubernetes 证书来下发的,可以看已知的风险

这些问题都在 Istio 1.1 中通过提供 SDS 身份认证解决了。整个过程可以描述如下:

  • workload 边车 Envoy 向 Citadel 代理请求密钥和证书:Citadel 代理是一个 SDS 服务,作为每个节点上的 DaemonSet 运行。 Envoy 在请求时会传一个 Kubernetes 服务帐号的 JWT 到代理。

  • Citadel 代理产生密钥对并且发送 CSR 请求给 Citadel 服务:Citadel 服务验证收到的 JWT 并且向 Citadel 颁发证书。

  • Citadel 代理发送回密钥和证书给到 workload 边车。

这样做有如下优势:

  • 私钥是永远不会离开节点的:只保存在 Citadel 代理上和 Envoy 边车的内存中。

  • 再也不需要挂载密钥卷了:不在依赖 Kubernetes 的密钥。

  • Envoy 边车可以通过 SDS 的 API 动态的更新密钥和证书:证书的更换也不再需要重启 Envoy 了。

开始之前

参考Istio 安装指南 使用 SDS 配置文件设置 Istio。

通过 SDS 使用密钥/证书为服务到服务提供双向 TLS

参考认证策略任务来设置测试服务。

ZipZipZipZip

  1. $ kubectl create ns foo
  2. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
  3. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
  4. $ kubectl create ns bar
  5. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
  6. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar

验证是否所有的双向 TLS 请求都已经成功:

  1. $ for from in "foo" "bar"; do for to in "foo" "bar"; 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
  2. sleep.foo to httpbin.foo: 200
  3. sleep.foo to httpbin.bar: 200
  4. sleep.bar to httpbin.foo: 200
  5. sleep.bar to httpbin.bar: 200

验证看是不是没有生成密钥卷文件

下面来验证是不是没有生成密钥卷文件,先访问部署的 workload 边车容器:

  1. $ kubectl exec -it $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c istio-proxy -n foo -- /bin/bash

你可以看到 /etc/certs 目录下没有挂载密钥文件。

使用 pod 上的安全策略来保护 SDS

Istio 的密钥发现服务(SDS)使用 Citadel 代理通过 Unix domain 套接字来给 Envoy 边车分发证书。 所有在同一个 Kubernetes 节点上的 pod 通过 Unix domain 套接字共享同一个 Citadel 代理。

为了防止对 Unix domain 套接字的意外修改,需要启用pod 安全策略来限制 pod 对 Unix domain 套接字的权限。否则,有权限修改 deployment 的恶意用户会劫持 Unix domain 套接字来断开 SDS 服务,或者会从运行在同一个 Kubernetes 节点上的其它 pod 那里偷取身份证书。

可以通过执行以下步骤来启用 pod 安全策略:

  • Citadel代理创建成功 Unix domain 套接字才能启动成功。通过实施下面的 pod 安全策略才能只启用 Citadel 代理对 Unix domain 套接字的修改权限。
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: policy/v1beta1
  3. kind: PodSecurityPolicy
  4. metadata:
  5. name: istio-nodeagent
  6. spec:
  7. allowedHostPaths:
  8. - pathPrefix: "/var/run/sds"
  9. seLinux:
  10. rule: RunAsAny
  11. supplementalGroups:
  12. rule: RunAsAny
  13. runAsUser:
  14. rule: RunAsAny
  15. fsGroup:
  16. rule: RunAsAny
  17. volumes:
  18. - '*'
  19. ---
  20. kind: Role
  21. apiVersion: rbac.authorization.k8s.io/v1
  22. metadata:
  23. name: istio-nodeagent
  24. namespace: istio-system
  25. rules:
  26. - apiGroups:
  27. - extensions
  28. resources:
  29. - podsecuritypolicies
  30. resourceNames:
  31. - istio-nodeagent
  32. verbs:
  33. - use
  34. ---
  35. apiVersion: rbac.authorization.k8s.io/v1
  36. kind: RoleBinding
  37. metadata:
  38. name: istio-nodeagent
  39. namespace: istio-system
  40. roleRef:
  41. apiGroup: rbac.authorization.k8s.io
  42. kind: Role
  43. name: istio-nodeagent
  44. subjects:
  45. - kind: ServiceAccount
  46. name: istio-nodeagent-service-account
  47. namespace: istio-system
  48. EOF
  • 要阻止其它 pod 修改 Unix domain 套接字,就要修改配置项 allowedHostPaths ,读写权限配置为readOnly: true, 这个选项是 Citadel 代理用于配置 Unix domain 套接字路径的。

假设以下的 pod 安全策略是之前其它 pod 没有使用过的。如果你已经实施了其它的 pod 安全策略,则给已经存在的策略新增以下的配置值,而不是直接实施配置。

  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: policy/v1beta1
  3. kind: PodSecurityPolicy
  4. metadata:
  5. name: istio-sds-uds
  6. spec:
  7. # Protect the unix domain socket from unauthorized modification
  8. allowedHostPaths:
  9. - pathPrefix: "/var/run/sds"
  10. readOnly: true
  11. # Allow the istio sidecar injector to work
  12. allowedCapabilities:
  13. - NET_ADMIN
  14. seLinux:
  15. rule: RunAsAny
  16. supplementalGroups:
  17. rule: RunAsAny
  18. runAsUser:
  19. rule: RunAsAny
  20. fsGroup:
  21. rule: RunAsAny
  22. volumes:
  23. - '*'
  24. ---
  25. kind: ClusterRole
  26. apiVersion: rbac.authorization.k8s.io/v1
  27. metadata:
  28. name: istio-sds-uds
  29. rules:
  30. - apiGroups:
  31. - extensions
  32. resources:
  33. - podsecuritypolicies
  34. resourceNames:
  35. - istio-sds-uds
  36. verbs:
  37. - use
  38. ---
  39. apiVersion: rbac.authorization.k8s.io/v1
  40. kind: ClusterRoleBinding
  41. metadata:
  42. name: istio-sds-uds
  43. roleRef:
  44. apiGroup: rbac.authorization.k8s.io
  45. kind: ClusterRole
  46. name: istio-sds-uds
  47. subjects:
  48. - apiGroup: rbac.authorization.k8s.io
  49. kind: Group
  50. name: system:serviceaccounts
  51. EOF
  • 给你的平台启用 pod 安全策略。不同的平台启用的 pod 安全策略是不一样的。请参考你用平台的相关文档。如果在使用 Google Kubernetes Engine (GKE),你必须启用 pod 安全策略控制器

在启用 pod 安全策略之前要先授权它所需要的权限。一旦策略启用,授权不足 pod 将会无法启动。

  • 使用下面的命令重启 Citadel 代理:
  1. $ kubectl delete pod -l 'app=istio-nodeagent' -n istio-system
  2. pod "istio-nodeagent-dplx2" deleted
  3. pod "istio-nodeagent-jrbmx" deleted
  4. pod "istio-nodeagent-rz878" deleted
  • 为了验证 Citadel 代理能否使用启用了的 pod 安全策略,等待几秒钟并且执行下面的命令来确认 Citadel 代理已经成功启动。
  1. $ kubectl get pod -l 'app=istio-nodeagent' -n istio-system
  2. NAME READY STATUS RESTARTS AGE
  3. istio-nodeagent-p4p7g 1/1 Running 0 4s
  4. istio-nodeagent-qdwj6 1/1 Running 0 5s
  5. istio-nodeagent-zsk2b 1/1 Running 0 14s
  • 执行下面的命令来启动一个 normal pod。
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: normal
  6. spec:
  7. replicas: 1
  8. selector:
  9. matchLabels:
  10. app: normal
  11. template:
  12. metadata:
  13. labels:
  14. app: normal
  15. spec:
  16. containers:
  17. - name: normal
  18. image: pstauffer/curl
  19. command: ["/bin/sleep", "3650d"]
  20. imagePullPolicy: IfNotPresent
  21. EOF
  • 为了验证 normal pod 能不能使用启用了的 pod 安全策略,再等几秒钟并且执行下面的命令来确认 normal pod 已经成功启动。
  1. $ kubectl get pod -l 'app=normal'
  2. NAME READY STATUS RESTARTS AGE
  3. normal-64c6956774-ptpfh 2/2 Running 0 8s
  • 启动一个恶意 pod, 这个 pod 会尝试挂载一个有写权限的 Unix domain 套接字。
  1. $ cat <<EOF | kubectl apply -f -
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: malicious
  6. spec:
  7. replicas: 1
  8. selector:
  9. matchLabels:
  10. app: malicious
  11. template:
  12. metadata:
  13. labels:
  14. app: malicious
  15. spec:
  16. containers:
  17. - name: malicious
  18. image: pstauffer/curl
  19. command: ["/bin/sleep", "3650d"]
  20. imagePullPolicy: IfNotPresent
  21. volumeMounts:
  22. - name: sds-uds
  23. mountPath: /var/run/sds
  24. volumes:
  25. - name: sds-uds
  26. hostPath:
  27. path: /var/run/sds
  28. type: ""
  29. EOF
  • 为了验证这个 Unix domain 套接字被保护了,执行下面的命令来确认这个恶意 pod 无法启动,因为有安全策略。
  1. $ kubectl describe rs -l 'app=malicious' | grep Failed
  2. Pods Status: 0 Running / 0 Waiting / 0 Succeeded / 0 Failed
  3. ReplicaFailure True FailedCreate
  4. Warning FailedCreate 4s (x13 over 24s) replicaset-controller Error creating: pods "malicious-7dcfb8d648-" is forbidden: unable to validate against any pod security policy: [spec.containers[0].volumeMounts[0].readOnly: Invalid value: false: must be read-only]

清理

  • 清理测试服务和 Istio 控制面。
  1. $ kubectl delete ns foo
  2. $ kubectl delete ns bar
  3. $ kubectl delete -f istio-auth-sds.yaml
  • 根据你的平台文档关闭你集群的 pod 安全策略。如果你在使用 GKE,参考关闭 pod 安全策略控制器

  • 删除 pod 安全策略和测试 deployments:

  1. $ kubectl delete psp istio-sds-uds istio-nodeagent
  2. $ kubectl delete role istio-nodeagent -n istio-system
  3. $ kubectl delete rolebinding istio-nodeagent -n istio-system
  4. $ kubectl delete clusterrole istio-sds-uds
  5. $ kubectl delete clusterrolebinding istio-sds-uds
  6. $ kubectl delete deploy malicious
  7. $ kubectl delete deploy normal

注意事项

目前,SDS 身份提供流程有以下注意事项:

  • SDS 目前只支持Alpha版本。

  • 目前还无法流畅的将群集从使用密钥卷装载方式迁移到使用 SDS , 功能还在开发中。

相关内容

DNS 证书管理

在 Istio 中配置和管理 DNS 证书。

Istio v1beta1 授权策略概述

Istio v1beta1 授权策略的设计原则、基本概述及迁移操作。

安全管理 Webhook

一种更安全管理 Istio webhook 的方法。

用于隔离和边界保护的多网格部署

将需要隔离的环境部署到单独的网格中,并通过网格联邦启用网格间通信。

APP 身份和访问适配器

使用 Istio 实现零代码改动保护多云 Kubernetes 应用。

Istio 1.2.4 sidecar 镜像漏洞

由于错误的发布操作,出现了错误的 1.2.4 sidecar 镜像。