在 Pod 中,访问 Kubernetes API 接口,并控制访问权限

问题描述

在实际的应用场景中,我们的应用程序运行在集群中(以 Pod 的形式存在),并且该应用程序将在集群中进行创建资源、修改资源、删除资源等等操作。

在 Pod 中,访问 Kubernetes API 的方法有很多,而通过 Client Libraries(程序类库)是官方推荐的做法,也是我们接下来将要学习的方法。

该笔记将记录:在 Pod 中,通过 Client Libraries 访问 Kubernetes API(管理 Kubernetes 集群)的方法,以及相关问题的解决办法。

解决方案

接下来我们将介绍例如,如果创建 ServiceAccount 对应用程序进行访问控制,只允许其查看 Pod 资源(即查看 Pod 列表和详细信息)

第 1 步、创建 ServiceAccount 资源

而这其中最大的问题是,如何进行合理的授权,即对于既定的用户或应用程序,如何允许或拒绝特定的操作?—— 通过 ServiceAccount 实现。

# kubectl create serviceaccount myappsa

第 2 步、引用 ServiceAccount 资源

定义一个 Pod,使用为 myappsa 的 ServiceAccount 资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kubectl apply -f - <<EOF
kind: Pod
apiVersion: v1
metadata:
name: myapp
spec:
serviceAccountName: myappsa
containers:
- name: main
image: bitnami/kubectl:latest
command:
- "sleep"
- "infinity"
EOF

ServiceAccount 是种身份,而在 Pod 中引用 ServiceAccount 则是将这种身份赋予 Pod 实例。而接下来的任务是给 ServiceAccount 这种身份赋予各种权限 —— 具体的做法便是将各种角色(Role)绑定(RoleBinding)到这个身份(ServiceAccount)上。

第 3 步、创建 Role 资源

定义名为 podreader 的 Role 资源,并定义其能够进行的访问操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl apply -f - <<EOF
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: podreader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
EOF

# 或,直接使用命令创建
# kubectl create role podreader --verb=get --verb=list --resource=pods -n default

第 4 步、将 Role 与 ServiceAccount 绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl apply -f - <<EOF
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: podreaderbinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: podreader
subjects:
- kind: ServiceAccount
name: myappsa
namespace: default
EOF

# 或,从从命令行直接创建:
# kubectl create rolebinding podreaderbinding --role=default:podreader --serviceaccount=default:myappsa --namesepace default -n default

第 5 步、访问 Kubernetes API 测试

通过 Service Account 相关信息来访问资源:

1
2
3
4
5
6
7
8
9
10
11
12
# 通过我们运行的 kubectl 容器访问
kubectl get pods # 这里是为了体现 kubectl 并未通过 kubeconfig 的信息来访问集群,
# 而是通过 /var/run/secrets/kubernetes.io/serviceaccount 来访问集群
# kubectl 手册介绍该命令是如何查找访问信息的;

# 通过 curl API 访问
APISERVER=https://kubernetes.default.svc
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
TOKEN=$(cat ${SERVICEACCOUNT}/token)
CACERT=${SERVICEACCOUNT}/ca.crt
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/default/pods

第 6 步、通过客户端类库访问集群

以 Java 客户端为例:

如果需要在集群内部访问集群:
1)按照如上示例,定义 ServiceAccount 资源,
2)并参照 InClusterClientExample.java 示例代码

如果需要在集群外部访问集群:
1)按照如上示例,在远端集群定义 ServiceAccount 资源,
2)并参照 java/KubeConfigFileClientExample.java 示例代码

补充说明

访问集群级别的资源

上面是命名空间内的访问控制设置,因为上述使用的是 Role 和 RoleBinding 资源,而对于集群范围的访问控制应该使用 ClusterRole 和ClusterRoleBinding 命令。

替换 kind 为 ClusterRole 及 ClusterRoleBinding 即可,其他部分与 Role、RoleBinding 类似,这里不再赘述。

通过 ServiceAccount 生成 Kubeconfig 的方法

如果需要使用 kubeconfig 文件,可通过 ServiceAccount 资源来创建,参考 How to create a kubectl config file for serviceaccount 讨论。

简而言之,kubeconfig 的 user 部分为 token,而非 client-certificate-data 与 client-key-data 参数。

参考文献

Accessing the Kubernetes API from a Pod | Kubernetes
dashboard - How to bind roles with service accounts - Kubernetes - Stack Overflow
A Quick Intro to the Kubernetes Java Client | Baeldung