k8s安全认证-service account




 

1 k8s的认证:

API Server作为Kubernetes网关,是访问和管理资源对象的唯一入口,其各种集群组件访问资源都需要经过网关才能进行正常访问和管理。每一次的访问请求都需要进行合法性的检验,其中包括身份验证、操作权限验证以及操作规范验证等,需要通过一系列验证通过之后才能访问或者存储数据到etcd当中

 

与API server通信的客户端大致有两类:
  1.集群客户端工具(kubectl、kubeadm、kubelet等)
  2.集群内pod.

1.1  客户端访问k8sAPI时的过程:

第一步:对客户端访问进行认证操作,确认是否具有访问k8s权限

token(共享秘钥)

SSL(双向SSL认证)

....通过任何一个认证即表示认证通过,进入下一步

第二步:授权检查,确认是否对资源具有相关的权限

ABAC(基于属性的访问控制)

 RBAC(基于角色的访问控制)

NODE(基于节点的访问控制)

WEB HOOK(自定义HTTP回调方法的访问控制)

第三步:准入控制(对操作资源相关联的其他资源是否有权限操作)

 

1.2 Kubernetes只对以下的API请求属性进行检查:

user - username,uid

group - user group

"extra"- 额外信息

API - API资源的对象

Request path - 请求资源的路径(k8s使用resultful风格接口的API)

 http://Node_IPaddr:6443/apis/apps/v1/namespaces/namespaces_name/resource_name/

HTTP 请求动作 - HTTP verbs get,post,put,和delete用于非资源请求

HTTP 请求动作映射到 API资源操作-  get,list,create,update,patch,watch,proxy,redirect,delete,和deletecollection用于请求resource

Resource -被访问(仅用于resource 请求)的resource 的ID或名字- *对于使用resource 的请求get,update,patch,和delete,必须提供resource 名称。

Subresource - 正在访问的subresource (仅用于请求resource )

Namespace - 正在访问对象的命名空间(仅针对命名空间的请求资源)

API group - 正在访问的API组(仅用于请求资源)。空字符串指定核心API组。

 

 

1.3 什么是service account

Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务而设计的。它与User account不同

  1. User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API而设计;
  2. User account是跨namespace的,而service account则是仅局限它所在的namespace;
  3. 每个namespace都会自动创建一个default service account
  4. Token controller检测service account的创建,并为它们创建secret
  5. 开启ServiceAccount Admission Controller后
  1. 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
  2. 验证Pod引用的service account已经存在,否则拒绝创建
  3. 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
  4. 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/

1.4  验证service account

 当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。如下:

 

[root@k8s-master ~]# kubectl get pod
NAME                         READY   STATUS    RESTARTS   AGE
myapp-demo-b9997455b-5mxjm   1/1     Running   0          2d17h
[root@k8s-master ~]# kubectl get pod -o yaml| grep serviceAccount
    serviceAccount: default
    serviceAccountName: default
[root@k8s-master ~]# kubectl describe pods myapp-demo-b9997455b-5mxjm
……
Volumes:
  mysql:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  test
    Optional:    false
  default-token-wldf4:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-wldf4
Optional:    false
……..

 

从上面可以看到每个Pod无论定义与否都会有个存储卷,这个存储卷为default-token-*** token令牌,这就是pod和serviceaccount认证信息。通过secret进行定义,由于认证信息属于敏感信息,所以需要保存在secret资源当中,并以存储卷的方式挂载到Pod当中。从而让Pod内运行的应用通过对应的secret中的信息来连接apiserver,并完成认证。每个 namespace 中都有一个默认的叫做 default 的 service account 资源。进行查看名称空间内的secret,也可以看到对应的default-token。让当前名称空间中所有的pod在连接apiserver时可以使用的预制认证信息,从而保证pod之间的通信。

[root@k8s-master ~]# kubectl get secrets | grep service-account-token
default-token-wldf4                  kubernetes.io/service-account-token   3      11d
nfs-client-provisioner-token-mz6dc   kubernetes.io/service-account-token   3      3d20h
[root@k8s-master ~]#

[root@k8s-master ~]# kubectl get sa
NAME      SECRETS   AGE
default   1         50d
[root@k8s-master ~]# kubectl get sa -n ingress-nginx  #前期创建的ingress-nginx名称空间也存在这样的serviceaccount
NAME                           SECRETS   AGE
default                        1         11d
nginx-ingress-serviceaccount   1         11d
[root@k8s-master ~]# kubectl get secret
NAME                    TYPE                                  DATA      AGE
default-token-j5pf5     kubernetes.io/service-account-token   3         50d
mysecret                Opaque                                2         1d
tomcat-ingress-secret   kubernetes.io/tls                     2         10d
[root@k8s-master ~]# kubectl get secret -n ingress-nginx
NAME                                       TYPE                                  DATA      AGE
default-token-zl49j                        kubernetes.io/service-account-token   3         11d
nginx-ingress-serviceaccount-token-mcsf4   kubernetes.io/service-account-token   3         11d

 

 

2 service account实例

2.1  创建service account

[root@k8s-master ~]# kubectl create  serviceaccount admin
serviceaccount/admin created
[root@k8s-master ~]# kubectl get sa
NAME                     SECRETS   AGE
admin                    1         5s
default                  1         11d
nfs-client-provisioner   1         3d20h
[root@k8s-master ~]# kubectl describe sa admin #查看名称为admin的sa的信息,系统会自动创建一个token信息

