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

简介: 介绍了几种在开发过程中与 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 基本的认证方式介绍的差不多了,感兴趣的同学可以尝试下,欢迎留言讨论!❤️

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
6天前
|
运维 Kubernetes 安全
高分通过Kubernetes/k8s CKS认证考试!
高分通过Kubernetes/k8s CKS认证考试!
|
29天前
|
Kubernetes 网络协议 网络安全
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
21 0
|
7月前
|
Kubernetes 负载均衡 网络协议
【Kubernetes中Gateway和ServiceEntry使用、SDS认证授权等使用】
【Kubernetes中Gateway和ServiceEntry使用、SDS认证授权等使用】
|
29天前
|
Kubernetes 应用服务中间件 nginx
提升CKA认证成功率:Kubernetes Ingress七层代理全攻略!
提升CKA认证成功率:Kubernetes Ingress七层代理全攻略!
22 0
|
3月前
|
Kubernetes 监控 安全
「译文」在 K8S/OpenShift 上开发应用程序的 14 种最佳实践
「译文」在 K8S/OpenShift 上开发应用程序的 14 种最佳实践
|
5月前
|
存储 Kubernetes Cloud Native
云原生|kubernetes|apiserver审计日志的开启
云原生|kubernetes|apiserver审计日志的开启
59 0
|
5月前
|
Kubernetes API 网络架构
k8s学习-CKS真题-启用API Server认证,禁止匿名访问
k8s学习-CKS真题-启用API Server认证,禁止匿名访问
73 0
|
5月前
|
Kubernetes Cloud Native 数据安全/隐私保护
k8s 认证和权限控制
k8s 认证和权限控制
|
7月前
|
Kubernetes Cloud Native Java
猫头虎博主赠书一期:《Kubernetes原生微服务开发》
猫头虎博主赠书一期:《Kubernetes原生微服务开发》
60 0
|
9月前
|
存储 缓存 Kubernetes
Kubernetes 认证机制学习
# 引言 Kubernetes API Server 组件是 Kubernetes 具有网关性质的组件,它是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。 ```bash iZj6ccqyhc7xduup9vl8mvZ :: ~ »

推荐镜像

更多