零信任与SPIFFE(一)

概述

传统的网络安全模型通过划分不同的网络分区,同一个网络分区是可信的,不同网络分区之间通过防火墙隔断。这种方式在云原生时代已经变得不可适用了。
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/