跳转至

认证、授权、准入控制

基础知识

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