概述
传统的网络安全模型通过划分不同的网络分区,同一个网络分区是可信的,不同网络分区之间通过防火墙隔断。这种方式在云原生时代已经变得不可适用了。
1、同一网络分区内流量无法进行管控,特别是是如今随着容器大规模落地,容器IP又不是二层可见和固定的无法进行安全管控。
2、传统网络边界防火墙采用静态方式配置规则,对于云原生这类动态变化的环境无法适应。
零信任安全框架在此背景下提出。
零信任是默认不信任使用,除非通过验证。通过身份认证,访问策略控制,实现最小权限访问控制。零信任安全的本质是以身份为中心进行动态访问控制,SPIFFE项目(Secure Product Identity Framework For Everyone)通用安全身份框架 。通过X.509 证书的形式为生产环境中的每个工作负载提供安全身份分发,认证。
https://spiffe.io/
SPIFFE本身也是开源项目,目前托管在CNCF基金会,在2022年9月正式毕业。
SPIFFE
架构和概念解析
SPIFFE ( Secure Production Identity Framework For Everyone ):通用安全身份认证框架。
SPIRE ( SPIFFE Runtime Environment ):是 SPIFFE 标准的一套生产就绪实现,它执行节点证明和工作负载证明,可以安全地向服务颁发身份凭证,并根据预定义的条件集合验证其他服务的身份。
https://github.com/spiffe/spire
Spire由SPIRE-Server和一个或多个SPIRE-Agent组成。
Server端充当签名机构(CA)通过Agent颁发给工作负载的证书。它还进行证书维护和验证。
Agent运行在每个workload所在节点上,作用是从Server端接受证书,并将其存储在缓存中。另外是对workload暴露SPIFFE Workload API 充当 SDS(secret discovery service)角色处理整个mTLs流量进行证书交互和验证
SPIFFE安全框架主要包含以下部分:
SPIFFE ID:用于标识对应信任域的工作负载,类似URI格式的字符串包含以下
由spiffe://信任域的名字/工作负载名字或对应的身份标识
SVID(SPIFFE Verifiable Identity Document):
svid可以是两种格式之一\:X.509证书或jwt。证书svid可用于建立端到端相互TLS
加密连接。jwt在端到端相互TLS加密不需要或不需要的情况下非常有用,例如当
使用负载均衡器。jwt对于已经支持基于jwt的身份验证的各种云服务的身份验证也很有用。无论是使用JWT SVIDs还是X.509 SVIDs, SPIFFE id、信任包格式和工作负载API都是相同的。
Trust Bundle:用于验证svid的公钥集
Workload API:工作负载通过此api获取对应的SPIFFE ID、SVID、Trust Bundle。
SPIFFE 联邦:不同信任域共享SPIFFE信任包,比如数据中心A的Spire环境与数据中心B的Spire环境建立联邦关系就可以互相配置和检查
注:这里写的workload(工作负载)并不等同于k8s里面的workload,主要指的是需要接入SPIFFE的对象可以是docker容器、VM、k8s-pod等等。
演示
软件版本:
1、kubernetes v1.24.8
2、Spire:v1.5.3
Spire部署
部署local-path-provisioner
因为Spire-Server为有状态服务,依赖存储,所以这里部署local-path-provisioner
并设置为默认StorageClass
https://github.com/rancher/local-path-provisioner.git
完成后
1 2 3 4 5
| kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 2d7h
|
部署Spire-server和Spire-agent
clone此项目
1
| git clone https://github.com/spiffe/spire-tutorials
|
切换到spire-tutorials/k8s/quickstart目录
1
| kubectl apply -f spire-namespace.yaml
|
配置spire-server权限
1 2 3 4 5 6 7
| kubectl apply \
-f server-account.yaml \
-f spire-bundle-configmap.yaml \
-f server-cluster-role.yaml
|
部署Spire-server
1 2 3 4 5 6 7
| kubectl apply \
-f server-configmap.yaml \
-f server-statefulset.yaml \
-f server-service.yaml
|
查看部署状态
1 2 3 4 5
| kubectl get statefulset --namespace spire
NAME READY AGE
spire-server 1/1 2d8h
|
部署spire-agent
1、配置权限
1 2 3 4 5
| kubectl apply \
-f agent-account.yaml \
-f agent-cluster-role.yaml
|
2、部署spire-agent
1 2 3 4 5
| kubectl apply \
-f agent-configmap.yaml \
-f agent-daemonset.yaml
|
3、检查
1 2 3 4 5
| kubectl get daemonset --namespace spire
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
spire-agent 2 2 2 2 2 <none> 2d8h
|
4、注册spire-agent
1 2 3 4 5 6 7 8 9 10 11 12 13
| kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/spire/sa/spire-agent \
-selector k8s_sat:cluster:demo-cluster \
-selector k8s_sat:agent_ns:spire \
-selector k8s_sat:agent_sa:spire-agent \
-node
|
1 2 3 4 5 6 7 8 9 10 11
| kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/default/sa/default \
-parentID spiffe://example.org/ns/spire/sa/spire-agent \
-selector k8s:ns:default \
-selector k8s:sa:default
|
5、验证
Spire-agent默认会将socket文件映射到k8s集群主机的/run/spire/sockets/agent.sock,部署测试容器查看
1
| kubectl apply -f client-deployment.yaml
|
验证容器是否可以访问socket
1 2 3
| kubectl exec -it $(kubectl get pods -o=jsonpath='{.items[0].metadata.name}' \
-l app=client) -- /opt/spire/bin/spire-agent api fetch -socketPath /run/spire/sockets/agent.sock
|
如果agent正常运行,将看到一个 SVID 列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| SPIFFE ID: spiffe://example.org/ns/default/sa/default
SVID Valid After: 2022-12-25 11:41:16 +0000 UTC
SVID Valid Until: 2022-12-25 12:41:26 +0000 UTC
CA #1 Valid After: 2022-12-23 15:04:07 +0000 UTC
CA #1 Valid Until: 2022-12-24 15:04:17 +0000 UTC
CA #2 Valid After: 2022-12-24 03:04:07 +0000 UTC
CA #2 Valid Until: 2022-12-25 03:04:17 +0000 UTC
CA #3 Valid After: 2022-12-24 15:04:07 +0000 UTC
CA #3 Valid Until: 2022-12-25 15:04:17 +0000 UTC
CA #4 Valid After: 2022-12-25 03:04:07 +0000 UTC
CA #4 Valid Until: 2022-12-26 03:04:17 +0000 UTC
|
Demo应用部署
本次演示
将Envoy与X.509-SVID结合使用保护微服务通信
如图所示,前端服务通过sidecar Envoy执行X.509 SVID 身份验证与实例建立的起mTLS连接,连接到后端服务。
SPIRE Agent原生支持做为Envoy的SDS服务。通过本地socket连接SDS服务。
切换到spire-tutorials/k8s/envoy-x509目录
部署应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| kubectl apply -k k8s/.
configmap/backend-balance-json-data created
configmap/backend-envoy created
configmap/backend-profile-json-data created
configmap/backend-transactions-json-data created
configmap/frontend-2-envoy created
configmap/frontend-envoy created
configmap/symbank-webapp-2-config created
configmap/symbank-webapp-config created
service/backend-envoy created
service/frontend-2 created
service/frontend created
deployment.apps/backend created
deployment.apps/frontend-2 created
deployment.apps/frontend created
|
以backend模块为例
查看k8s/backend/config/envoy.yaml文件,可以科技Envoy配置的与spire-agent的socket连接
1 2 3 4 5 6 7 8 9 10 11 12 13
| clusters:
- name: spire_agent
connect_timeout: 0.25s
http2_protocol_options: {}
hosts:
- pipe:
path: /run/spire/sockets/agent.sock
|
手动将backend、frontend、frontend-2注册到sprie-server,当然SPIFFE也有自动正常功能就是使用SPIRE Controller Manager 模块(https://github.com/spiffe/spire-controller-manager)
1
| bash create-registration-entries.sh
|
注册完成后可以查看注册的服务
1
| kubectl exec -n spire spire-server-0 -c spire-server -- /opt/spire/bin/spire-server entry show -selector k8s:ns:default
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| Found 4 entries
Entry ID : 3478c441-3e25-40e7-96d9-ef74611f2205
SPIFFE ID : spiffe://example.org/ns/default/sa/default
Parent ID : spiffe://example.org/ns/spire/sa/spire-agent
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:ns:default
Selector : k8s:sa:default
Entry ID : c188d47c-e886-492e-bf67-6a6bf42c3667
SPIFFE ID : spiffe://example.org/ns/default/sa/default/backend
Parent ID : spiffe://example.org/ns/spire/sa/spire-agent
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:container-name:envoy
Selector : k8s:ns:default
Selector : k8s:pod-label:app:backend
Selector : k8s:sa:default
Entry ID : 6c376401-67d4-499a-a9d9-6ab71caf69c4
SPIFFE ID : spiffe://example.org/ns/default/sa/default/frontend
Parent ID : spiffe://example.org/ns/spire/sa/spire-agent
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:container-name:envoy
Selector : k8s:ns:default
Selector : k8s:pod-label:app:frontend
Selector : k8s:sa:default
Entry ID : 49f88c69-b4ee-4656-b740-6dbee5bb89a3
SPIFFE ID : spiffe://example.org/ns/default/sa/default/frontend-2
Parent ID : spiffe://example.org/ns/spire/sa/spire-agent
Revision : 0
X509-SVID TTL : default
JWT-SVID TTL : default
Selector : k8s:container-name:envoy
Selector : k8s:ns:default
Selector : k8s:pod-label:app:frontend-2
Selector : k8s:sa:default
|
可以看见对应的SPIFFE ID
访问服务
1 2 3 4 5 6 7 8 9 10 11 12 13
| kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-envoy ClusterIP None <none> 9001/TCP 2d8h
frontend LoadBalancer 10.43.106.227 <pending> 3000:32082/TCP 2d8h
frontend-2 LoadBalancer 10.43.203.167 <pending> 3002:30664/TCP 2d8h
go-demo NodePort 10.43.120.2 <none> 8080:30007/TCP 2d10h
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 2d10h
|
frontend对应的NodePort端口为32082
frontend-2对应的NodePort端口为30664
frontend显示Jacob Marley的账户情况
frontend-2显示Alex Fergus的账户情况
更新策略只允许frontend服务访问backend访问
1
| kubectl apply -f backend-envoy-configmap-update.yaml
|
实际上就是更新backend的Envoy配置对应的 k8s/backend/config/envoy.yaml
删除了以下条目
1
| - exact: "spiffe://example.org/ns/default/sa/default/frontend-2"
|
1 2 3 4 5
| match_subject_alt_names:
- exact: "spiffe://example.org/ns/default/sa/default/frontend"
- exact: "spiffe://example.org/ns/default/sa/default/frontend-2"
|
重启backend服务获取最新配置
1 2 3
| kubectl scale deployment backend --replicas=0
kubectl scale deployment backend --replicas=1
|
在次访问frontend正常显示,访问frontend-2
总结
SPIFFE支持多种方式集成如和Istio的envoy-sidecar、OPA策略等方式,可以非常灵活细粒化控制应用访问权限。
参考链接:
https://jimmysong.io/blog/why-istio-need-spire/
https://mp.weixin.qq.com/s/4eEEYb8RuOFOmLcdL3N6wA
https://atbug.com/what-is-spiffe-and-spire/
https://www.nginx-cn.net/blog/mtls-architecture-nginx-service-mesh/