Name:                admin
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   admin-token-bt2n9
Tokens:              admin-token-bt2n9
Events:              <none>
[root@k8s-master ~]# kubectl get secret #会自动创建一个secret(admin-token-rxtrc),用于当前sa连接至当前API server时使用的认证信息
NAME                                 TYPE                                  DATA   AGE
admin-token-bt2n9                    kubernetes.io/service-account-token   3      27s
db-user-pass                         Opaque                                2      2d19h
default-token-wldf4                  kubernetes.io/service-account-token   3      11d
myapp-ingress-secret                 kubernetes.io/tls                     2      6d19h
myregistrykey                        kubernetes.io/dockerconfigjson        1      2d19h
mysecret                             Opaque                                2      2d19h
nfs-client-provisioner-token-mz6dc   kubernetes.io/service-account-token   3      3d20h
nginx-ingress-secret                 kubernetes.io/tls                     2      6d19h
test                                 Opaque                                1      2d18h
tomcat-ingress-secret                kubernetes.io/tls                     2      6d20h

 

 

2.2   创建一个pod应用刚刚创建的sa

# cat myapp-deploy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sa-demo
  labels:
    app: myapp
    release: wx
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v2
    ports:
    - name: httpd
      containerPort: 80
  serviceAccountName: admin  #此处指令为指定sa的名称
[root@k8s-master myapp]# kubectl create  -f myapp-deploy.yaml
pod/sa-demo created
[root@k8s-master myapp]# kubectl  get pod
NAME      READY   STATUS    RESTARTS   AGE
sa-demo   1/1     Running   0          8s
[root@k8s-master myapp]# kubectl describe pod sa-demo
….
    Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from admin-token-bt2n9 (ro)
Volumes:
  admin-token-bt2n9:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  admin-token-bt2n9
    Optional:    false

 

 

3 Kubeconfig证书认证

集群交互的时候少不了的是身份认证,使用 kubeconfig(即证书) 和 token 两种认证方式是最简单也最通用的认证方式,下面我使用kubeconfing来进行认证

使用kubeconfig文件来组织关于集群,用户,名称空间和身份验证机制的信息。使用 kubectl命令行工具对kubeconfig文件来查找选择群集并与群集的API服务器进行通信所需的信息。

默认情况下 kubectl使用的配置文件名称是在$HOME/.kube目录下 config文件,可以通过设置环境变量KUBECONFIG或者--kubeconfig指定其他的配置文件

 

3.1 系统的kubeconfig

 

# kubectl config view 
apiVersion: v1
clusters:   #集群列表 
- cluster:
    certificate-authority-data: REDACTED  #认证集群的方式
    server: https://172.16.150.212:6443    #访问服务的APIserver的路径
  name: kubernetes #集群的名称
contexts: #上下文列表
- context:
    cluster: kubernetes  #访问kubernetes这个集群
    user: kubernetes-admin  #使用 kubernetes-admin账号
  name: kubernetes-admin@kubernetes #给定一个名称
current-context: kubernetes-admin@kubernetes #当前上下文,表示使用哪个账号访问哪个集群
kind: Config
preferences: {}
users:  #用户列表
- name: kubernetes-admin #用户名称
  user:
    client-certificate-data: REDACTED #客户端证书,用于与apiserver进行认证
    client-key-data: REDACTED #客户端私钥

 

 

在上面的配置文件当中,定义了集群、上下文以及用户。其中Config也是K8S的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个;用户列表也可以有多个,指明集群中的用户;而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么。如图:定义了用户kubernetes-admin可以对kubernetes该集群的访问,用户kubernetes-user1对Clluster1集群的访问

 

kubernetes X509 证书认证_Pod

 

 

 

3.2  自建证书和账号进行访问apiserver

#生成证书

[root@k8s-master pki]# (umask 077;openssl genrsa -out wx.key 2048)
Generating RSA private key, 2048 bit long modulus
..............................................................................................................................+++
.......................+++
e is 65537 (0x10001)
[root@k8s-master pki]#

 

 

#使用ca.crt进行签署

[root@k8s-master pki]# openssl req -new -key wx.key -out wx.csr -subj "/CN=wx"  #是后面我们创建的用户名称,需要保持一致
[root@k8s-master pki]# ls wx.*
wx.csr  wx.key
[root@k8s-master pki]# openssl x509 -req -in wx.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial -out wx.crt -days 365
Signature ok
subject=/CN=wx
Getting CA Private Key
[root@k8s-master pki]#

 

 

#创建一个当前集群用户

[root@k8s-master pki]# kubectl config set-credentials wx --client-certificate=./wx.crt --client-key=./wx.key --embed-certs=true
User "wx" set.

 

 

 

#为wx用户用户创建一个context

[root@k8s-master pki]# kubectl config set-context wx@kubernetes --cluster=kubernetes --user=wx
Context "wx@kubernetes" created.
[root@k8s-master pki]#
[root@k8s-master pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://10.6.76.25:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
- context:
    cluster: kubernetes
    user: wx
  name: wx@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
- name: wx
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

 

#切换serviceaccount

 

root@k8s-master pki]# kubectl config use-context wx@kubernetes
Switched to context "wx@kubernetes".
[root@k8s-master pki]#

[root@k8s-master pki]# kubectl get pod
Error from server (Forbidden): pods is forbidden: User "wx" cannot list resource "pods" in API group "" in the namespace "default"
[root@k8s-master pki]#

 

从上面的演示,当切换成wx用户进行访问集群时,由于该账户没有管理集群的权限,所以在获取pods资源信息时,会提示Forrbidde这就需要授权了,例如RBAC授权