认证、授权、准入控制
基础知识
1)在K8s上有两类客户端需要访问API Server:
- kubectl
- 我们经常在Master上使用kubectl来操作k8s,很容易以为,kubectl是只能在Master上运行的,其实不是,这样做仅仅是为了方便访问,才将kubectl安装在Master上,其实你可以在Windows上安装一个kubectl,然后将~/.kube/config 这个记录了认证信息的文件拷贝过去,就可以在Windows上使用了。因此它其实是个远程客户端。
- Pod (如: coreDNS, Dashboard)
- 像kube-dns 它本身也是需要访问API Server的,因为当集群中新增资源对象时,它需要动态的生成一条该资源对象的A记录,所以它需要访问API Server来获取这个对象的信息,Dashboard也需要,因为它提供了一个Web界面,可让我们通过图像界面来管理操作k8s,如创建,删除Pod,创建删除名称空间等,所以它也需要访问API Server。
2)用户通过URL操作k8s
- 在k8s中发起API请求,其内部实际是一个URL请求路径:
格式: http://API_Server_IP:6443/apis/apps/v1/namespaces/default/deployments/myapp-deploy/
(1)一个普通的HTTP请求有:
get
post
put
delete
(2)在K8s中实际用的还是基本的请求方法,但K8s将这些基本方法,又做了更细致的封装,于是就有以下API 请求命令:
get
list
create
update
patch
watch
proxy
redirect(重定向)
delete
deletecollection(级联删除)
(3)一个API请求的URL拆解开后会包含以下信息,当然这些信息是不包含认证,仅是资源:
API Group
Namespace
Resource
Subresource
- 在k8s中访问资源,其实就是对URL发起增删改查的操作
验证方式:
1. 在安装了kubectl的节点上,启动kubectl的代理. 【注: kubectl所在节点必须有认证配置信息,即 .kube/config】
kubectl proxy --port=8888
2. 接着就可以在本地使用HTTP访问8888来向运行在HTTPS协议上的API Server发起请求了
curl http://localhost:8888/....
#注意:
一定要先启动一个Proxy,因为,kubectl自身是有认证信息的,你每次执行kubectl命令时,
它都会读取 ~/.kube/config 文件中的认证信息,所以你不需要输入任何认证信息,其实背后,是自动做了认证数据传递的。
但你若直接使用curl 来请求APIServer,你就必须给 curl 制作一个API Server认可的认证信息,
否则,curl是获取不到任何信息的!所以为了简单演示期间,可以使用上面的命令,先启动一个kubectl代理,
然后,curl向它发起请求,这样curl就不需要提供任何认证信息,所有认证都将在kubectl proxy 和 API Server之间自动进行。
通常为了安全,通常仅将代理启动为监听在127.0.0.1上,然后在本地做 curl 请求。
kind(即:类型):
它有三种类型:【每种类型都有一个固定的JSON表达方式,配置清单使用yaml写,但在提交时,会被自动转换为JSON格式】
1. 对象类型,如: Pod, deployment, service,namespace等这些都称为对象,它们都是可在集群上创建出来的具体实体。
2. 列表类型,在RESTful风格下它被称为集合,在K8s上称为列表(list)
# curl http://localhost:8888/api/v1/namespaces #注意:namespaces其实就是一个集合,它会列出该对象集合中的所有子资源.
# curl http://localhost:8888/api/v1/namespaces/default
{
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {
"name": "default",
"uid": "ea69bbf9-4248-434f-b5e8-c738f9c3b437",
"resourceVersion": "1012706502",
"creationTimestamp": "2021-06-08T03:04:14Z",
"managedFields": [
{
"manager": "kube-apiserver",
"operation": "Update",
"apiVersion": "v1",
"time": "2021-06-08T03:04:14Z",
"fieldsType": "FieldsV1",
"fieldsV1": {"f:status":{"f:phase":{}}}
}
]
},
"spec": {
"finalizers": [
"kubernetes"
]
},
"status": {
"phase": "Active"
}
RESTful: REST 称为表针状态转移,通常用于承载对象数据状态的格式,也称为序列化的数据结构,一般用于流式化数据的格式有xml,yaml,json。
在K8s中,使用的都是Json来作为其输出输入数据的格式,即便我们编写的所有清单文件都是yaml格式,但kubectl在将清单信息提交给API Server 前还是会自动将其转换为Json格式再提交给API Server去处理。
curl http://localhost:8888/apis/apps/v1/namespaces/kube-system/deployments/coredns
注意:
以上两个URL,一个是起始于api ,一个是起始于apis
区别: api 它是一个特殊链接,只有在核心v1群组中的对象才能使用。
apis 它是一般API访问的入口固定格式名。
3)Pod 操作 k8s
kubectl get svc
kubectl describe svc kubernetes
可以看到API Server被映射为K8s上的Service了,Pod就是通过这个Service访问API Server的.
注:
- 由于API Server的访问必须要通过证书认证,它是双向认证,即客户端Pod要验证API Server的身份,API也要验证客户端的身份
- 所以对于Pod来说,它访问API Server时,使用的地址是Service的地址.假如是10.96.0.1,而真实API Server所在Node节点的IP为172.20.0.1。
- 这就导致我们在创建API Server的证书时,必须要能够实现,Pod获取API Server的证书,去验证里面的主机名时,解析到的IP中必须有10.96.0.1,这就意味着DNS上要有两条A记录,一条解析为10.96.0.1,一条解析为172.20.0.1,这样Pod验证API Server才能通过,而kubectl这种外部客户端访问验证API Server解析的地址中有172.20.0.1,也能验证API身份通过。
- 或者在证书中直接写两个IP地址,也是可以的。但是我们又该知道,Pod验证API Server是必须,但我们更需要的是API Server去验证客户端!
- Pod中的应用要访问API Server,它事先是不可能专门为API Server设计一个读取kubernetes认证信息的功能的, 就如: nginx 它是不可能直接去读取kubernetes的认证信息,然后去访问API Server的!
- 所以,访问API Server时的认证信息要由Pod本身来完成。
kubectl describe pods coredns -n kube-system
可看到它默认挂载了一个存储卷,类型是secret的.它其实就是Pod访问APIServer
时提供的认证信息,不过默认的token是普通用户,它仅能查看自己的相关信息.
kubectl get secret
使用它可看到,当前名称空间的默认token,即 default-token-…
它就是当名称空间中所有Pod ,默认访问API Server时,提供的认证信息,当然你可加“ -n 名称空间名” 来查看其它名称空间中的默认token,而且其中一定有一个default-token-xxx..的标准k8s资源。
4)k8s 的权限系统
- k8s有完善的准入机制,分为 认证,授权,访问控制 三部分,当用户访问k8s集群时,请求将会发送至集群唯一入口apiserver,进入后需要经历如下认证过程:认证 -> 授权 -> 准入控制
1.认证(Authenticating)
对客户端的认证,通俗点就是用户名密码验证,判断用户是否为集群用户。
2.授权(Authorization)
用户认证通过后,要查看该用户操作资源的权限,若其要操作的资源在其允许操作的资源范围内则通过。
3.准入控制(Admission Control)
用户想要操作的资源,也许需要级联其它相关资源或级联了其它相关操作,那么这些级联的资源或级联的操作,该用户是否有权限访问?这个就是由准入控制来检查的,若不允许访问级联资源,那么该资源也将无法访问。
- k8s的整体架构也是一个微服务的架构,所有的请求都是通过kube-apiserver这个组件(对外提供REST服务),k8s中客户端有两类,一种是用户(user account),一种是集群内的Pod(service account),这两种客户端的认证机制略有不同,但无论是哪一种,都需要依次经过认证,授权,准入这三个机制。
- k8s是高度模块化设计,因此,这三种检查都允许用户自定义使用何种检测机制(插件)来进行访问控制。
一、认证
1)认证支持多种插件
- 1.令牌(token)认证:
- 双方有一个共享密钥,服务器上先创建一个密码,存下来,客户端登陆的时候拿这个密码登陆即可,这个就是对称密钥认证方式;k8s提供了一个restful风格的接口,它的所有服务都是通过http协议提供的,因此认证信息只能经由http协议的认证首部进行传递,这种认证首部进行传递通常叫做令牌;
- 2.ssl(证书)认证:
- 对于k8s访问来讲,ssl认证能让客户端确认服务器的认证身份,我们在跟服务器通信的时候,需要服务器发过来一个证书,我们需要确认这个证书是不是ca签署的,如果是我们认可的ca签署的,里面的信息与我们访问的目标主机信息保持一致,没有问题,那么我们就认为服务器的身份得到认证了,k8s中最重要的是服务器还需要认证客户端的信息,kubectl也应该有一个证书,这个证书也是server所认可的ca签署的证书,双方需要互相认证,实现加密通信,这就是ssl认证
2)kubernetes上的账号
- kubectl explain pods.spec 可以看到有一个字段 serviceAccountName(服务账号名称),这个就是我们 pod 连接 apiserver 时使用的账号
因此整个kubernetes集群中的账号有两类: - Service Account(服务账号) - 为集群内部Pod里面的进程调用Kubernetes API或其他外部服务而设计的,是kubernetes中的一种资源。 - User Account(用户账号) - 用户(人)拥有的账号,使用客户端(kubectl)对apiserver发起请求,apiserver要识别这个客户端是否有请求的权限,那么不同的用户就会有不同的权限,靠用户账号(UserName)表示。
1.ServiceAccount
-
Service Account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。它与User account不同,User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计;User account是跨namespace的,而service account则是仅局限它所在的namespace;每个namespace都会自动创建一个default service account用于pod和apiserver之间进行通信。
-
开启ServiceAccount Admission Controller后
- 1.Pod创建时如果没有指定Service Account,在创建后都会自动设置spec.serviceAccountName为default,使用当前namespace的默认Service Account
- 2.如果Pod引用的Service Account不存在,将拒绝创建pod
- 3.如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
# 例如:
kubectl get pods nginx-test-76bdc48865-9nqxn -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
cni.projectcalico.org/podIP: 10.244.84.163/32
cni.projectcalico.org/podIPs: 10.244.84.163/32
labels:
app: nginx-test
spec:
containers:
- image: nginx:1.8.1
imagePullPolicy: Always
name: nginx
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-lbdxk
readOnly: true
volumes:
- name: default-token-lbdxk
secret:
defaultMode: 420
secretName: default-token-lbdxk
kubectl get sa
NAME SECRETS AGE
default 1 36d
mongodb-kubernetes-operator 1 10d
nfs-provisioner 1 26d
kubectl get sa default -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2021-02-19T16:11:36Z"
name: default
namespace: default
resourceVersion: "379"
selfLink: /api/v1/namespaces/default/serviceaccounts/default
uid: 4d3e362a-78ff-4aaf-b980-a7d3651b8c0b
secrets:
- name: default-token-lbdxk
kubectl get sa -A | grep default
default default 1 36d
jenkins-k8s default 1 6d3h
kube-node-lease default 1 36d
kube-public default 1 36d
kube-system default 1 36d
mongodb default 1 10d
- 从上面可以看到:
- 1.每个Pod无论定义与否都会有个存储卷,这个存储卷为default-token-***的 token令牌,这就是pod和 认证信息。
- 2.通过secret进行定义,由于认证信息属于敏感信息,所以需要保存在secret资源当中,并以存储卷的方式挂载到Pod当中。从而让Pod内运行的应用通过对应的secret中的信息来连接apiserver,并完成认证。
- 3.每个 namespace 中都有一个默认的叫做 default 的 serviceaccount 资源。进行查看名称空间内的secret,也可以看到对应的default-token。让当前名称空间中所有的pod在连接apiserver时可以使用的预制认证信息,从而保证pod之间的通信。
2.UserAccount
kubeconfig文件 - 在K8S集群当中,每一个用户对资源的访问都是需要通过apiserver进行通信认证才能进行访问的,那么在此机制当中,对资源的访问可以是token,也可以是通过配置文件的方式进行保存和使用认证信息,可以通过kubectl config进行查看配置
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.16.1.80:6443 # apiserver的地址
name: kubernetes # 集群的名称
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes # 上下文的名称
current-context: kubernetes-admin@kubernetes #当前上下文的名称
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- 在上面的配置文件当中,定义了集群、上下文以及用户。
- 其中Config也是K8S的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个
- 用户列表也可以有多个,指明集群中的用户
- 而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么
二、授权
Kubernetes的授权是基于插件形式的,其常用的授权插件有以下几种: - Node(节点认证) - ABAC(基于属性的访问控制) - RBAC(基于角色的访问控制) - Webhook(基于http回调机制的访问控制)
cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.0.0.81:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --feature-gates=RemoveSelfLink=false
- --advertise-address=10.0.0.81
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
...
1)Node Authorization(默认开启)
1.7+版本才release的一种授权机制 - 通过配合NodeRestriction control准入控制插件来限制kubelet访问node,endpoint、pod、service以及secret、configmap、PV和PVC等相关的资源。 - 配置方式为: - –authorization-mode=Node,RBAC –admission-control=…,NodeRestriction,…
2)ABAC Authorization
ABAC(Attribute-Based Access Control) - 配置方式为: - –authorization-mode=ABAC –authorization-policy-file=SOME_FILENAME - 这种模式的实现相对比较生硬,就是在master node保存一份policy文件,指定不用用户(或用户组)对不同资源的访问权限,当修改该文件后,需要重启apiserver,跟openstack 的ABAC类似。policy文件的格式如下:
# Alice can do anything to all resources:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "alice",
"namespace": "*",
"resource": "*",
"apiGroup": "*"
}
}
# Kubelet can read any pods:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "kubelet",
"namespace": "*",
"resource": "pods",
"readonly": true
}
}
# Kubelet can read and write events:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "kubelet",
"namespace": "*",
"resource": "events"
}
}
3)RBAC(默认开启)
- NameSpace级别:
- 1.Role 创建一个角色,拥有一些权限
- 2.RoleBinding 用户绑定角色获得角色的权限
- Cluster级别:
- 3.ClusterRole 创建一个集群角色,拥有一些权限
- 4.ClusterRoleBinding 用户绑定集群角色获得集群角色的权限
在k8s中采用RBAC的方式进行授权,其工作逻辑是: - 把对对象的操作权限定义到一个角色当中,再将用户绑定到该角色,从而使用户得到对应角色的权限。
- 此时有三种情况:
- 1.User通过RoleBinding绑定Role,用户获得Role定义的对当前名称空间下资源的权限。
- 2.User通过ClusterRoleBinding绑定ClusterRole,用户获得ClusterRole定义的对集群中所有名称空间下资源的权限。
- 3.User通过RoleBinding绑定ClusterRole,用户获得ClusterRole定义的对当前名称空间下资源的权限。
cat mongo/rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: mongodb-kubernetes-operator
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: mongodb-kubernetes-operator
rules:
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- create
- update
- apiGroups:
- ""
resources:
- secrets
- configmaps
verbs:
- get
- list
- create
- update
- delete
- watch
- apiGroups:
- apps
resources:
- statefulsets
verbs:
- create
- get
- list
- watch
- delete
- update
- apiGroups:
- apps
resources:
- deployments
verbs:
- create
- get
- list
- watch
- delete
- update
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- apiGroups:
- mongodbcommunity.mongodb.com
resources:
- mongodbcommunity
verbs:
- create
- get
- list
- watch
- delete
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: mongodb-kubernetes-operator
subjects:
- kind: ServiceAccount
name: mongodb-kubernetes-operator
namespace: default
roleRef:
kind: ClusterRole
name: mongodb-kubernetes-operator
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-mongodb-kubernetes-operator
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-mongodb-kubernetes-operator
subjects:
- kind: ServiceAccount
name: mongodb-kubernetes-operator
namespace: default
roleRef:
kind: Role
name: leader-mongodb-kubernetes-operator
apiGroup: rbac.authorization.k8s.io
4)Webhook Authorization
- 用户在外部提供 HTTPS 授权服务,然后配置 apiserver 调用该服务去进行授权。
- apiserver配置参数:
- –authorization-webhook-config-file=SOME_FILENAME
- 配置文件的格式跟kubeconfig的格式类似,具体参考官方文档
三、准入控制
- Kubernetes的Admission Control实际上是一个准入控制器(Admission Controller)插件列表,发送到APIServer的请求都需要经过这个列表中的每个准入控制器插件的检查,如果某一个控制器插件准入失败,就准入失败。
控制器插件如下:
- AlwaysAdmit:允许所有请求通过
- AlwaysPullImages:在启动容器之前总是去下载镜像,相当于每当容器启动前做一次用于是否有权使用该容器镜像的检查
- AlwaysDeny:禁止所有请求通过,用于测试
- DenyEscalatingExec:拒绝exec和attach命令到有升级特权的Pod的终端用户访问。如果集中包含升级特权的容器,而要限制终端用户在这些容器中执行命令的能力,推荐使用此插件
- ImagePolicyWebhook
- ServiceAccount:这个插件实现了serviceAccounts等等自动化,如果使用ServiceAccount对象,强烈推荐使用这个插件
- SecurityContextDeny:将Pod定义中定义了的SecurityContext选项全部失效。SecurityContext包含在容器中定义了操作系统级别的安全选型如fsGroup,selinux等选项
- ResourceQuota:用于namespace上的配额管理,它会观察进入的请求,确保在namespace上的配额不超标。推荐将这个插件放到准入控制器列表的最后一个。ResourceQuota准入控制器既可以限制某个namespace中创建资源的数量,又可以限制某个namespace中被Pod请求的资源总量。ResourceQuota准入控制器和ResourceQuota资源对象一起可以实现资源配额管理。
- LimitRanger:用于Pod和容器上的配额管理,它会观察进入的请求,确保Pod和容器上的配额不会超标。准入控制器LimitRanger和资源对象LimitRange一起实现资源限制管理
- NamespaceLifecycle:当一个请求是在一个不存在的namespace下创建资源对象时,该请求会被拒绝。当删除一个namespace时,将会删除该namespace下的所有资源对象
- DefaultStorageClass
- DefaultTolerationSeconds
- PodSecurityPolicy