kubernetes安全机制
大概分为三个部分,其一Kubernetes
安全框架,其二传输安全,认证。授权准入控制,其三使用RBAC
授权,这算是一系列流程,了解一下每个环节都做了哪些操作。
kubernetes安全框架
当你使用kubectl||API||UI
实际上就是操作apiserver
上的资源,之前创建过Deployment
,使用api
版本为apps/v1
,也就是这个,
apiVersion: apps/v1
kind: Deployment
在你创建的时候apiserver
会识别你请求的资源,也就是上面的那两个,如果无法识别直接报错,识别成功后会经历三个阶段,第一认证,第二授权,第三准入控制,当你发起一个请求需要过了这三步验证K8S
才会为你创建资源,如果这三步有任意一步出现问题,那你看到的就是一个失败的结果。
通常用户如果要安全访问集群Apiserver
往往需要证书、token
、或用户名加密码,token
之前配置过,手动生成的一个token
,而且指定了一个token
文件,也就是这里。
[root@k8s01 ~]# cat /opt/kubernetes/cfg/kube-apiserver.conf | grep token.csv
--token-auth-file=/opt/kubernetes/cfg/token.csv \
[root@k8s01 ~]#
这个token
文件有一个值是随机生成的,这里就是使用token
来认证的,指定了用户,把token
对应的用户绑定相对应的权限,那么拿着个token
值就有相对应的权限来访问apiserver
了。
[root@k8s01 ~]# kubectl get serviceaccounts
NAME SECRETS AGE
default 1 7h2m
nfs-client-provisioner 1 6h46m
[root@k8s01 ~]#
可以通过serveraccount
在pod
中去访问apiserver
,说白了就是不同类型的来访问apiserver
都会有不同的方式,apiserver
在各组件之间起到了协调的作用,和集群访问入口的功能,你想访问集群资源就必须得经过Apiserver
。
K8S
安全控制框架主要是由以下三个阶段进行控制,
Authentication
Authorrization
AdmissionControl
如图所示
每一个阶段都支持插件方式,需要通过API Server
配置来启用插件,之前都已经配置过了,现在看一下,例如准入控制
[root@k8s01 ~]# cat /opt/kubernetes/cfg/kube-apiserver.conf | grep -i Admission
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
授权启用了RBAC
[root@k8s01 ~]# cat /opt/kubernetes/cfg/kube-apiserver.conf | grep -i Author
--authorization-mode=RBAC,Node \
Authentication
也就是认证,在认证之前还有个传输,现在已经告别了8080
,使用了6443
,现在8080
已经不对外提供服务了,对外提供服务的是6443
,8080
主要是本地master
组件来连接使用。
认证方面提供了三种客户端认证方式。
- HTTPS证书认证:基于CA证书签名的数字证书认证
- 之前部署
K8S
时我们生成了N
多证书,通过证书内的CN
字段就可以识别出您是谁。
- 之前部署
- HTTP TOKEN认证: 通过TOKEN来识别用户
- 这个也用到了,就是在
apiserver
中配置的那个token
文件,那个是做kubelet-bootstrap
认证时候用到的,这个用的比较广泛
- 这个也用到了,就是在
- HTTP Base认证: 用户名加密码的方式认证
- 这种用的就很少了,安全系数比较低。
这是传输和认证层面,第一阶段验证你的身份,你可以理解为这是门禁,你通过工卡进行身份验证,验证通过之后你就能进入到某片区域了。
Authorrization
第二阶段,授权,第一阶段你身份验证通过了,然后进入到了某片区域,但是这片区域有很多房间,具体你能进入到哪个房间就得看你工卡的授权了,你有权限就可以刷开某个门,没权限你就是刷不开,所以这就涉及到授权了,一般用的就是RBAC
,基于角色的访问控制,角色就是具体的访问权限的集合,他是负责完成授权工作的。
他去判断你是否有权限去访问某些资源,他会检查以下属性,如图。
在你请求的时候我绝对会携带我要访问哪个资源,下面他就开始检查你有没有权限去访问那个资源,如果没授权的话在这个阶段直接给你拒绝了,你看到的是没有权限访问,所以这就是授权这个阶段。
AdmissionControl
准入控制,前两步认证都过了就该这个了,他实际上是一个准入控制器插件列表,发送到API Server
的请求都需要经过这个列表中的每个准入控制器检查,检查不通过,则拒绝请求,啥意思呢,大概是这样,我现在想通过apiserver
限制pod
资源,但是想限制pod
资源需要用到apiserver
的LimitRanger
插件,如果这个插件没启用直接拒绝请求,提示我不支持这个撒,现在这个插件启着呢,所以不会有这个问题,他的工作逻辑就是这样,下面主要看一下RBAC
授权。
RBAC授权
也就是上文第二阶段的授权,它允许我们通过API Server
来动态修改配置,实时生效,在开始之前先来了解一下RBAC
的组成。
RBAC核心概念
- 角色
(Role,ClusterRole)
- Role:授权特定命名空间的访问权限,
K8S
逻辑隔离是使用namespaces
实现的,它的授权是在命名空间层面的,你能不能访问某个命名空间 - ClusterRole:此为集群层面的,针对所有的命名空间,
- Role:授权特定命名空间的访问权限,
- 主体(User,Group,ServiceAccount)
- User: 用户
- Group: 用户组
- ServicAccount:服务账号
- 角色绑定(RoleBinding,ClusterRoleBinding)
- RoleBinding:将角色绑定到主体,既subject ,它对应
Role
,创建Role
使用这个去绑定,绑定后才会有相对应的权限 - ClusterRoleBinding: 将集群角色绑定到主体,它对应
ClusterRole
,创建ClusterRole
使用这个去绑定,绑定后才会有相对应的权限
- RoleBinding:将角色绑定到主体,既subject ,它对应
RBAC授权普通用户访问命名空间
现在授权一个用户对某个命名空间有读取的权限,说白了就是只读,让你看看就行了,现在随便创建一个命名空间,然后在这个命名空间里启动几个pod
[root@k8s01 ~]# kubectl create namespace opesn
namespace/opesn created
[root@k8s01 ~]# kubectl get namespaces opesn
NAME STATUS AGE
opesn Active 8s
[root@k8s01 ~]# kubectl run nginx --image=nginx --replicas=3 --namespace=opesn
[root@k8s01 ~]# kubectl get pods -n opesn
NAME READY STATUS RESTARTS AGE
nginx-6db489d4b7-9xg6b 1/1 Running 0 26s
nginx-6db489d4b7-bfgwf 1/1 Running 0 26s
nginx-6db489d4b7-s6hrn 1/1 Running 0 26s
[root@k8s01 ~]#
现在新建一个用户opesn
,他的权限为只读opesn
命名空间,其他的全部拒绝,首先要创建一个Role
角色,需要定义规则了,基于官方文档
[root@k8s01 ~]# cat rbac-role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: opesn
name: opesn
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
权限为get,watch,list
,只读的,创建看一下
[root@k8s01 ~]# kubectl apply -f rbac-role.yaml
role.rbac.authorization.k8s.io/opesn created
[root@k8s01 ~]# kubectl get role -n opesn
NAME AGE
opesn 12s
[root@k8s01 ~]#
现在有一个角色了,现在把他绑定到这个角色里。
[root@k8s01 ~]# cat rbac-rolebinding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: opesn
subjects:
- kind: User
name: opesn # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role #this must be Role or ClusterRole
name: opesn # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
[root@k8s01 ~]#
主要两块,命名空间,subjects
的kind
指定为User
,不是程序,用户名是opesn
,在下面就是角色绑定了,指定了类型和名称,也就是上面创建的那个,这就可以了,创建吧。
[root@k8s01 ~]# kubectl create -f rbac-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/read-pods created
[root@k8s01 ~]# kubectl get role,rolebinding -n opesn
NAME AGE
role.rbac.authorization.k8s.io/opesn 4m40s
NAME AGE
rolebinding.rbac.authorization.k8s.io/read-pods 10s
[root@k8s01 ~]#
现在是创建完了,现在还差识别身份,现在使用基于证书的来识别身份,直接上脚本吧。
[root@k8s01 kubernetes]# cat rabc-user.sh
cat > opesn-csr.json <<EOF
{
"CN": "opesn",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes opesn-csr.json | cfssljson -bare opesn
kubectl config set-cluster kubernetes \
--certificate-authority=ca.pem \
--embed-certs=true \
--server=https://192.168.10.4:6443 \
--kubeconfig=opesn-kubeconfig
kubectl config set-credentials opesn \
--client-key=opesn-key.pem \
--client-certificate=opesn.pem \
--embed-certs=true \
--kubeconfig=opesn-kubeconfig
kubectl config set-context default \
--cluster=kubernetes \
--user=opesn \
--kubeconfig=opesn-kubeconfig
kubectl config use-context default --kubeconfig=opesn-kubeconfig
opesn
就是我之前定义的用户名,apiserver
我写的是负载均衡地址,会用到apiserver
的根证书,所以要复制过来,下面执行一下吧。
[root@k8s01 kubernetes]# bash rabc-user.sh
2020/06/07 23:14:29 [INFO] generate received request
2020/06/07 23:14:29 [INFO] received CSR
2020/06/07 23:14:29 [INFO] generating key: rsa-2048
2020/06/07 23:14:29 [INFO] encoded CSR
2020/06/07 23:14:29 [INFO] signed certificate with serial number 147728077879656546154832435011075986225633644035
2020/06/07 23:14:29 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
Cluster "kubernetes" set.
User "opesn" set.
Context "default" created.
Switched to context "default".
[root@k8s01 kubernetes]#
[root@k8s01 kubernetes]# kubectl --kubeconfig=opesn-kubeconfig get pod -n default
Error from server (Forbidden): pods is forbidden: User "opesn" cannot list resource "pods" in API group "" in the namespace "default"
[root@k8s01 kubernetes]# kubectl --kubeconfig=opesn-kubeconfig get pod -n opesn
NAME READY STATUS RESTARTS AGE
nginx-6db489d4b7-9xg6b 1/1 Running 0 16m
nginx-6db489d4b7-bfgwf 1/1 Running 0 16m
nginx-6db489d4b7-s6hrn 1/1 Running 0 16m
[root@k8s01 kubernetes]# kubectl --kubeconfig=opesn-kubeconfig get service
Error from server (Forbidden): services is forbidden: User "opesn" cannot list resource "services" in API group "" in the namespace "default"
[root@k8s01 kubernetes]#
只能看到opesn
命名空间下的pod
,什么service
之类的都不行,因为没有授权,
RBAC授权ServiceAccount
访问命名空间
创建一个WEBUI
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
vi recommended.yaml
…
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30001
selector:
k8s-app: kubernetes-dashboard
…
# kubectl apply -f recommended.yaml
#创建service account并绑定默认cluster-admin管理员集群角色:
cat dashboard-adminuser.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
#启动
apply -f dashboard-adminuser.yaml
#获取token
kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')
访问地址:http://NodeIP:30001
使用输出的token登录Dashboard。
现在创建一个ServiceAccount
,只允许访问opesn
命名空间,权限和上面的一样,首先要创建一个ServiceAccount
,然后把这个ServiceAccount
绑定到上面创建的角色里。
[root@k8s01 ~]# cat sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
namespace: opesn
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: sa-read-pods
namespace: opesn
subjects:
- kind: ServiceAccount
name: pod-reader
roleRef:
kind: Role
name: opesn
apiGroup: rbac.authorization.k8s.io
[root@k8s01 ~]#
就是这样,然后创建
[root@k8s01 ~]# kubectl create -f sa.yaml
serviceaccount/pod-reader created
rolebinding.rbac.authorization.k8s.io/sa-read-pods created
[root@k8s01 ~]# kubectl get secrets -n opesn pod-reader-token-fv9qg
NAME TYPE DATA AGE
pod-reader-token-fv9qg kubernetes.io/service-account-token 3 38s
[root@k8s01 ~]# kubectl describe secrets -n opesn pod-reader-token-fv9qg
Name: pod-reader-token-fv9qg
Namespace: opesn
Labels: <none>
Annotations: kubernetes.io/service-account.name: pod-reader
kubernetes.io/service-account.uid: 009f347c-be63-40cf-97c2-b4631f5e5b4c
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1359 bytes
namespace: 5 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlNqcHV5S0V5dnZEWDJtck9TSGdZYkppb0V5SFdyOXpVUnd5b3NHX1Zmd00ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJvcGVzbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJwb2QtcmVhZGVyLXRva2VuLWZ2OXFnIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InBvZC1yZWFkZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIwMDlmMzQ3Yy1iZTYzLTQwY2YtOTdjMi1iNDYzMWY1ZTViNGMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6b3Blc246cG9kLXJlYWRlciJ9.j8ZryrAGx4c2VAiW2ZR_McJCDSUDYKXpDf5hwkLxOzYAt5_gIzvJCWE3htCd3IGiALanK88iDCid3nwRzyNPXSEcMtTGeLtAdIJ9ZwtFfxZVTNuHhW0p4C1Gw-9PWTNvbZiLQv4IUKzJRlSvfn121lo7OSG5WdovmCkmGug_c8es4FJvOHPVtQBXvOTWihlvwZMO6NRLQ9mTtBaqbpXseBUUjfENk7tuRs-LsS31bqlmCH2DkcPGlLArpMBp0bnjtAH0OAfYJbfErTfeXyNjHDVSx1tOJV7yly4nvKwe5_PFYlKBKZ-FDMT6T0qTmitDvYjB6cml9AdzgukJjHJ9AQ
下面去用这个token
登陆一下UI
命名空间需要你手动输入了,然后会发现各种权限不足,就是这种效果,只读opesn
命名空间,其他的全部权限不足,这就是RBAC
授权