1.认识k8s中的用户账号:生成证书
1.1 用户账号(UA UserAccount)
UA代表的是集群外部的实际用户,如开发人员、系统管理员等。 UA并不是 Kubernetes 原生的资源类型,通常是通过外部身份提供者(如 LDAP、OAuth、OIDC 等)来管理的。UA是为集群管理员和开发人员设计的,用于直接访问和操作集群资源。Kubernetes 不管理UA,而是依赖外部的认证机制。认证插件负责将外部用户的身份信息传递给 Kubernetes API Server。UA主要用于对 Kubernetes API Server 的访问控制。用于管理、监控和配置集群资源。
例如kubectl get pods -n kube-system
,像这种命令还有go程序访问k8s都是访问某个api,这就是UA。
例如我们正在做一个普通的网站
- 首先会有不同的用户
- 登录时需要认证用户的账号
- 查看账号的权限—涉及到角色、权限、用户和角色绑定—RABC
- 再根据权限决定是否可以访问
我们在使用各种命令的时候并不是没有用户,而是之前就配置好了。
cat ~/.kube/config
admin拥有所有权限。
1.2 服务账号(SA ServiceAccount)
SA是 Kubernetes 原生的资源类型,主要用于在集群内运行的 Pod 之间的身份验证和授权。SA 是为自动化流程和应用程序设计的。SA 代表的是在集群内部运行的应用程序、Pod 等。每个命名空间默认创建一个 default
SA。SA 由 Kubernetes 管理,并存储在 etcd 中。可以通过 YAML 文件创建和管理SA。
1.3 创建客户端证书
在k8s中,UserAccount也就是集群外部访问时使用的用户。最常见的就是kubectl命令就是作为kubernetes-admin用户来执行的,k8s本身不记录这些账号。
我们首先来学习默认的认证方式 :客户端证书
首先假设你装好了openssl(如果没有安装,请执行sudo apt install openssl
)
- 创建一个文件夹叫做 ua/horizon
- cd 到 ua/horizon 下
- 执行
openssl genrsa -out client.key 2048
生成客户端私钥。openssl req -new -key client.key -out client.csr -subj "/CN=horizon"
根据私钥生成csr,/CN指定了用户名horizon。sudo openssl x509 -req -in client.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out client.crt -days 365
根据k8s的CA证书生成我们用户的客户端证书
2.用户账号:使用证书初步请求api、设置上下文
如果你忘了证书这是的CN(Common name)是啥 可以用下面的命令搞定
openssl x509 -noout -subject -in client.crt
首先我们把client.crt加入到 ~/.kube/config,执行kubectl命令时切换成我们的用户(虽然现在还没设置权限)
kubectl config --kubeconfig=/home/horizon/.kube/config set-credentials horzion --client-certificate=/home/horizon/client.crt --client-key=/home/horizon/client.key
cat ~/.kube/config
可以看到多了一组-name: horizon user: …
接下来我们要创建一个上下文。可以使用kubectl config current-context
来查看当前的上下文。
使用以下两条命令设置新的上下文并使用。
kubectl config --kubeconfig=/home/horizon/.kube/config set-context user_context --cluster=kubernetes --user=horizon
kubectl config --kubeconfig=/home/horizon/.kube/config use-context user_context
此时,再次使用命令kubectl get pods -n kube-system
发现已经没有权限了。
3.入门Role和RoleBinding、创建一个角色
3.1 RABC
RBAC(Role-Based Access Control,基于角色的访问控制)是 Kubernetes 中用于管理权限的机制。 RBAC 允许管理员通过定义角色和绑定,将特定的权限分配给用户、组或服务账号,从而控制谁可以对哪些资源执行哪些操作。
3.1.1 RBAC 组件
- Role:定义一组权限,仅在特定命名空间内有效。
- ClusterRole:定义一组权限,可以在集群范围内使用。
- RoleBinding:将一个 Role 绑定到一个用户或组,使其在特定命名空间内生效。
- ClusterRoleBinding:将一个 ClusterRole 绑定到一个用户或组,使其在整个集群范围内生效。
3.1.2 创建role
上一小节我们把horiozn这个用户(UA)加入到kubectl config中, 并且用了kubectl config use-context user_context
来切换上下文。
切回管理员使用命令kubectl config use-context kubernetes-admin@kuberneres
。
我们可以使用kubectl get role --all-namespaces
来查看所有命名空间下的role。
接下来我们要做的是让horizon这个用户账号能查看pod
我们创建一个一个文件mypod_role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: mypod
rules:
- apiGroups: ["*"]
resources: ["pods"]
verbs: ["get", "watch", "list"]
关于apiversion:查文档Using RBAC Authorization
关于资源:可以用 kubectl api-resources -o wide 查看
例如 role 对应的操作如下:create、delete、deletecollection、get、list、patch、update、wath
接下来执行kubectl apply -f mypod_role.yaml
查看 kubectl get role -n default
删除role kubectl delete role mypod
不加 -n 默认就是default
4.用户和角色进行绑定(RoleBinding)
上节中介绍到,Role可以包含很多权限,用于授予对单个命名空间的资源访问。RoleBinding,将一个 Role 绑定到一个用户或组,使其在特定命名空间内生效。
接下来我们进行绑定。
第一种方式:
执行命令,这个要在kubectl config use-context kubernetes-admin@kubernetes
下执行,然后再切换回去测试。
kubectl create rolebinding mypodbinding -n default --role mypod --user horizon
kubectl get rolebinding
kubectl config use-context user_context
测试
kubectl get pods -n default
第二种方式:
创建文件mypod_rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: mypodrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mypod
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: horizon
5.ClusterRole和RoleBinding
ClusterRole:管理多个namespace,就需要使用到clusterrole,绑定既可以使用RoleBinding,也可以使用ClusterRoleBinding(两者效果不同)
第一种:ClusterRole+RoleBinding
创建mypod-cluster.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: mypod-cluster
rules:
- apiGroups: ["*"]
resources: ["pods"]
verbs: ["get", "watch", "list"]
然后绑定,mypodrolebinding-cluster.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mypodrolebinding-cluster
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mypod-cluster
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: horzion
测试
kukectl config use-config user_context
kubectl get pods -n kube-system
6.ClusterRole和CluserRoleBinding
第二种:ClusterRole+ClusterRoleBinding
首先把之前的role和rolebinding都删除掉,
kubectl delete rolebinding mypodrolebinding-cluster -n kube-system
kubectl delete rolebinding mypodrolebinding
kubectl delete role mypod
创建 mypod-clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mypod-clusterrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mypod-cluster
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: horizon
测试
首先切换上下文
kukectl config use-config user_context
kubectl get pods -n kube-system
7.配置使用token的方式请求API(UserAccount)
之前我们介绍了用证书访问api,接下来我们使用token的方式。
首先测试一下:curl https://192.168.22.142:6443
,这是不可以访问的。
接下来我们我们设置一个token(使用上节的普通用户horizon)
head -c 16 /dev/urandom | od -An -t x | tr -d ' '
kubectl config set-credentials horzion --token=456sd53sdf1e976d7f78ac7fcxgf9adc
测试:cat ~/.kube/config
,发现-name: horzion下面多了一个token
找一个获取pod的api试一试
curl -H "Authorization: Bearer 456sd53sdf1e976d7f78ac7fcxgf9adc" https://192.168.22.142:6443/api/v1/namespaces/default/pods --insecure
curl --cert ./client.crt --key ./client.key --cacert /etc/kubernetes/pki/ca.crt -s https://192.168.22.142:6443/api/v1/namespaces/default/pods
测试一下:还是不行的,若是使用kubeadm配置的集群,没有加载我们的token file,只能支持证书验证的方式,需要去修改一下api server启动的参数
首先创建
sudo vi /etc/kubernetes/pki/token_auth
加入一下内容
456sd53sdf1e976d7f78ac7fcxgf9adc,horzion,1001
然后是修改api-server启动参数
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
加入
--token-auth-file=/etc/kubernetes/pki/token_auth
设置完之后再次进行测试
curl -H "Authorization: Bearer 456sd53sdf1e976d7f78ac7fcxgf9adc" https://192.168.22.142:6443/api/v1/namespaces/default/pods --insecure
8.ServiceAccount(1):创建账号
查看ServiceAccount:kubectl get sa 等于 kubectl get sa -n default
、kubectl get sa --all-namespaces
创建:kubectl create serviceaccount mysa
使用kubectl describe sa mysa
查看,发现各个参数都为空,这是因为:
在 Kubernetes 1.24 版本之后,ServiceAccount 令牌控制器的行为发生了变化,默认情况下不会自动为每个 ServiceAccount 创建秘密令牌(Secrets)。相反,你需要显式地请求或引用一个投影的令牌。
- 创建 ServiceAccount
mysa:
kubectl create serviceaccount mysa
2. 创建一个包含投影令牌的 Secret:
创建一个 YAML 文件,例如 mysa-token.yaml,以请求投影令牌:
apiVersion: v1
kind: Secret
metadata:
name: mysa-token
annotations:
kubernetes.io/service-account.name: mysa
type: kubernetes.io/service-account-token
应用此配置:kubectl apply -f mysa-token.yaml
3. 查看 Secret:kubectl get secret mysa-token -o yaml
4. 在 Pod 中使用投影令牌:然后apply pod
首先kubectl delete sa mysa
kubectl create serviceaccount mysa -o yaml --dry-run=client
或者
kubectl create serviceaccount mysa -o yaml --dry-run=client > mysa.yaml
cat mysa.yaml
kubtctl appy -f mysa.yaml
kubectl describe sa mysa
注意这里还是没有Tokens,需要手动映射,下面再介绍
9.ServiceAccount(2):赋予权限、外部访问API
接下来做的实验:外部请求依然用ServiceAccount
首先装个软件jq:是一个json处理命令,可以对json数据进行分片、过滤、映射和转换jq。对数据进行格式化输出.sudo apt install jq
绑定角色:之前我们创建了一个clusterrole叫做mypod-cluster,此权限可以查看pods列表
kubectl create clusterrolebinding mysa-crb --clusterrole=mypod-cluster --serviceaccount=default:mysa
kubectl get clusterrolebinding
接下来,如何对api server进行请求,这里暂时使用外部,
kubectl get sa mysa
kubectl get sa mysa -o json
这里发现根本没有secrets,需自己手动映射,根据上节。
发现get sa mysa -o json中还是没有secret,但是没关系,创建好的secret中已将包含sa的映射,在使用此sa访问api是只需指定此secret中的token即可。
kubectl get sa mysa -o json | jq '.secrets[0].name'
kubectl get sa mysa -o json | jq -Mr '.secrets[0].name'
假设得到的值是mysa-token-6tggr
kubectl get secret mysa-token-6tggr
kubectl get secret mysa-token-6tggr -o json | jq -Mr '.data.token'
kubectl get secret mysa-token-6tggr -o json | jq -Mr '.data.token' | base64 -d
连接起来
kubectl get secret $(kubectl get sa mysa -o json | jq -Mr '.secrets[0].name'
) -o json | jq -Mr '.data.token' | base64 -d
mysatoken=$(kubectl get secret $(kubectl get sa mysa -o json | jq -Mr '.secrets[0].name'
) -o json | jq -Mr '.data.token' | base64 -d)
在我的集群中应该这样写
mysatoken=$(kubectl get secret mysa-token -o json | jq -Mr '.data.token' | base64 -d)
测试
curl -H "Authorization: Bearer $mysatoken" --insecure https://192.168.22.142:6443/api/v1/namespaces/default/pods
10.ServiceAccount(3):在Pod里访问k8sAPI(token的方式)
创建一个pod
myngx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myngx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
serviceAccountName: mysa # 注意这里一定要指明
containers:
- name: nginxtest
image: nginx:1.18-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
kubectl apply -f myngx.yaml
进入该容器
kubectl exec -it myngx-575cf56d8-nb4sz -- sh
在容器中执行
echo $KUBERNETES_SERVICE_HOST
echo $KUBERNETES_PORT_443_TCP_PORT
于是
echo $KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT
临时设置的环境变量,退出容器后就没有了,需要重新设置
TOKEN=`cat /var/run/secrets/kubernetes.io/serviceaccount/token`
APISERVER="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT"
测试
curl --header "Authorization: Bearer $TOKEN" --insecure -s $APISERVER/api
curl --header "Authorization: Bearer $TOKEN" --insecure -s $APISERVER/api/v1/namespaces/default/pods
这里两个都可以,因为我们已经把myngx.yaml中加入了serviceAccountName: mysa
,已经有了权限。
11.ServiceAccount(4):在Pod里访问k8sAPI(token+证书的方式)
更常用的方式是直接使用证书
cd到存放证书的位置,进入容器后执行,cd /var/run/secrets/kubernetes.io/serviceaccount
执行
注意两个临时环境变量要重新设置
TOKEN=`cat /var/run/secrets/kubernetes.io/serviceaccount/token`
APISERVER="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT"
测试
curl --header "Authorization: Bearer $TOKEN" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt $APISERVER/api/v1/namespaces/default/pods
12.写在最后
当然,以上只是k8s中最基础的认证与授权功能,更高阶的也都在此基础之上,如果大家有兴趣请自行探索。如果大家觉得有用,欢迎大家点赞、收藏、关注我,有什么问题可以在评论区讨论或者私信我。在码字的过程中难免会出错,欢迎大家批评指正。我会长期分享K8s、CloudNative方面的知识。