【Kubernetes】开发中与 APIServer 常见的几种认证方式

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 介绍了几种在开发过程中与 APIServer 交互的认证方式

前言

在 Kubernetes 生态开发过程中,不管是使用 client-go、controller-runtime-client 还是 http-client等,搞定与 APIServer 的认证是必不可少的。本文介绍了在开发过程中,几种常见的认证方式,从案例入手,快速入门。


环境准备

这里我们选用 kind 快速启动一个 kubernetes 环境,当然你可以选择其他的方式,如果手中已经有了 kubernetes 环境,可以略过该章节。


首先我们安装 kind(下载地址),选择对应开发环境的 release 即可,kind 只有一个二进制,部署很方便。

image.png


安装完成后,我们执行如下命令,等待初始化完成。

默认会拉起最新支持的 kubernetes 版本,可以通过 --image 指定镜像,支持的镜像可以从 dockerhub 中获取

kind create cluster --name dev --image kindest/node:v1.20.15

image.png

根据提示我们将 kubectl 的 context 切换到新拉起的环境。

kubectl cluster-info --context kind-dev
kubectl get no 

image.png

到这里环境就准备完成了。


场景分析

环境准备完成后,我们拟定一个场景,通过不同的认证方式向 apiserver 请求当前所有的节点,并按照上述 NAME, STATUS, ROLES, AGE, VERSION 的顺序输出。


前置准备

首先我们初始化一个项目,并且获取 client-go package

go get k8s.io/client-go@v0.22.7


对于上述场景,我们可以对 corev1.Node 资源进行解析,封装成一个简单的函数,后续几种连接 apiserver 后可以将 node 对象通过该函数渲染。

packageresourcesimport (
"strings""time"corev1"k8s.io/api/core/v1")
const (
NodeRoleLabel="node-role.kubernetes.io/"KubernetesRoleLabel="kubernetes.io/role")
funcGetNodeFormattedInfo(node*corev1.Node) []string {
var (
statusstringroles=make([]string, 0)
    )
// 解析节点状态,status conditions 中最后实例中的 Type 表示最后的状态iflen(node.Status.Conditions) !=0 {
status=string(node.Status.Conditions[len(node.Status.Conditions)-1].Type)
    } else {
status="Unknown"    }
// 节点的角色标签可以从 labels 中过滤出来fork, v :=rangenode.Labels {
switch {
casestrings.HasPrefix(k, NodeRoleLabel):
ifrole :=strings.TrimPrefix(k, NodeRoleLabel); len(role) >0 {
roles=append(roles, role)
            }
casek==KubernetesRoleLabel&&v!="":
roles=append(roles, v)
        }
    }
return []string{
node.Name,
status,
strings.Join(roles, ","),
// 通过节点创建时间与现在时间差可以计算出节点存活时间time.Now().Sub(node.CreationTimestamp.Time).String(),
// version 代表的是该节点 kubelet 注册上来的版本node.Status.NodeInfo.KubeletVersion,
    }
}


kubeconfig认证

首先我们介绍的是通过 kubeconfig 认证,这也是最直接的认证方式,直接上代码

packagemainimport (
"context""flag""path/filepath""strings""fmt""k8s.io/client-go/tools/clientcmd""k8s.io/client-go/kubernetes"metav1"k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/util/homedir""kubernetes-auth/pkg/resources")
funcmain() {
varkubeconfig*stringifhome :=homedir.HomeDir(); home!="" {
kubeconfig=flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "")
    } else {
kubeconfig=flag.String("kubeconfig", "", "")
    }
flag.Parse()
// 自动通过 homedir 解析 kubeconfig 路径// 此处同样可以手动指定 kubeconfig 的 pathrc, err :=clientcmd.BuildConfigFromFlags("", *kubeconfig)
iferr!=nil {
panic(err)
    }
// 获取 client-setcs, err :=kubernetes.NewForConfig(rc)
iferr!=nil {
panic(err)
    }
// 通过 clientset 获取 NodeListnodes, err :=cs.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
iferr!=nil {
panic(err)
    }
for_, node :=rangenodes.Items {
fmt.Println(strings.Join(resources.GetNodeFormattedInfo(&node), " "))
    }
}

对于上面代码我们进行一下分析,上述主要是通过 clientcmd.BuildConfigFromFlags 来指定 kubeconfig 的 path,其中会对 kubeconfig 中的 APIServer 地址,CA,Cert 三者进行解析,生成最重要的对象 rest.Config,这也是我们连接 Kubneretes 集群最重要的结构,后面会详细介绍。

rc, err :=clientcmd.BuildConfigFromFlags("", *kubeconfig)
iferr!=nil {
panic(err)
}

image.png

拿到 rest.Config 后,直接通过  kubernetes.NewForConfig 即可生成操作 Kubernetes 资源的 ClientSet,ClientSet 中包含了操作 Kubernetes 内置资源的所有 Client,接下来就可以按照资源的 Group Version Kind 等信息进行调用了。

