少年恃险若平地,独倚长剑凌清秋。这篇文章主要讲述K8S 安全与RBAC相关的知识,希望能为你提供帮助。
一、简介
K8S作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server是集群内部各个组件通信的中介,也是外部控制的入口。所以K8S的安全机制基本就是围绕保护APIServer来设计的。K8S使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Crontrol)三步来保证API Server的安全。 |
1.1.1、认证的方法有:
1. HTTP Token 认证:通过一个 Token 来识别合法用户:HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token。
2. HTTP Base 认证:通过 用户名+密码 的方式认证。用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 Heather;Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码
3. 最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式
1.1.2、认证包括两种类型
? Kubenetes组件对API Server的访问:kubectl、Controller
Manager、Scheduler、kubelet、kubeproxy
1. Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用API Server的非安全端口访问, --insecure-bind-address=127.0.0.1
2. kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证
3. 实现方式:kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群context 信息(集群名称、用户名)。Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群
? Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)。
?Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。Kubenetes使用了Service Account解决Pod 访问API Server的认证问题
?Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-accounttoken,另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace
1. token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证
2. ca.crt,根证书。用于Client端验证API Server发送的证书
3. namespace, 标识这个service-account-token的作用域名空间
认证总体上如下图:
1.2、鉴权Authorization
鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “—authorization-mode” 设置)
? AlwaysDeny:表示拒绝所有的请求,一般用于测试
? AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
? ABAC(Attribute-Based Access
Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
? Webbook:通过调用外部 REST 服务对用户进行授权
? RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则。相对其它访问控制方式,拥有以下优势:
?对集群中的资源和非资源均拥有完整的覆盖
?整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作
?可以在运行时进行调整,无需重启 API Server
1.3、准入控制Admission Control
准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount。
列举几个插件的功能:
? NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除namespace 时,连带删除它的所有资源对象。
? LimitRanger:确保请求的资源不会超过资源所在
Namespace 的 LimitRange 的限制。
? ServiceAccount: 实现了自动化添加 ServiceAccount。
? ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。
二、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
? Token controller检测service account的创建,并为它们创建??secret??
? 开启ServiceAccount Admission Controller后
?每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
?验证Pod引用的service account已经存在,否则拒绝创建
?如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
?每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/
当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。
当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account。而pod和apiserver之间进行通信的账号,称为serviceAccountName。如下:
C# [root@k8s-master top]# kubectl get pods nginx-demo -o yaml |grepserviceAccountName serviceAccountName: default [root@k8s-master top]# kubectl describe pods nginx-demo |egrep "Mounts|from" Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7qkm9(ro) Type: Projected (a volume thatcontains injected data from multiple sources) |
Fortran [root@k8s-master top]# kubectl get secret NAME TYPE DATA AGE db-user-pass Opaque 2 6d23h default-token-kmq6s kubernetes.io/service-account-token 3 52d mysecret Opaque 1 6d22h [root@k8s-master top]# kubectl get sa NAME SECRETS AGE default 1 52d [root@k8s-master top]# kubectl get secret default-token-kmq6s NAME TYPE DATA AGE default-token-kmq6s kubernetes.io/service-account-token 3 52d |
从上可以得知,SA是会挂载到POD里面的,那具体是哪个位置呢?如下:
Haskell [root@k8s-master top]# kubectl exec -it nginx-demo -- bash root@nginx-demo:/# ls -l /run/secrets/kubernetes.io/serviceaccount/ total 0 lrwxrwxrwx 1 root root 13 May 11 11:34 ca.crt -> ..data/ca.crt lrwxrwxrwx 1 root root 16 May 11 11:34 namespace -> ..data/namespace lrwxrwxrwx 1 root root 12 May 11 11:34 token -> ..data/token root@nginx-demo:/# cat/run/secrets/kubernetes.io/serviceaccount/namespace |xargs default |
SA的创建方法非常简单,直接 kubectl create
serviceaccount admin 即可。我们在POD创建时可以使用spec.serviceAccountName字段中将name设置为您想要用的 service account 名字即可。在 pod 创建之初 service account 就必须已经存在,否则创建将被拒绝。需要注意的是不能更新已创建的 pod 的 service account。
YAML [root@k8s-master serviceaccount]# kubectl create serviceaccount admin serviceaccount/admin created [root@k8s-master serviceaccount]# kubectl get sa NAME SECRETS AGE admin 1 10s default 1 52d [root@k8s-master serviceaccount]# kubectl describe sa admin Name: admin Namespace: default Labels: < none> Annotations: < none> Image pull secrets: < none> Mountable secrets: admin-token-84mdr Tokens: admin-token-84mdr Events: < none> |
RBAC 引入了 4 个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作。
在RBAC API中,一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有”否定”的规则)。 角色可以由命名空间(namespace)内的Role对象定义,而整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
3.1、Role与ClusterRole
在RBAC API中,一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有”否定”的规则)。 角色可以由名字空间(namespace)内的Role对象定义,而整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
一个Role对象只能用于授予对某一单一名字空间中资源的访问权限。
以下示例描述了”default”名字空间中的一个Role对象的定义,用于授予对pod的读访问权限:
CSS [root@k8s-master rbac]# cat defatlt.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-reader rules: - apiGroups: [""] # "" indicates the core API group resources: ["pods"] verbs: ["get","watch", "list"] [root@k8s-master rbac]# kubectl apply -f defatlt.yaml role.rbac.authorization.k8s.io/pod-reader created [root@k8s-master rbac]# kubectl get role NAME CREATED AT pod-reader 2022-05-16T09:00:13Z prometheus-k8s 2022-04-21T10:10:06Z |
? 集群级别的资源控制,如node的访问权限
? 非资源型的访问权限,如endpoints “/healthz”
? 所有命名空间的资源控制。即控制所有的namespace
下面示例中的ClusterRole定义可用于授予用户对某一特定名字空间,或者所有名字空间中的secret的读访问权限:
CSS [root@k8s-master rbac]# cat clusterrole.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: # 鉴于ClusterRole是集群范围对象,所以这里不需要定义"namespace"字段 name: secret-reader rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get","watch", "list"] |
3.2、RoleBinding与ClusterRoleBinding
角色绑定将一个角色中定义的各种权限授予一个或者一组用户。角色绑定包含了一组相关主体(即subject, 包括用户——User、用户组——Group、或者服务账户——Service Account)以及对被授予角色的引用。 在名字空间中可以通过RoleBinding对象授予权限,而集群范围的权限授予则通过ClusterRoleBinding对象完成。
RoleBinding可以引用在同一名字空间内定义的Role对象。下面示例中定义的RoleBinding对象在”default”名字空间中将”pod-reader”角色授予用户”jane”。 这一授权将允许用户”jane”从”default”名字空间中读取pod。
YAML [root@k8s-master rbac]# cat rolebinding.yaml kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: read-pods namespace: default subjects: - kind: User name: jane apiGroup:rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup:rbac.authorization.k8s.io |
YAML ## 以下角色绑定允许用户"dave"读取"development"名字空间中的secret。 kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: read-secrets namespace: development # 这里表明仅授权读取"development"名字空间中的资源。 subjects: - kind: User name: dave apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: secret-reader apiGroup:rbac.authorization.k8s.io |
3.3、Subject角色绑定主体
RoleBinding或者ClusterRoleBinding将角色绑定到角色绑定主体(Subject)。 角色绑定主体可以是用户组(Group)、用户(User)或者服务账户(Service Accounts)。
? 用户名由字符串表示。可以是纯粹的用户名,例如”alice”、电子邮件风格的名字,如 “bob@example.com” 或者是用字符串表示的数字id。对于用户名,RBAC授权系统不要求任何特定的格式。然而,前缀system:是 为Kubernetes系统使用而保留的,所以管理员应该确保用户名不会意外地包含这个前缀。
? Kubernetes中的用户组信息由授权模块提供。用户组与用户一样由字符串表示。Kubernetes对用户组 字符串没有格式要求,但前缀system:同样是被系统保留的。
? Service Accounts拥有包含 system:serviceaccount:前缀的用户名,并属于拥有system:serviceaccounts:前缀的用户组。
一个名为”alice@example.com”的用户:
YAML subjects: - kind: User name:"alice@example.com" apiGroup:rbac.authorization.k8s.io |
大多数资源由代表其名字的字符串表示,例如”pods”,就像它们出现在相关API endpoint的URL中一样。然而,有一些Kubernetes API还 包含了”子资源”,比如pod的logs。在Kubernetes中,pod logs endpoint的URL格式为:
Go GET /api/v1/namespaces/namespace/pods/name/log |
CSS kind: Role apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: namespace: default name: pod-and-pod-logs-reader rules: - apiGroups: [""] resources: ["pods","pods/log"] verbs: ["get","list"] |
? apiGroups定义可以使用api的版本,为空表示核心版本,如 ["extensions", "apps"]允许读写在”extensions”和”apps”的API。
? resources表示对哪些资源进行控制,可以为pods、deployments、nodes、secrets。。。。
? verbs表示允许的动作。如"get",
"list", "watch", "create", "update",
"patch", "delete"等。
? resourceNames表示一个资源的实例名,如一个pod的名字为abc,那加上这个,那这个rule就表示只对这个pod生效。
四、实例
4.1、普通用户管理集群
在实际生产环境中,有很可能是多个部门共用同一套K8S的环境,这就需要做权限的隔离,以下以devuser为例,创建一个普通用户只能管理namespace=dev这个环境
PowerShell # 下载证书生成工具 wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 mv cfssl_linux-amd64 /usr/local/bin/cfssl wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 mv cfssljson_linux-amd64 /usr/local/bin/cfssljson wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo cat > /root/devuser-csr.json < < EOF "CN":"devuser", "hosts": [], "key": "algo":"rsa", "size": 2048 , "names": [ "C": "CN", "ST":"BeiJing", "L":"BeiJing", "O":"k8s", "OU":"System" ] EOF cd /etc/kubernetes/pki/ cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes/root/devuser-csr.json | cfssljson -bare devuser # 设置集群参数 export KUBE_APISERVER="https://192.168.4.169:6443" kubectl config set-cluster kubernetes--certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true--server=$KUBE_APISERVER --kubeconfig=devuser.kubeconfig # 设置客户端认证参数 kubectl config set-credentials devuser--client-certificate=/etc/kubernetes/pki/devuser.pem--client-key=/etc/kubernetes/pki/devuser-key.pem --embed-certs=true--kubeconfig=devuser.kubeconfig # 设置上下文参数 kubectl create namespace dev kubectl config set-context kubernetes --cluster=kubernetes --user=devuser--namespace=dev --kubeconfig=devuser.kubeconfig # 设置默认上下文 kubectl create rolebinding devuser-admin-binding --clusterrole=admin--user=devuser --namespace=dev # 创建用户 useradd devuser cp -f ./devuser.kubeconfig /home/devuser/.kube/config chown devuser:devuser /home/devuser/.kube/config #使用devuser用户登陆系统 kubectl config use-context kubernetes |
YAML [root@k8s-master rbac]# kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data:DATA+OMITTED server:https://172.16.4.169:6443 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 |
4.2.1、创建用户凭证
C# # 给用户 fdm 创建一个私钥 [root@master user_login_kube-systecm]# openssl genrsa -out fdm.key 2048 Generating RSA private key, 2048 bit long modulus ...........................................................................................................................................................................+++ .........................................................................................................................................................+++ e is 65537 (0x10001) [root@master user_login_kube-systecm]# ll total 4 -rw-r--r-- 1 root root 1675 Feb 29 23:13 fdm.key # 使用我们刚刚创建的私钥创建一个证书签名请求文件fdm.csr。 # -subj参数中指定用户名和组,CN表示用户名,O表示组 [root@master user_login_kube-systecm]# openssl req -new -key fdm.key -outfdm.csr -subj "/CN=fdm/O=wangsu" [root@master user_login_kube-systecm]# ll total 8 -rw-r--r-- 1 root root 907 Feb 2923:15 fdm.csr -rw-r--r-- 1 root root 1675 Feb 29 23:13 fdm.key # 使用k8s的CA证书,生成最终的证书文件fdm.crt,我们这里设置证书的有效期为3650天 [root@master user_login_kube-systecm]# openssl x509 -req -in fdm.csr -CA/etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial-out fdm.crt -days 3650 Signature ok subject=/CN=fdm/O=wangsu Getting CA Private Key [root@master user_login_kube-systecm]# ll total 12 -rw-r--r-- 1 root root 993 Feb 2923:16 fdm.crt -rw-r--r-- 1 root root 907 Feb 2923:15 fdm.csr -rw-r--r-- 1 root root 1675 Feb 29 23:13 fdm.key # 查看证书的有效期 [root@master user_login_kube-systecm]# openssl x509 -in fdm.crt -text|grep Not Not Before: Feb 2915:16:49 2020 GMT Not After : Feb 2615:16:49 2030 GMT |
CoffeeScript [root@master user_login_kube-systecm]# kubectl config set-credentials fdm--client-certificate=fdm.crt --client-key=fdm.key User "fdm" set. # 创建context [root@master user_login_kube-systecm]# kubectl config set-contextfdm-context --cluster=kubernetes --namespace kube-system --user=fdm Context "fdm-context" created. # 测试报错,因为没有为该用户定义任何操作的权限 [root@master user_login_kube-systecm]# kubectl get pods--context=fdm-context Error from server (Forbidden): pods is forbidden: User "fdm"cannot list resource "pods" in API group "" in thenamespace "kube-system" |
【K8S 安全与RBAC】用户创建完成后,接下来就需要给该用户添加操作权限,我们来定义一个YAML文件,创建一个
推荐阅读
- 自建Gitlab迁移工具使用指南
- Appium - DesiredCapabilities对象的参数配置及含义
- springboot项目里,让tk-mybatis支持可以手写sql的mapper.xml文件
- 速云达ERP手机App开始内测,完全兼容速达5000以上的任何版本
- apply() 函数家族介绍
- k8s实践(Helm and Kubeapps UI)
- app爬虫--mitmproxy用法梳理
- [开源]如何使用goapp写你的后台管理系统_golang
- 分析Android APK-砸壳-Fdex2