09-k8s安全

摘要

本文内容转自网络,个人学习记录使用,请勿传播

kubernetes安全框架

image-20220211175618642

K8S安全控制框架主要由下面3个阶段进行控制,每一个阶段都支持插件方式,通过API Server配置来启用插件。

  1. Authentication(鉴权)

  2. Authorization(授权)

  3. Admission Control(准入控制)

客户端要想访问K8s集群API Server,一般需要证书、Token或 者用户名+密码;如果Pod访问,需要ServiceAccount

鉴权

K8s Apiserver提供三种客户端身份认证:

  • HTTPS 证书认证:基于CA证书签名的数字证书认证(kubeconfig)
  • HTTP Token认证:通过一个Token来识别用户(serviceaccount)
  • HTTP Base认证:用户名+密码的方式认证(1.19版本弃用)

授权(RBAC)

RBAC(Role-Based Access Control,基于角色的访问控制):负责完成授权(Authorization)工作。

RBAC根据API请求属性,决定允许还是拒绝。比较常见的授权维度:

  • user:用户名
  • group:用户分组
  • 资源,例如pod、deployment
  • 资源操作方法:get,list,create,update,patch,watch,delete
  • 命名空间
  • API组

准入控制

Adminssion Control实际上是一个准入控制器插件列表,发送到API Server的请求都需要经过这个列表中的每个准入控

制器插件的检查,检查不通过,则拒绝请求。

  • 启用一个准入控制器:kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

  • 关闭一个准入控制器:kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

  • 查看默认启用:kubectl exec kube-apiserver-k8s-master -n kube-system -- kube-apiserver -h | grep enable-admission-plugins

1
2
# kubectl exec kube-apiserver-kube-0-3.xxx -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
--enable-admission-plugins strings admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.

基于角色的权限访问控制:RBAC

image-20220211181850046

RBAC(Role-Based Access Control,基于角色的访问控制),是K8s默认授权策略,并且是动态配置策略(修改即时生效)。

主体(subject)

  • User:用户
  • Group:用户组
  • ServiceAccount:服务账号

角色

  • Role:授权特定命名空间的访问权限
  • ClusterRole:授权所有命名空间的访问权限

角色绑定

  • RoleBinding:将角色绑定到主体(即subject)
  • ClusterRoleBinding:将集群角色绑定到主体

RoleBinding在指定命名空间中执行授权,ClusterRoleBinding在集群范围执行授权。

案例:为指定用户授权访问不同命名空间权限,例如新入职一个小弟,希望让他先熟悉K8s集群,为了

安全性,先不能给他太大权限,因此先给他授权访问default命名空间Pod读取权限。实施大致步骤:

  1. 用K8S CA签发客户端证书

  2. 生成kubeconfig授权文件

  3. 创建RBAC权限策略

  4. 指定kubeconfig文件测试权限:kubectl get pods –kubeconfig=./aliang.kubeconfig

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
# 客户端签发证书

cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF

cat > demo-csr.json <<EOF
{
"CN": "demo",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}
EOF

cfssl gencert -ca=/etc/kubernetes/pki/ca.crt -ca-key=/etc/kubernetes/pki/ca.key -config=ca-config.json -profile=kubernetes demo-csr.json | cfssljson -bare demo

# 生成kubeconfig授权文件

kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=https://10.0.0.1:6443 \
--kubeconfig=demo.kubeconfig

# 设置客户端认证
kubectl config set-credentials demo \
--client-key=demo-key.pem \
--client-certificate=demo.pem \
--embed-certs=true \
--kubeconfig=demo.kubeconfig

# 设置默认上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=demo \
--kubeconfig=demo.kubeconfig

# 设置当前使用配置
kubectl config use-context kubernetes --kubeconfig=demo.kubeconfig

# 测试发现无权限
kubectl get pod --kubeconfig=./demo.kubeconfig
Error from server (Forbidden): pods is forbidden: User "demo" cannot list resource "pods" in API group "" in the namespace "default"


# 查看所有资源类型
kubectl api-resources

rbac授权资源清单

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
# cat rbac.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # api组,例如apps组,空值表示是核心API组,像namespace、pod、service、pv、pvc都在里面
resources: ["pods"] # 资源名称(复数),例如pods、deployments、services
verbs: ["get", "watch", "list"] # 资源操作方法

---

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: demo
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

服务账号(SA)

服务账号(SA)示例:为一个服务账号分配只能创建deployment、daemonset、statefulset的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建集群角色
kubectl create clusterrole deployment-clusterrole \
--verb=create --resource=deployments,daemonsets,statefulsets

# 创建服务账号
kubectl create serviceaccount cicd-token -n app-team1

# 将服务账号绑定角色
kubectl create rolebinding cicd-token \
--serviceaccount=app-team1:cicd-token \
--clusterrole=deployment-clusterrole -n app-team1

# 测试服务账号权限
kubectl --as=system:serviceaccount:app-team1:cicd-token \
get pods -n app-team1

资源清单

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
# cat sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: cicd-token
namespace: app-team1
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: deployment-clusterrole
rules:
- apiGroups: ["apps"]
resources: ["deployments","daemonsets","statefulsets"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cicd-token
namespace: app-team1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: deployment-clusterrole
subjects:
- kind: ServiceAccount
name: cicd-token
namespace: app-team1

网络策略,控制Pod网络通信

默认情况下,Kubernetes 集群网络没任何网络限制,Pod 可以与任何其他 Pod 通信,在某些场景下就需要进行网络控制,减少网络攻击面,提高安全性,这就会用到网络策略。网络策略(Network Policy):是一个K8s资源,用于限制Pod出入流量,提供Pod级别和Namespace级别网络访问控制。

网络策略的应用场景:

  • 应用程序间的访问控制,例如项目A不能访问项目B的Pod
  • 开发环境命名空间不能访问测试环境命名空间Pod
  • 当Pod暴露到外部时,需要做Pod白名单
  • 多租户网络环境隔离

资源清单常用字段

  • podSelector:目标Pod,根据标签选择。

  • policyTypes:策略类型,指定策略用于入站、出站流量。

  • Ingress:from是可以访问的白名单,可以来自于IP段、命名空间、Pod标签等,ports是可以访问的端口。

  • Egress:这个Pod组可以访问外部的IP段和端口。

案例1:拒绝其他命名空间Pod访问

1
2
3
4
# 准备环境
kubectl run busybox --image=busybox -n test -- sleep 12h
kubectl run web --image=nginx -n test
kubectl exec busybox -n test -- ping 10.244.169.135

需求:test命名空间下所有pod可以互相访问,也可以访问其他命名空间Pod,但其他命名空间不能访问test命名空间Pod。

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-namespaces
namespace: test
spec:
podSelector: {} # 未配置,匹配本命名空间所有pod
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # 未配置,匹配本命名空间所有pod

案例2:同一个命名空间下应用之间限制访问

需求:将test命名空间携带run=web标签的Pod隔离,只允许test命名空间携带run=client1标签的Pod访问80端口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-to-app
namespace: test
spec:
podSelector:
matchLabels:
run: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: client1
ports:
- protocol: TCP
port: 80

案例3:只允许指定命名空间中的应用访问

需求:只允许dev命名空间中的Pod访问test命名空间中的pod 80端口命名空间打标签: kubectl label namespace dev name=dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-port-from-namespace
namespace: test
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: dev
ports:
- protocol: TCP
port: 80