通过 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
参考认证策略任务来设置测试服务。
$ kubectl create ns foo
$ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
$ kubectl create ns bar
$ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar
验证是否所有的双向 TLS 请求都已经成功:
$ 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
sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
验证看是不是没有生成密钥卷文件
下面来验证是不是没有生成密钥卷文件,先访问部署的 workload 边车容器:
$ 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 套接字的修改权限。
$ cat <<EOF | kubectl apply -f -
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: istio-nodeagent
spec:
allowedHostPaths:
- pathPrefix: "/var/run/sds"
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: istio-nodeagent
namespace: istio-system
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- istio-nodeagent
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: istio-nodeagent
namespace: istio-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: istio-nodeagent
subjects:
- kind: ServiceAccount
name: istio-nodeagent-service-account
namespace: istio-system
EOF
- 要阻止其它 pod 修改 Unix domain 套接字,就要修改配置项
allowedHostPaths
,读写权限配置为readOnly: true
, 这个选项是 Citadel 代理用于配置 Unix domain 套接字路径的。
假设以下的 pod 安全策略是之前其它 pod 没有使用过的。如果你已经实施了其它的 pod 安全策略,则给已经存在的策略新增以下的配置值,而不是直接实施配置。
$ cat <<EOF | kubectl apply -f -
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: istio-sds-uds
spec:
# Protect the unix domain socket from unauthorized modification
allowedHostPaths:
- pathPrefix: "/var/run/sds"
readOnly: true
# Allow the istio sidecar injector to work
allowedCapabilities:
- NET_ADMIN
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: istio-sds-uds
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- istio-sds-uds
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: istio-sds-uds
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: istio-sds-uds
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:serviceaccounts
EOF
- 给你的平台启用 pod 安全策略。不同的平台启用的 pod 安全策略是不一样的。请参考你用平台的相关文档。如果在使用 Google Kubernetes Engine (GKE),你必须启用 pod 安全策略控制器。
在启用 pod 安全策略之前要先授权它所需要的权限。一旦策略启用,授权不足 pod 将会无法启动。
- 使用下面的命令重启 Citadel 代理:
$ kubectl delete pod -l 'app=istio-nodeagent' -n istio-system
pod "istio-nodeagent-dplx2" deleted
pod "istio-nodeagent-jrbmx" deleted
pod "istio-nodeagent-rz878" deleted
- 为了验证 Citadel 代理能否使用启用了的 pod 安全策略,等待几秒钟并且执行下面的命令来确认 Citadel 代理已经成功启动。
$ kubectl get pod -l 'app=istio-nodeagent' -n istio-system
NAME READY STATUS RESTARTS AGE
istio-nodeagent-p4p7g 1/1 Running 0 4s
istio-nodeagent-qdwj6 1/1 Running 0 5s
istio-nodeagent-zsk2b 1/1 Running 0 14s
- 执行下面的命令来启动一个 normal pod。
$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: normal
spec:
replicas: 1
selector:
matchLabels:
app: normal
template:
metadata:
labels:
app: normal
spec:
containers:
- name: normal
image: pstauffer/curl
command: ["/bin/sleep", "3650d"]
imagePullPolicy: IfNotPresent
EOF
- 为了验证 normal pod 能不能使用启用了的 pod 安全策略,再等几秒钟并且执行下面的命令来确认 normal pod 已经成功启动。
$ kubectl get pod -l 'app=normal'
NAME READY STATUS RESTARTS AGE
normal-64c6956774-ptpfh 2/2 Running 0 8s
- 启动一个恶意 pod, 这个 pod 会尝试挂载一个有写权限的 Unix domain 套接字。
$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: malicious
spec:
replicas: 1
selector:
matchLabels:
app: malicious
template:
metadata:
labels:
app: malicious
spec:
containers:
- name: malicious
image: pstauffer/curl
command: ["/bin/sleep", "3650d"]
imagePullPolicy: IfNotPresent
volumeMounts:
- name: sds-uds
mountPath: /var/run/sds
volumes:
- name: sds-uds
hostPath:
path: /var/run/sds
type: ""
EOF
- 为了验证这个 Unix domain 套接字被保护了,执行下面的命令来确认这个恶意 pod 无法启动,因为有安全策略。
$ kubectl describe rs -l 'app=malicious' | grep Failed
Pods Status: 0 Running / 0 Waiting / 0 Succeeded / 0 Failed
ReplicaFailure True FailedCreate
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 控制面。
$ kubectl delete ns foo
$ kubectl delete ns bar
$ kubectl delete -f istio-auth-sds.yaml
根据你的平台文档关闭你集群的 pod 安全策略。如果你在使用 GKE,参考关闭 pod 安全策略控制器。
删除 pod 安全策略和测试 deployments:
$ kubectl delete psp istio-sds-uds istio-nodeagent
$ kubectl delete role istio-nodeagent -n istio-system
$ kubectl delete rolebinding istio-nodeagent -n istio-system
$ kubectl delete clusterrole istio-sds-uds
$ kubectl delete clusterrolebinding istio-sds-uds
$ kubectl delete deploy malicious
$ kubectl delete deploy normal
注意事项
目前,SDS 身份提供流程有以下注意事项:
SDS 目前只支持Alpha版本。
目前还无法流畅的将群集从使用密钥卷装载方式迁移到使用 SDS , 功能还在开发中。
相关内容
在 Istio 中配置和管理 DNS 证书。
Istio v1beta1 授权策略的设计原则、基本概述及迁移操作。
一种更安全管理 Istio webhook 的方法。
将需要隔离的环境部署到单独的网格中,并通过网格联邦启用网格间通信。
使用 Istio 实现零代码改动保护多云 Kubernetes 应用。
由于错误的发布操作,出现了错误的 1.2.4 sidecar 镜像。