想要从Pod内部与API-Server 进行通信,只需要关注以下三件事情
- 确定API服务器的位置
- 确保是与API服务器进行交互,而不是一个冒名者
- 通过服务器的认证,否则将不能查看 任何内容以及进行任何操作
运行一个pod来尝试与API服务器进行通信
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- name: main
image: curlimages/curl
command: ["sleep", "infinity"]
tty: true
第一步:发现API服务器地址
首先,需要找到Kubemetes API 服务器的 IP 地址和端口。 这一 点比较容易做到, 因为一个名为kubernetes的服务在默认的命名空间被自动暴露,并被配置为指向 API 服务器
➜ ~ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.18.2.1 <none> 443/TCP
每个服务都被配置了对应的环境变量, 在容器内通过查询 KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT 这两个环境变量 ;就可以获取 API 服务器的 IP 地址和端口
~ $ env | grep -i kubernetes_service
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=172.18.2.1 这个地址就是上面的kubernetes的service的地址
每个 service 都可以获得一个 DNS 入口,所以只需要简单的将 curl 指向 https://kubernetes 即可
会发现 curl 命令报错,这是未进行HTTPS校验(也就是要检查服务端的证书)
我们可以直接使用 -k 参数来跳过校验(但是不推荐:在真实的应用中, 永远不要跳过栓查服务器证书的环节。 这样做会导致你的应用验证凭证暴露给采用中间人攻击方式的攻击者 )
第二步:验证服务器的身份
k8s 创建的每个pod中的容器都会自动挂载一个secret 到容器的 /var/run/secrets/kubemetes.io/serviceaccount
目录下
这个目录下有三个东西
ca.crt 就是 CA 的证书,用来对 Kubemetes API 服务器证书进行签名
namespace 就是当前的 pod 所在的命名空间
curl 允许使用 -cacert 选项来指定 CA 证书或者直接通过设置 CURL_CA_BUNDLE环境变量 ,而不必在每次运行curl时都指定 --cacert 选项; 我们来尝试重新访问 API 服务器
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
~ $ curl https://kubernetes
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message" : "Unauthorized" ,
"reason" : "Unauthorized" ,
"code": 401
}
会发现返回了未授权的信息,这表明我们的客户端 curl 现在信任服务器,但是API服务器并不确认访问者的身份, 所以 API-Server 没有授权允许访问
第三步:获取API服务器授权
为了获得授权, 我们需要认证的凭证,这个凭证就在 token 文件中
TOKEN=$( cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -H "Authorization: Bearer $TOKEN" https://kubernetes
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message" : "forbidden: User "system:serviceaccount:default:default" cannot get path "/"" ,
"reason" : "Forbidden" ,
"details": {},
"code": 403
}
我们再次访问发现还是访问不了,但是这次报错信息是 Forbidden 信息,说明授权过了,但是访问资源的权限有限,默认的服务账号 default 不允许访问 api-server 下的根目录 / 的权限
我们调整资源访问路径,比如获取当前pod所在命名空间中的所有pod清单
$ curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods > txt && vi txt
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "777954"
},
"items": [
{
"metadata": {
"name": "redis",
"generateName": "redis",
"namespace": "default",
"uid": "d4889bd4-f0da-42e1-8b8b-8f84d4db208f",
"resourceVersion": "283684",
"creationTimestamp": "2023-04-06T13:17:02Z",
"labels": {
...
},
"annotations": {
"sidecar.istio.io/inject": "false"
},
....
可以发现能看到 default 空间内的所有的pod的资源信息
注意:上面的Token代表的是这个pod所绑定的ServiceAccount的访问权限
默认情况下,default ServiceAccount 拥有以下权限:
- 访问自身的 API 对象:default ServiceAccount 可以获取、创建、更新和删除与自身相关的 Kubernetes API 对象,如 Pod、Service、ReplicationController 等。
- 访问自身命名空间的 API 对象:default ServiceAccount 可以获取、创建、更新和删除该命名空间下的 Kubernetes API 对象,如 Pod、Service、ReplicationController 等。
- 访问所有命名空间的 API 对象:default ServiceAccount 可以获取、列出其他命名空间下的 Kubernetes API 对象,但不能创建、更新和删除。
- 访问 Kubernetes API 集群范围的对象(就是不属于任何Namespace的对象,如 Node、PV、PVC):default ServiceAccount 可以获取、列出 Kubernetes API 集群范围的对象,如 Namespace、Node 等,但不能创建、更新和删除。
注意:这里凭证代表的是当前这个pod绑定的ServiceAccount的身份,所以这个token能访问到的资源的权限就是这个ServiceAccount所绑定的Role的相关权限
就可以理解为 token 是一个身份的象征,这个身份就是 ServiceAccount,而拿到这个 token 所能操作的权限就是这个身份所绑定的 Role 的指定权限