一、访问方式
由于API-Server 是 Kubernetes 集群数据的唯一访问入口,任一 Kubernetes API 的访问都属于以下三种方式之一:
-
以证书方式访问的普通用户或进程,包括运维人员及 kubectl、 kubelet 等进程
-
以 Service Account 方式访问的 Kubernetes 的内部服务进程
-
以匿名方式访问的进程
两种用户账号
-
集群内部的 Service-Account,但它并不是给 Kubernetes 集群的用户(系统管理员、 运维人员、租户用户等)用的,而是给运行在 Pod 里的进程用的,它为 Pod 里的进程提供了必要的身份证明
-
外部的用户账号 user-account,给用户使用,具有全局性
1. 证书访问
yaml
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTi***
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: LS0tLS1CRUdJ***
client-key-data: LS0tLS1CR***
2. ServiceAccount 访问
3. 匿名方式访问
bash
➜ ~ curl -k https://localhost:6443/api
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "forbidden: User "system:anonymous" cannot get path "/api"",
"reason": "Forbidden",
"details": {},
"code": 403
}
匿名用户 system:anonymous
sql
k get rolebindings.rbac.authorization.k8s.io -owide -A | grep -i system:anonymous
kube-public kubeadm:bootstrap-signer-clusterinfo Role/kubeadm:bootstrap-signer-clusterinfo 45d system:anonymous
k describe role kubeadm:bootstrap-signer-clusterinfo -n kube-public
Name: kubeadm:bootstrap-signer-clusterinfo
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
configmaps [] [cluster-info] [get]
匿名账号访问绑定的 role 只能查看 k8s 的 cluster-info
bash
curl -k https://localhost:6443/api/v1/namespaces/kube-public/configmaps/cluster-info
二、API-Server 架构分析
API Server 的架构从上到下可以分为以下几层:
1. Client
访问 api-server 的客户端主要分为两类
- kubectl:使用 kubeconfig 里的信息 (里面保存了客户端访问API-server的密钥相关信息) 来访问
- Pod: 使用 ServiceAccount 来访问
2. API层
k8s 所有的 API 参看 Github文档
主要以 REST 方式提供各种API接口,除了有 Kubernetes 资源对象的 CRUD 和 Watch 等主要 API,还有健康检查、UI、日志、性能指标等运维监控相关的API
一个 API 对象在 etcd 里面完整的资源路径是由 Group(API组),Version (API版本) 和 Resource (API资源类型) 三个部分组成
bash
/apis/batch/v1/jobs
Group: batch
Version: v1
Resouces: jobs
对于k8s里面的核心API对象,比如Pod、Node等,是不需要Group的(它们的Group是""); 对于这些核心的API对象,k8s会直接在 /api 这个层级下进行下一步的匹配过程
3. 访问控制层
当客户端访问API接口时,访问控制层负责对用户身份鉴权,验明用户身份,核准用户对 Kubernetes 资源对象的访问权限,然后根据配置的各种资源访问许可逻辑(Admission Control),判断是否允许访问
4. 注册表层
Kubernetes 把所有资源对象都保存在注册表(Registry)中,针对注册表中的各种资源对象都定义了:资源对象的类型、如何创建资源对象、如何转换资源的不同版本,以及如何将资源编码和解码为 JSON 或 ProtoBuf 格式进行存储
5. etcd数据库
用于持久化存储 Kubernetes 资源对象的KV数据库。etcd 的 watch API接口对于API Server来说至关重要,因为通过这个接口,API Server 创新性地设计了 List-Watch 这种高性能的资源对象实时同步机制,使 Kubernetes 可以管理超大规模的集群,及时响应和快速处理集群中的各种事件
6. List-Watch 机制
从本质上看,API Server与常见的MIS或ERP系统中的DAO模块类似,可以将主要处理逻辑视作对数据库表的CRUD操作
这里解读API Server中资源对象的 List-Watch 机制。以一个完整的Pod调度过程为例,对API-Server 的 List-Watch 机制进行说明
-
借助 etcd 提供的 Watch API 接口,API Server可以监听(Watch)在 etcd 上发生的数据操作事件,比如Pod创建事件、更新事件、删除事件等,在这些事件发生后,etcd 会及时通知 API-Server
-
当一个 ReplicaSet 对象被创建并被保存到 etcd 中后,etcd 会立即发送一个对应的 Create 事件给 API-Server(图中的箭头3)
-
为了让 Kubernetes 中的其他组件在不访问底层 etcd 数据库的情况下,也能及时获取资源对象的变化事件,API Server 模仿 etcd 的 Watch API 接口提供了自己的 Watch 接口,这样一来,这些组件就能近乎实时地获取它们感兴趣的任意资源对象的相关事件通知了,图中的 controller-manager 、scheduler、以及 kubelet 等组件之间的 watch 就表明了这个过程,在监听自己感兴趣的资源的时候,客户端可以增加过滤条件,比如 node1 节点的kubelet 进程只对自己节点上的 pod 事件感兴趣
三、认证(Authentication)
认证即是识别用户身份,认证的方式很多,比如 HTTP base,HTTP token,TLS,Service Account,OpenID Connect等
Kubernetes 集群中所有资源的访问和变更都是通过 Kubernetes API Server 的 REST API 实现的,所以集群安全的关键点就在于如何识别并认证客户端身份 (Authentication),以及随后访问权限的授权 (Authorization) 这两个关键问题
对于第一个认证的问题,Kubernetes 集群提供以下几种级别的客户端身份认证方式
-
最严格的 HTTPS 证书认证:基于CA根证书签名的双向数字证书认证方式
-
HTTP Token认证:通过一个Token 来识别合法用户
-
OpenID Connect Token 认证 第三方认证:通过第三方OIDC协议进行认证
-
Webhook Token 认证:通过外部 Webhook 服务进行认证
-
Authenticating Proxy:通过代理程序进行认证
1. 双向数字证书认证
单向数字证书验证,如HTTPS TLS 层握手,client 只校验 server 的证书
CA 签发证书的过程,如上图左边部分:
- 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值
- 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
- 最后将 Certificate Signature 添加在文件证书上,形成数字证书;
客户端校验服务端的数字证书的过程,如上图右边部分:
-
首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
-
通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
-
最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信
双向数字证书认证即是 client 校验 server 的证书,server 也校验 client 的证书
1. 使用证书 与 api-server 交互
yaml
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJT****
这个 kubernetes-admin 用户没有绑定任何的 role
css
➜ ~ k get clusterrolebindings.rbac.authorization.k8s.io -owide | grep -i kubernetes-admin
➜ ~
➜ ~ k get rolebindings.rbac.authorization.k8s.io -A -owide | grep -i kubernetes-admin
所以这个用户肯定是借用的所在的用户组 system:masters 的权限
ini
➜ ~ k get clusterrolebindings.rbac.authorization.k8s.io -owide | grep -i system:masters
cluster-admin ClusterRole/cluster-admin 47d system:masters
➜ ~ k describe clusterrole cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
[*] [] [*]
所以这个 kubernetes-admin 这个用户能够操作集群内的任何资源
2. 签发证书
签发客户端证书有两种方式,一种是基于CA根证书签发证书,另一个种是发起 CSR(Certificate Signing Requests)请求
1. 使用CA根证书签发客户端证书(用的不多)
sql
1.生成私钥
openssl genrsa -out cat.key 2048
2.是用于创建证书签名请求(Certificate Signing Request,CSR)
openssl req -new -key cat.key -out cat.csr -subj "/CN=cat"
3.基于CSR文件签发x509证书
openssl x509 -req -in cat.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out cat.crt -days 365
4.将客户端证书文件 cat.crt 和客户端密钥文件 cat.key 设置为名为 cat 的用户凭证,并将这些证书和密钥嵌入到 kubeconfig 文件中
kubectl config set-credentials cat --client-certificate=cat.crt --client-key=cat.key --embed-certs=true
kubectl config view
kubectl config set-context cat@kubernetes --cluster=kubernetes --user=cat
kubectl config use-context cat@kubernetes
配置 userAccount 访问权限
yaml
cat <<EOF | k apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-cat-rolebinding
subjects:
- kind: User
name: cat
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
EOF
上面方式的缺陷,我们需要在集群的master节点上进行配置,但是生产环境下一般情况下我们都没办法进入到集群的master节点机器
但是一般情况我们都能拿到对应的一个kubeconfig文件用来访问k8s集群,通过 kubeconfig 文件我们也可以进行证书的批准和授权
2. 通过 CSR 签发证书(推荐)
- 前面通过CA签发证书需要有CA的私钥,其实 Kubernetes 可以直接发起 CSR 请求
csharp
生成私钥和CSR文件
openssl genrsa -out cat.key 2048
- 生成一个CSR文件
vbnet
openssl req -new -key cat.key -out cat.csr
这里一定注意:生成的CSR文件里面的用户名一定要写你下面利用 kubeconfig 文件操作 k8s 集群的用户名
- 通过 kubectl 创建一个 CertificateSigningRequest 并将其提交到 Kubernetes 集群
yaml
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: cat
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0****
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400 # one day
usages:
- client auth
EOF
usage
字段必须是 'client auth
',代表密钥的用途expirationSeconds
证书过期时间(1.22版本之前没有这个字段,默认的过期时间是十年左右)
以下是我在 k8s 1.20 版本上申请的证书的默认生效时间范围
request
字段是 CSR 文件内容的 base64 编码值。 要得到该值,可以执行命令
cat cat.csr | base64 | tr -d "\n"
- 批准 CertificateSigningRequest
bash
kubectl certificate approve cat
注意:下面的 csr 代表的是 certificatesigningrequests(是k8s里面的一种资源),注意与上面的 certificate 区分开来
- 从 CertificateSigningRequest 导出颁发的证书
bash
kubectl get csr cat -o jsonpath='{.status.certificate}'| base64 -d > cat.crt
- 配置到对应的 kubeconfig 上下文中
matlab
将客户端证书文件 cat.crt 和客户端密钥文件 cat.key 设置为名为 cat 的用户凭证,并将这些证书和密钥嵌入到 kubeconfig 文件中
kubectl config set-credentials cat --client-certificate=cat.crt --client-key=cat.key --embed-certs=true
kubectl config view
kubectl config set-context cat@kubernetes --cluster=kubernetes --user=cat
- 配置 userAccount 访问权限
参看 Github文档 创建k8s的所有clusterrole的相关权限
yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-cat-rolebinding
subjects:
- kind: User
name: cat
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # 不一定要配置集群管理员权限
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
- 切换上下文为当前新创建的用户
perl
kubectl config use-context cat@kubernetes
2. HTTP Token认证
HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串一Token 来表明客户身份的一种方式。在通常情况下,Token 是一个很复杂的字符串,比如我们用私钥签名一个字符串后的数据就可以当作一个Token。此外,每个Token 对应一个用户名,存储在 API Server 能访问的一个文件中
当客户端发起API调用请求时 ,需要在 HTTP Header 里放入 Token , 这样 API-Server 就能识别合法用户和非法用户
1. 静态令牌(Token)
自己机器上的 kube-apiserver 的启动参数如下
/etc/kubernetes/manifests/kube-apiserver.yaml
kube-apiserver 启动参数参考官网
ini
- command:
- kube-apiserver
- --advertise-address=10.37.18.97
- --allow-privileged=true
- --authorization-mode=Node,RBAC 启动 RBAC 和 Node 授权模式
- --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
- --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=172.16.9.0/24
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --token-auth-file=/etc/kubernetes/pki/token.csv 启用HTTP-Token认证
-
client-ca-file 指定CA根证书文件,内置CA公钥用于验证某证书是否是CA签发的证书
-
tls-cert-file 指定Api-Server证书文件
-
tls-private-key-file 指定Api-Server私钥文件
生成静态 token,格式如下 token,user,uid,"group1,group2,group3"
bash
export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
cat > token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF
请求Api时只要在Authorization头中加入Bearer Token即可:
bash
curl -k --header "Authorization: Bearer d6fe3b32dac12ca0e55f8a0114eec2d0" https://localhost:6443/api
2. ServiceAccount Token
服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的持有者令牌来验证请求
每个 Service-Account 都对应一个 Secret 对象,在这个 Secret 对象中有一个加密的 secret 字段,这个 Token 字段就是 Bearer Token
这个 token 使用哪个公钥加密的,取决于 API-Server 的启动参数 --service-account-key-file 设置的文件
Service Account 的正常工作离不开以下三个控制器:
Service Account Controller、Token Controller、Admission Controller
Service Account Controller
Service Account Controller 的工作相对简单,它会监听 Service Account 和 Namespace 这两种资源对象的事件,如果在一个 Namespace 中没有默认的 Service Account,那么它会为该 Namespace 创建一个默认的 ServiceAccount 对象,这就是在每个 Namespace 下都有一个名为 default 的 Service Account 的原因
Token Controller
Token Controller 也监听 Service Account 的事件,如果发现在新建的 Service Account 里没有对应的 Service Account Secret,则会用 APIServer 私钥(--service-account-private-key-file 指定的文件)创建一个 Token,并用该 Token、api-server 的 CA 证书等三个信息产生一个新的 Secret 对象,然后放入刚才的 Service Account 中。如果监听到的事件是 Service Account 删除事件,则自动删除与该 Service Account 相关的所有 Secret。此外,Token Controller 对象也会同时监听 Secret 的创建和删除事件,确保与对应的 Service Account 的关联关系正确
Admission Controller
Admission Controller 会验证 Pod 里的 Service Account 是否合法,当我们在 api-server 的准入控制链中启用了 Service Account 类型的准入控制器时(这也是默认的设置),则针对 Pod 新增或修改的请求,会做出如下控制操作:
-
如果 spec.serviceAccount 域没有被设置,则 Kubernetes 默认为其指定名称为 default 的 ServiceAccount
-
如果 Pod 的 spec.serviceAccount 域指定了不存在的 Service Account,则该 Pod 操作会被拒绝。
-
如果在 Pod 中没有指定 ImagePullSecrets,那么这个 spec.serviceAccount 域指定的 ServiceAccount 的 ImagePullSecrets 会被加入该 Pod 中。
-
给 Pod 添加一个特殊的 volumeSource,在该 Volume 中包含 ServiceAccountSecret 中的 Token
-
给 Pod 里的每个容器都增加对应的 VolumeSource,将包含 Secret 的 Volume 挂载到 Pod 中所有容器的指定目录下(/var/run/secrets/kubernetes.io/serviceaccount)
一旦 Secret 被创建,就可以通过下面三种方式使用它 :
-
创建 Pod 时,通过为 Pod 指定 ServiceAccount 来自动使用该 Secret
-
通过挂载该 Secret 到 Pod 来使用它
-
在 Docker 镜像下载时使用,通过指定 Pod 的 spc.ImagePullSecrets 来使用它
pod 中如果不手动指定service-account,则默认的secret会被自动创建,且对应的卷会被挂载到每个pod上
在Pod内使用ServiceAccount的token和证书
go
func InClusterConfig() (*Config, error) {
const (
tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
if len(host) == 0 || len(port) == 0 {
return nil, ErrNotInCluster
}
token, err := ioutil.ReadFile(tokenFile)
if err != nil {
return nil, err
}
tlsClientConfig := TLSClientConfig{}
if _, err := certutil.NewPool(rootCAFile); err != nil {
klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
} else {
tlsClientConfig.CAFile = rootCAFile
}
return &Config{
// TODO: switch to using cluster DNS.
Host: "https://" + net.JoinHostPort(host, port),
TLSClientConfig: tlsClientConfig,
BearerToken: string(token),
BearerTokenFile: tokenFile,
}, nil
}
Service-account token 是 JWT(Json web token)
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik91a1NaajM2UWd6SlU0cE5wRlZ5amdLZTlTd0FSMkVpdlhkYXJqMnhud3MifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tbG54ZHciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjgzM2UyYTc0LTMzNTQtNDlkMS1iMzZhLTY2YTEyOGM4MGYyMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.TYFnZOt6S-o1DqTfhuDt0Tz25u0jhEVKn_G7wUMG8sanwuQaJEJJ8hGZMwLNDVZQrNpsmg3B591YEUhOx8m1G9zMzqt948plgtsW4p878UwzwjuTgk9JDPcYgT5O2CRY_tE6cN4-FPPj3S7_JD6Pto1n55Bx-6yvts8epu4fG7gpTWePMjdOXKC2tz6xawNGvo92RnI4FEVpozDWKJn5p8azrfudqRQhSbs4Oj_haSnV4Rn677gfBLMinoNC_GHd5EWLRQCrHUcErH42gkF66qxVpp19Rh_csZNwQ4fgajbyQQBEuYQ7CJenv73vTbfPifeVCun1Vy2mGjx6hPeN6A
一个JWT实际上就是一个字符串,由三部分组成分别是:
- header(头部)
json
{
"alg": "RS256",
"kid": "OukSZj36QgzJU4pNpFVyjgKe9SwAR2EivXdarj2xnws"
}
- payload (载荷)
payload(载荷)信息存放的是Claims声明信息。载荷其实就是自定义的数据,一般存储用户Id,过期时间等信息。也就是JWT的核心所在,因为这些数据就是使后端知道此token是哪个用户已经登录的凭证。而且这些数据是存在token里面的,由前端携带,所以后端几乎不需要保存任何数据
json
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "default",
"kubernetes.io/serviceaccount/secret.name": "default-token-lnxdw",
"kubernetes.io/serviceaccount/service-account.name": "default",
"kubernetes.io/serviceaccount/service-account.uid": "833e2a74-3354-49d1-b36a-66a128c80f20",
"sub": "system:serviceaccount:default:default"
}
- signature(签名)
signature(签名)需要使用编码后的header和payload以及一个秘钥,使用header中声明的编码方式进行加盐secret组合加密,然后就构成了jwt的第三部分
最后将 token = header + '.'+ payload +'.'+ signature,生成token
Token 验证流程
-
客户端提交账号和密码等信息到服务端
-
通过登录验证后,服务端响应由 JWT 生成的 Token 令牌
-
客户端要访问服务器中其他资源,会在请求中带着 Token 到服务端
-
服务端接收到请求之后,从 Token 中拿出 header 和 payload ,然后通过HS256算法将 header 和 payload 和 "盐" 值 进行计算得出内容,将计算出的内容与Token中的第三部分,也就是 Signature 去比较,如果一致则验证通过,反之则失败
3. Bootstrap Token
API 服务器上设置 --enable-bootstrap-token-auth
标志来启用基于启动引导令牌的身份认证组件
lua
kubeadm token create --kubeconfig ...
在 kube-system namespace 下
- 创建Token
- 查看Token
四、授权/鉴权(Authorization)
授权是识别是否有相应操作权利
授权者,通过组合属性(用户属性,资源属性,实体)的策略向用户授予访问权限。授权的方式也有很多,比如:AlwaysDeny,AlwaysAllow,ABAC,RBAC,Node 等,高版本的 kubernetes 默认的授权方式是 RBAC 和 Node
通过 api-server 的 ****authorization-mode 来开启授权
ini
- --authorization-mode=Node,RBAC 启动 RBAC 和 Node 授权模式
1. 授权方式--Node
要启用节点鉴权器,使用 --authorization-mode=Node
启动 API 服务器
根据调度到 kubelet 上运行的 Pod 为 kubelet 授予权限,专门对 kubelet 发出的 API 请求进行授权
节点鉴权器允许 kubelet 执行 API 操作。包括:
读取操作:
- services、endpoints、nodes、pods、与绑定到 kubelet 节点的 Pod 相关的 Secret、ConfigMap、PersistentVolumeClaim 和持久卷
写入操作:
- 节点和节点状态(启用
NodeRestriction
准入插件以限制 kubelet 只能修改自己的节点) - Pod 和 Pod 状态 (启用
NodeRestriction
准入插件以限制 kubelet 只能修改绑定到自身的 Pod) - Event
身份认证与鉴权相关的操作:
- 对于基于 TLS 的启动引导过程时使用的 certificationsigningrequests API 的读/写权限
- 为委派的身份验证/鉴权检查创建 TokenReview 和 SubjectAccessReview 的能力
为了获得节点鉴权器的授权,kubelet 必须使用一个凭证以表示它在 system:nodes
组中,用户名为 system:node:<nodeName>
,并且该组名和用户名的格式需要与 kublet TLS 启动过程中为 kubelet 创建的标识匹配
yaml
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS**
server: https://10.37.18.97:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: system:node:n37-018-097
name: system:node:n37-018-097@kubernetes
current-context: system:node:n37-018-097@kubernetes
kind: Config
preferences: {}
users:
- name: system:node:n37-018-097
user:
client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem kubelet证书
client-key: /var/lib/kubelet/pki/kubelet-client-current.pem kubelet私钥
vbnet
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -text
- CN (Common Name):通常是证书持有者的名称或标识符,例如域名、个人姓名等
- O (Organization):证书持有者所属的组织或机构名称
观察图可知,这个用户组就是system:nodes,用户名是 system:node:n37-018-097,授权中心是 kubernetes
sql
➜ ~ k get clusterrolebindings.rbac.authorization.k8s.io -owide | grep -i system:nodes
➜ ~ k describe clusterrolebindings.rbac.authorization.k8s.io kubeadm:node-autoapprove-certificate-rotation
Name: kubeadm:node-autoapprove-certificate-rotation
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:nodes
2. 授权方式--RBAC
RBAC 是一种基于角色的授权方式,角色是对用户拥有权利的抽象,角色绑定是将角色绑定到用户(user,group或者service account)
RBAC API 声明了四种 Kubernetes 对象:Role 、ClusterRole 、RoleBinding 和 ClusterRoleBinding
RBAC 的三个基本概念
- Subject:被作用者,它表示k8s中的三类主体, user, group, serviceAccount
- Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限
- RoleBinding:定义了"被作用者"和"角色"的绑定关系
Q: 为什么需要 ClusterRole 和 ClusterRoleBinding ?
A:两个理由
- 一个常规的角色只允许访问和角色在同一命名空间中的资源。 如果你希望允许跨不同命名空间访问资源,就必须要在每个命名空间中创建一个 Role 和 RoleBinding。 如果你想将这种行为扩展到所有的命名空间(集群管理员可能需要 ), 需要在每个命名空间中创建相同的 Role和 RoleBinding。当创建一个新的命名空间时, 必须记住也要在新的命名空间中创建这两个资源
比如我上面 bar 命名空间的服务账号想要访问 foo 命名空间的 services 相关信息,就必须要在 foo 命令空间内创建对应的 Role 和 RoleBinding(注意RoleBinding绑定对应的服务账号是可以指定namespace的)
- 一些特定的资源完全不在命名空间中 (包括 Node、 PersistentVolume、 Namespace,等等)。我们也提到过 API 服务器对外暴露了 一些不表示资源的 UI 路径(例如/ healthz)。 常规角色不能对这些资源或非资源型的 URL 进行授权,但是 ClusterRole 可以
另外,Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用,它们是:
-
cluster-admin:超管
-
admin:普通管理权限
-
edit:修改权限
-
view:只读权限
3. webhook
WebHook 是一种 HTTP 回调:某些条件下触发的 HTTP POST 请求;通过 HTTP POST 发送的简单事件通知。一个基于 web 应用实现的 WebHook 会在特定事件发生时把消息发送给特定的 URL
具体来说,当在判断用户权限时,Webhook
模式会使 Kubernetes 查询外部的 REST 服务
Webhook
模式需要一个 HTTP 配置文件,通过
--authorization-webhook-config-file=SOME_FILENAME
的参数声明
配置文件的格式使用 kubeconfig。 在该文件中,"users" 代表着 API 服务器的 webhook,而 "cluster" 代表着远程服务
使用 HTTPS 客户端认证的配置例子
yaml
# Kubernetes API 版本
apiVersion: v1
# API 对象种类
kind: Config
# clusters 代表远程服务。
clusters:
- name: name-of-remote-authz-service
cluster:
# 对远程服务进行身份认证的 CA
certificate-authority: /path/to/ca.pem
# 远程服务的查询 URL。必须使用 'https'。
server: https://authz.example.com/authorize
# users 代表 API 服务器的 webhook 配置
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # webhook plugin 使用 cert
client-key: /path/to/key.pem # cert 所对应的 key
# kubeconfig 文件必须有 context。需要提供一个给 API 服务器。
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authz-service
user: name-of-api-server
name: webhook
五、准入(Admisson control)
判断你的操作是否符合集群的要求,是一种更灵活的管控机制,用户还可以根据自己的需求定义准入插件来管理集群
kubernetes 中将准入模块分为三种,validating(验证型),mutating(修改型)以及两者兼有,准入的默认配置是NodeRestriction
默认的准入控制器
sql
查看哪些插件被默认启动
kube-apiserver -h | grep enable-admission-plugins
--enable-admission-plugins strings admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, PodSecurity, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurity, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.
NamespaceLifecycle
类别:验证型
该准入控制器禁止在一个正在被终止的 Namespace
中创建新对象,并确保针对不存在的 Namespace
的请求被拒绝。该准入控制器还会禁止删除三个系统保留的名字空间,即 default
、 kube-system
和 kube-public
。
Namespace
的删除操作会触发一系列删除该名字空间中所有对象(Pod、Service 等)的操作。 为了确保这个过程的完整性,我们强烈建议启用这个准入控制器。
DefaultTolerationSeconds
类别:修改型
此准入控制器基于 k8s-apiserver 的输入参数 default-not-ready-toleration-seconds
和 default-unreachable-toleration-seconds
为 Pod 设置默认的容忍度,以容忍 notready:NoExecute
和 unreachable:NoExecute
污点 (如果 Pod 尚未容忍 node.kubernetes.io/not-ready:NoExecute
和 node.kubernetes.io/unreachable:NoExecute
污点的话)。 default-not-ready-toleration-seconds
和 default-unreachable-toleration-seconds
的默认值是 5 分钟。
ServiceAccount
类别:变更和验证
此准入控制器实现了 ServiceAccount 的自动化
额外的准入控制器
也可以用 --enable-admission-plugins 开启额外的"准入控制器"
NodeRestriction
类别:验证型
这个插件的作用是限制每一个kubelet只能操作自己node的资源
Node 授权模式针对的 Subject 是 Node, 不是 user 或者应用的 Service Account, 是专门对 kubelet 发起的 API 请求进行授权的管理模式
Node 授权者 (node authorizer) 允许 kubelet 发起 API 操作的资源对象如下
- 读取操作: Service、 Endpoint、 Node、 Pod、 Secret、 ConfigMap、 PVC, 以及绑定到 Node 的与 Pod 相关的持久卷
- 写入操作:
- Node 和 Node Status (启用 NodeRestriction 准入控制器,以限制 kubelet 只能修改自己节点的信息)
- Pod 和 Pod Status (启用 NodeRestriction 准入控制器,以限制 kubelet 只能修改绑 定到本节点的 Pod 信息 )
- Event
- 授权相关操作:
- 基于 TLS 启动引导过程中使用的 certificationsigningrequest 资源对象的读写操作;
- 在代理鉴权或授权检查过程中创建 tokenreview 和 subjectaccessreview 资源对象
为了获取 Node 授权者的授权, kubelet 需要使用一个凭据,以标识它在 system:nodes 组内,用户名为 system:node:,并且该组名和用户名的格式需要与 kubeletTLS 启动过程中为 kubelet 创建的标识匹配
参考文章
Linux企业运维------Kubernetes(十三)访问控制