权限管理实践
本文主要通过一个例子来介绍如何基于 Kubernetes 的 RBAC 实现授权决策,允许集群管理员通过 Kubernetes API 动态配置策略,让非集群管理员具有某个 namespace 下的所有权限,并可通过 Dashboard 或者 kubectl 来管理该 ns 下的资源。
如果要更加深入地了解和掌握 RBAC,可以查看官方文档 。
K8S里面有两类用户,
- Service Account,Kubernetes 中一种用于非人类用户的账号,在 Kubernetes 集群中提供不同的身份标识。详细可参考官方文档
- 普通用户(user),K8S本身不并管理user,而是交由外部独立服务管理,不能通过K8SAPI来创建user;
目前是通过kubectl和Dashboard来管理集群,Service account已经足够满足要求,而且可以在Kubernetes中直接管理。因此这里不介绍如何使用user这个对象来管理集群。
1. 创建NS
kubectl create ns pre
上面的示例创建了一个名为”pre”的命名空间,用于部署预发布的服务。
2. 创建Service Account
kubectl create sa mingpianwang -n pre
在pre的命名空间下创建一个名为”mingpianwang”的Service account,给到某个特定的用户使用。
3. 赋予权限
由于我们已经预先说明,需要给mingpianwang这个用户赋予pre 这个命名空间下的所有权限,即admin权限。
重点来了,RoleBinding 对象是可以引用一个ClusterRole对象的,然后这个ClusterRole所拥有的权限只会在这个NS下面有效。这一点允许管理员在整个集群范围内首先定义一组通用的角色,然后再在不同的名字空间中复用这些角色。
我们先看下集群内默认的ClusterRole有哪些,执行get clusterrole命名可以看到,有admin、cluster-admin、edit等角色,那我们可以直接使用admin这个clusterrole角色,通过rolebinding的方式赋予”mingpianwang“这个用户。
[root@10-9-149-7 ~]# kubectl get clusterrole
NAME AGE
admin 4h53m
cluster-admin 4h53m
edit 4h53m
示例的yaml如下,我们只要执行下kubectl apply -f rolebinding.yaml 即可。
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kubernetes-dashboard-minimal
namespace: pre
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin
subjects:
- kind: ServiceAccount
name: mingpianwang
namespace: pre
当然,我们也可以创建一个Namespace级别的role,并将这个角色绑定到ServiceAccount。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: pre
name: admin
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kubernetes-dashboard-minimal
namespace: pre
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: admin
subjects:
- kind: ServiceAccount
name: mingpianwang
namespace: pre
只是这个role不能复用到其他Namespace,一般只有在做精细化权限管理的时候,我们才会创建Role对象,比如一个只能查看pod 名称为test-pod的Role。其他场景下,我们推荐集群管理员使用ClusterRole。
4. 访问Dashboard
在 Kubernetes 1.22 之前的版本中,Kubernetes 会以Secret 形式为ServciceAccount 提供一个长期的有效的静态令牌, Kubernetes v1.22 及更高版本中,需要用户自己配置;详细的可参考官方文档手动获取 ServiceAccount 凭据
Kubernetes 1.22 之前的版本
就要获取到”mingpianwang“的token,其实就是secret了。通过下面的方式来获取,最后的token复制下来就可以了。
bash-4.4# kubectl describe sa/mingpianwang -n pre
Name: mingpianwang
Namespace: pre
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: mingpianwang-token-4l8xj
Tokens: mingpianwang-token-4l8xj
Events: <none>
bash-4.4# kubectl describe secret/mingpianwang-token-4l8xj -n pre
Name: mingpianwang-token-4l8xj
Namespace: pre
Labels: <none>
Annotations: kubernetes.io/service-account.name: mingpianwang
kubernetes.io/service-account.uid: d7bb847d-7621-11e9-9679-5254007e7ba9
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1359 bytes
namespace: 5 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9/....
Kubernetes 1.22 之后的版本
如果你需要为 ServiceAccount 获得一个 API 令牌,你可以创建一个新的、带有特殊注解 kubernetes.io/service-account.name 的 Secret 对象。详细可参考官方文档手动获取一个长久的Token
bash-4.4# kubectl -n pre apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: mingpianwang-secret
annotations:
kubernetes.io/service-account.name: mingpianwang
type: kubernetes.io/service-account-token
EOF
可以通过下面的命令来查看 Secret:
bash-4.4# kubectl -n pre describe secrets/mingpianwang-secret
Name: mingpianwang-secret
Namespace: pre
Labels: <none>
Annotations: kubernetes.io/service-account.name: mingpianwang
kubernetes.io/service-account.uid: 0c3e9a16-6962-45ee-9e6e-e7f0107cd9a8
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1359 bytes
namespace: 3 bytes
token: ...
这里将 token 的内容抹去了。当你删除一个与某 Secret 相关联的 ServiceAccount 时,Kubernetes 的控制面会自动清理该 Secret 中长期有效的令牌。
可以使用以下命令查看 ServiceAccount:
kubectl -n pre get serviceaccount mingpianwang -o yaml
在 ServiceAccount API 对象中看不到 mingpianwang-secret Secret, .secrets 字段, 因为该字段只会填充自动生成的 Secret。
登陆Dashboard
复制到登录框,我们发现可以登录到Dashboard首页,不过需要注意的是,由于这个账号只有pre这个命名空间的权限,而Dashboard默认是default,所以进去之后会报一堆错咯,没关系,只要将左侧的NS改为pre即可。
5. 通过 kubectl 管理集群
由于我们还需要支持 kubectl 命令行管理 NS,因此还需要为 mingpianwang 生成kubeconfig,一个用户还好,多个用户就很麻烦了,因此这里我们使用一个自动生成 kubeconfig 的脚本,代码如下:
#!/bin/bash -e
# Usage ./k8s-service-account-kubeconfig.sh ( namespace ) ( service account name )
TEMPDIR=$( mktemp -d )
trap "{ rm -rf $TEMPDIR ; exit 255; }" EXIT
SA_SECRET=$( kubectl get sa -n $1 $2 -o jsonpath='{.secrets[0].name}' )
# Pull the bearer token and cluster CA from the service account secret.
BEARER_TOKEN=$( kubectl get secrets -n $1 $SA_SECRET -o jsonpath='{.data.token}' | base64 -d )
kubectl get secrets -n $1 $SA_SECRET -o jsonpath='{.data.ca\.crt}' | base64 -d > $TEMPDIR/ca.crt
CLUSTER_URL=$( kubectl config view -o jsonpath='{.clusters[0].cluster.server}' )
KUBECONFIG=kubeconfig
kubectl config --kubeconfig=$KUBECONFIG \
set-cluster \
$CLUSTER_URL \
--server=$CLUSTER_URL \
--certificate-authority=$TEMPDIR/ca.crt \
--embed-certs=true
kubectl config --kubeconfig=$KUBECONFIG \
set-credentials $2 --token=$BEARER_TOKEN
kubectl config --kubeconfig=$KUBECONFIG \
set-context registry \
--cluster=$CLUSTER_URL \
--user=$2 \
--namespace=$1
kubectl config --kubeconfig=$KUBECONFIG \
use-context registry
echo "kubeconfig written to file \"$KUBECONFIG\""
直接在master节点执行sh kubeconfig.sh pre mingpianwang
,即可自动生成一个kubeconfig文件,将这个kubeconfig文件分发给使用者,让其复制到~/.kube/config下即可,而且默认NS就是pre,get
nodes等操作都是不被允许的。
自动生成kubeconfig的源代码在这里,generator kubeconfig ,我们只是加了一个默认NS,这样不需要在执行kubectl命令的时候追加-n pre。