通过 SDS 提供身份服务

该任务演示 Istio 如何通过 SDS (secret discovery service) 来提供身份服务。

Istio 1.1 之前,Istio 为工作负载提供的密钥和证书是由 Citadel 生成并使用加载 Secret 卷的方式分发给 Sidecar 的,这种方式有几大缺陷:

  • 证书轮换造成的性能损失:证书发生轮换时,Envoy 会进行热重启以加载新的证书和密钥,会造成性能下降。

  • 潜在的安全漏洞:工作负载的私钥使用 Kubernetes Secret 的方式进行分发,存在一定风险

在 Istio 1.1 之中,上述问题可以使用 SDS 来解决。以下描述了它的工作流程:

  • 工作负载 Envoy sidecar 从 Citadel 代理中请求密钥和证书:Citadel 代理是一个 SDS 服务器,这一代理以 DaemonSet 的形式在每个节点上运行,在这一请求中,Envoy 把 Kubernetes service account 的 JWT 传递给 Citadel 代理。

  • Citadel 代理生成密钥对,并向 Citadel 发送 CSR 请求:Citadel 校验 JWT,并给 Citadel 代理签发证书。

  • Citadel 代理把密钥和证书返回给工作负载的 Sidecar。

这种方法有如下好处:

  • 私钥不会离开节点:私钥仅存在于 Citadel 代理和 Envoy Sidecar 的内存中。

  • 不再需要加载 Secret 卷:去掉对 Kubernetes Secret 的依赖。

  • Sidecar 能够利用 SDS API 动态的刷新密钥和证书:证书轮换过程不再需要重启 Envoy。

开始之前

  • 按照 安装说明 安装 Istio,并启用 SDS 和全局的双向 TLS:

通过 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

验证没有通过加载 Secret 卷的方式生成的文件

验证是否存在通过加载 Secret 卷的方式生成的文件,可以访问工作负载的 Sidecar 容器:

  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 文件夹中没有加载 Secret 卷生成的文件。

通过 pod 安全策略加固 SDS

Istio 的密钥分发服务(SDS,Secret Discovery Service)通过 Citadel 代理将证书分发到 Envoy sidecar,传输过程基于 Unix domain socket 。所有位于同一 Kubernetes node 节点的 pod 共用一个 Citadel 代理和 Unix domain socket。

为了避免对 Unix domain socket 的意外修改,可以开启 pod 安全策略 限制 pod 对 Unix domain socket 的权限。否则,一个有权限修改 deployment 的恶意用户可以通过劫持 Unix domain socket 来破坏 SDS 服务,或者窃取运行在相同 Kubernetes 节点上的其他 pod 的证书。

可以按照以下步骤开启 pod 安全策略:

  • Citadel 代理如果没有权限创建 Unix domain socket,它会启动失败。因此,应用以下 pod 安全策略只允许 Citadel 代理修改 Unix domain socket:
  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 socket 文件,修改它们的 pod 安全策略配置项 allowedHostPaths(为只读 readOnly: true),目录为 Citadel 代理配置 Unix domain socket 路径。

以下 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 安全策略。每一个平台中开启它的步骤不同,请阅读平台相关的文档。如果您使用的是 Google Kubernetes Engine (GKE),可以按照开启 pod 安全策略控制器来操作。

在您开启它之前,需要授予所有必须的权限。一旦策略开启,如果 pod 需要的某个权限没有授予,则 pod 将不能启动。

  • 执行以下命令重启 Citadel 代理:
  1. $ kubectl delete pod -l 'app=nodeagent' -n istio-system
  2. pod "istio-nodeagent-dplx2" deleted
  3. pod "istio-nodeagent-jrbmx" deleted
  4. pod "istio-nodeagent-rz878" deleted
  • 验证 Citadel 代理启用了 pod 安全策略。等待几秒钟,然后执行以下命令确认代理成功启动:
  1. $ kubectl get pod -l 'app=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
  • 执行以下命令启动普通 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
  • 验证普通 pod 已启用 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 socket:
  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 socket 确实受到保护。执行以下命令,确认恶意的 pod 由于启用了 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 安全策略以及 deployment:

  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 阶段。

  • 从 Secret 挂载卷到 SDS 的平滑迁移过程,也还在开发之中。

相关内容

Multi-mesh deployments for isolation and boundary protection

Deploy environments that require isolation into separate meshes and enable inter-mesh communication by mesh federation.

App Identity and Access Adapter

Using Istio to secure multi-cloud Kubernetes applications with zero code changes.

Change in Secret Discovery Service in Istio 1.3

Taking advantage of Kubernetes trustworthy JWTs to issue certificates for workload instances more securely.

Istio 1.2.4 sidecar image vulnerability

An erroneous 1.2.4 sidecar image was available due to a faulty release operation.

Secure Control of Egress Traffic in Istio, part 3

Comparison of alternative solutions to control egress traffic including performance considerations.

Secure Control of Egress Traffic in Istio, part 2

Use Istio Egress Traffic Control to prevent attacks involving egress traffic.