// 获取 client-setcs, err :=kubernetes.NewForConfig(rc)
iferr!=nil {
panic(err)
}
// 通过 clientset 获取 NodeListnodes, err :=cs.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
iferr!=nil {
panic(err)
}

执行上述代码,可以看到列出了 Node 节点的信息

image.png


rest.Config 配置 Cert 认证

上一个部分我们介绍了 kubeconfig 中的认证信息会被解析成 rest.Config 进行认证,这里我们来介绍一下如何使用 Cert 来构建 rest.Config 进行认证,rest.Config 中包含了 basicAuth,Cert,Token 等认证方式,还包含了 Dial,Transport,请求 QPS、Burst 等配置。


首先,通过 Cert 进行认证一般需要如下信息:

  • APIServer CA 证书(非必需)
  • APIServer 地址
  • 一对证书


这个我们默认使用的 ~/.kube/config 中就有上述所需要的信息, 我们构造如下 rest.Config 对象

其中 Insecure 设置为 true 可以绕过 CA 认证。

certDataBase64 :="your_cert_data"keyDataBase64 :="your_key_data"certData, err :=base64.StdEncoding.DecodeString(certDataBase64)
iferr!=nil {
panic(err)
}
keyData, err :=base64.StdEncoding.DecodeString(keyDataBase64)
iferr!=nil {
panic(err)
}
rc :=&rest.Config{
Host: "https://127.0.0.1:62866",
TLSClientConfig: rest.TLSClientConfig{
Insecure: true,
// CAData: []byte(""), 如果 Insecure: true 未开启,需要 CADataCertData: certData,
KeyData:  keyData,
    },
QPS:   10,
Burst: 100,
}

image.png

构造完成 rest.Config 对象后,后续的内容与第一部分一样了,构建 ClientSet 然后调用查询 Kubneretes Node 资源。


rest.Config 配置 BearerToken 认证

除了上一个部分的 Cert 认证,Token 认证同样也是用的最多的一种方式,Token 认证在原生 httpclient 调用 kubernetes 资源中也比较常用,这里我们来介绍一下。


首先通过 BearerToken 认证我们需要先生成 Token,这里可以先了解一下 RBAC, kubernetes 通过 RBAC 的方式来控制角色对于资源的访问权限,每个角色都会生成该角色所有权限的 Token,这里我们来生成一个对 Node 资源有操作权限的 Token。


首先我们创建一个 clusterrolenode-reader ,并且赋予其 Node 资源的访问权限

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:  name: node-reader
rules:- apiGroups: [""]  resources: ["nodes"]  verbs: ["get","watch","list"]

然后我们在 default namespace 创建一个 serviceAccountnode-reader ,可以理解为一个账户

apiVersion: v1
kind: ServiceAccount
metadata:  name: node-reader
  namespace: default

有个限制权限的角色,有了账户,这时候我们需要有个描述将他们 binding 到一起,这就是 clusterrolebinding,我们创建 node-reader-crb

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:  name: node-reader-crb
  namespace: default
subjects:- kind: ServiceAccount    # 指定绑定到 sa  name: node-reader       # sa 名称是 node-reader  namespace: default      # sa 所在namespace 是 defaultroleRef: # 角色来源  kind: ClusterRole       # 来源于 clusterrole  name: node-reader       # 名称是上述定义的 node-reader  apiGroup: rbac.authorization.k8s.io

此时带有权限的账户我们就创建完了,我们可以在 default 命名空间下查看 node-reader 已经生成了 secret

kubectl get secret | grep node-reader
kubectl get secret <your secret> -o yaml

image.png

其中 token 与 ca 就是我们链接的凭证,接下来我们构建 rest.Config 对象, 通过 BearerToken 即可进行 Token 认证,同样的上述的 CA 可选

