• 通过 SDS 提供身份服务
    • 开始之前
    • 通过 SDS 提供的密钥和证书支持服务间的双向 TLS
    • 验证没有通过加载 Secret 卷的方式生成的文件
    • 通过 pod 安全策略加固 SDS
    • 清理
    • 注意事项
    • 相关内容

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