tokenBase64 :="your secret token"token, err :=base64.StdEncoding.DecodeString(tokenBase64)
iferr!=nil {
panic(err)
}
rc :=&rest.Config{
Host: "https://127.0.0.1:62866",
TLSClientConfig: rest.TLSClientConfig{
Insecure: true,
// CAData: []byte(""), 如果 Insecure: true 未开启,需要 CAData    },
BearerToken: string(token),
}

获取到了 rest.Config 接下来流程与上述其他几种方式一致



http-client 使用 BearerToken 认证

通过上一个部分我们获取到了可以读取 Nodes 资源的 Token,这个 Token 不仅仅可以用于 client-go,通过 http 请求 Authorization: Bearer 同样可以认证。


我们使用 curl 来访问一下 nodes 资源

token=$(kubectl get secret <your_secret> -o jsonpath={'.data.token'} | base64 -d)curl-H"Authorization: Bearer $token" https://<apiserver地址/api/v1/nodes -k

image.png

curl 可以调用成功,这里我们使用 http-client 同样可以调用,通过设置 request 的 header 以及请求的 tls 认证,我们可以请求到 NodeList,然后通过 json 反序列化到 corev1.NodeList 对象,即可。

url :="https://your_api_server/api/v1/nodes"r, err :=http.NewRequest(http.MethodGet, url, nil)
iferr!=nil {
panic(err)
}
tokenBase64 :="your_token_base64"token, err :=base64.StdEncoding.DecodeString(tokenBase64)
iferr!=nil {
panic(err)
}
r.Header.Set("Authorization", "Bearer "+string(token))
client :=&http.Client{
// 可选, 这里我们跳过 ca 认证Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    },
}
resp, err :=client.Do(r)
iferr!=nil {
panic(err)
}
deferresp.Body.Close()
body, err :=ioutil.ReadAll(resp.Body)
nodes :=&corev1.NodeList{}
iferr :=json.Unmarshal(body, nodes); err!=nil {
panic(err)
}
for_, node :=rangenodes.Items {
fmt.Println(resources.GetNodeFormattedInfo(&node))
}


InClusterConfig 认证

inClusterConfig 在 client-go 的使用中算是比较特殊的一种认证,运行环境在集群内部的话可以使用该配置,其实第一种 kubeconfig 的认证方式中,如果声明的 kubeconfig 路径不合法,同样的会默认使用 `InClusterConfig` 来进行容错。

rc, err :=rest.InClusterConfig()
iferr!=nil {
panic(err)
}

我们可以点进去看一下 InClusterConfig 的实现,可以看到还是非常简单的,同样的它组合的 rest.Config 少不了三元素:apiserver地址,cert/token/basicauth 认证等,ca。

image.png

其中 apiserver 信息从环境变量中获取,token 以及 ca 信息从固定的文件中获取,这些都是 kubenretes 运行起来的 pod 中约定俗成的东西,其中 tokenFile 就是我们之前提到的 serviceaccount 对应的 secret 的值。


我们可以随便找一个 Pod 看一下 kubernetes 在发起一个 Pod 的时候会默认挂载了什么内容,可以看到 token 以及 ca 都会当作 volume 注入到 Pod 中,这里的 token 就是 Pod 所属的 ServiceAccount 对应的 token(默认是 default)。

在不同的版本中(比如 1.20以前 与 1.22以后),这里内容会有差异,但是挂到容器中的东西没有变化

image.png

所以 InClusterConfig 生成的 rest.Config 认证后可以对资源的操作权限,完全取决于 Pod 指定的 ServiceAccount 所绑定的角色有什么权限


到这里,常见的开发中与 APIServer 基本的认证方式介绍的差不多了,感兴趣的同学可以尝试下,欢迎留言讨论!❤️

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
8月前
|
Kubernetes 网络协议 网络安全
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
125 0
|
8月前
|
Kubernetes 开发工具 Docker
微服务实践k8s与dapr开发部署实验(2)状态管理
微服务实践k8s与dapr开发部署实验(2)状态管理
120 3
微服务实践k8s与dapr开发部署实验(2)状态管理
|
3月前
|
Kubernetes Docker 微服务
微服务实践k8s&dapr开发部署实验(1)服务调用(一)
微服务实践k8s&dapr开发部署实验(1)服务调用(一)
58 2
|
3月前
|
Kubernetes JavaScript 前端开发
k8s学习--chart包开发(创建chart包)
k8s学习--chart包开发(创建chart包)
155 1
|
3月前
|
Kubernetes Docker 微服务
微服务实践k8s&dapr开发部署实验(1)服务调用(二)
微服务实践k8s&dapr开发部署实验(1)服务调用(二)
69 0
|
4月前
|
Kubernetes 容器
k8s基于secretRef认证对接rbd块设备
文章介绍了如何在Kubernetes集群中通过secretRef认证方式接入Ceph的RBD块设备,并提供了详细的步骤和配置文件示例。
55 7
|
4月前
|
Kubernetes 容器 Perl
k8s基于keyring文件认证对接rbd块设备
文章介绍了如何在Kubernetes集群中使用Ceph的keyring文件进行认证,并对接RBD块设备,包括使用admin用户和自定义用户两种方式的详细步骤和注意事项。
72 3
|
4月前
|
Kubernetes 安全 API
Kubernetes系统安全-认证(Authentication)
文章主要介绍了Kubernetes系统中的安全认证机制,包括API服务器的访问控制、认证、授权策略和准入控制,以及如何使用kubeconfig文件和创建自定义用户与服务账号。
334 0
Kubernetes系统安全-认证(Authentication)
|
5月前
|
Kubernetes Docker Perl
在K8S中,如果是因为开发写的镜像问题导致pod起不来该怎么排查?
在K8S中,如果是因为开发写的镜像问题导致pod起不来该怎么排查?
|
5月前
|
Kubernetes 网络协议 Python
运维开发.Kubernetes探针与应用
运维开发.Kubernetes探针与应用
179 2