目录

技术背景

什么是admission controller?

应用场景

具体实现

代码结构

签发证书

创建对象

测试api


技术背景

什么是admission controller?

admission controller是一段代码,它会在请求通过认证和授权之后、对象被持久化之前拦截到达 API 服务器的请求。控制器编译进 kube-apiserver 可执行文件,并且只能由集群管理员配置。

这有点类似于插件的概念,官方提供了一系列的插件来帮助我们实现一些api层面的简单处理:

使用准入控制器 | Kubernetes

我们也可以通过自己编写一段代码二次开发来实现更为高级更为复杂的需求,

官方有具体的实例,该实例的用途是

only allow pods to pull images from specific registry.

即仅允许pod从指定的镜像仓库拉取image,否则apiserver将会拒绝本次请求。

应用场景

从运维角度,我们也可以借此约束非集群管理员的某些行为来实现安全运维的目的,或者通过admission controller的另外一个用途:修改请求,通过自动为k8s声明式的api自动注入配置规则,为非集群管理员的一些非约性的apply行为解绑。

举几个简单场景:

1.开发人员可能只有对应namespace下deployment的create,upduate,delete权限,,我们通过自动注入私仓的dockerconfig.json的secret对象作为imagePullSecret,来让开发人员尽可能少关注与应用本身描述无关的,诸如此类的配置选项。

2.传统微服务架构依赖注册中心,因此在pod终止时如何从注册中心下线来实现优雅停机成为了问题,为了实现低代码侵入,将这部分问题的解决下沉到运维层面,我们可以使用k8s提供prestop机制来实现pod在收到sigterm信号之前就处理掉这个问题。

具体实现

接下来我们来看具体实现

代码结构

.
├── go.mod
├── go.sum
├── lib
│   ├── config.go //用于配置tls证书和密钥
│   ├── convert.go //将具体的error转换成webhook回调返回的对象
│   ├── pods.go //输入请求对象,return一个返回对象,需要我们在其中实现判断逻辑
│   └── schema.go //存放了用于将byte转换成请求对象的反序列化器
├── main.go //http server并配置tls,反序列化后调用pods.go获得返回对象

其中的大部分代码都可以在kubernetes/main.go at release-1.21 · kubernetes/kubernetes · GitHub

中获取到。

main.go中httpserver的handler:

http.HandleFunc("/pods", func(writer http.ResponseWriter, request *http.Request) {var body []byteif request.Body != nil {if data, err := ioutil.ReadAll(request.Body); err == nil {body = data}}//第二步reqAdmissionReview := v1.AdmissionReview{} //请求resAdmissionReview := v1.AdmissionReview{  //响应 ---完整的对象在 https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/extensible-admission-controllers/#responseTypeMeta: metav1.TypeMeta{Kind:       "AdmissionReview",APIVersion: "admission.k8s.io/v1",},}//第三步,把body decode成对象deserializer := lib.Codecs.UniversalDeserializer()if _, _, err := deserializer.Decode(body, nil, &reqAdmissionReview); err != nil {resAdmissionReview.Response = lib.ToV1AdmissionResponse(err)} else {resAdmissionReview.Response = lib.AdmitPods(reqAdmissionReview)}resAdmissionReview.Response.UID = reqAdmissionReview.Request.UIDmarshal, _ := json.Marshal(resAdmissionReview)writer.Write(marshal)
})

签发证书

完成编码后,我们需要生成假证书,可以通过cfssl工具生成,参考下面这篇文章:

手把手-安装-cfssl - 光速狼 - 博客园

这里说下大概的步骤——

1.创建ca配置文件 (ca-config.json)

"ca-config.json":可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;

"signing":表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE

{"signing": {"default": {"expiry": "87600h"},"profiles": {"server": {"usages": ["signing"],"expiry": "87600h"}}}
}

2.创建ca证书签名(ca-csr.json)

{"CN": "Kubernetes", //SelfSignedCA"key": {"algo": "rsa","size": 2048},"names": [{"C": "zh","L": "bj","O": "bj","OU": "CA"}]
}

3.生成ca证书和私钥

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

——生成ca私钥 ca-key.pem 和 ca公钥ca.pem

4.创建服务端证书签名(server-csr.json)

——这个服务端可以是etcd,docker,apiserver等

{"CN": "admission", //etcd"key": {"algo": "rsa","size": 2048},"names": [{"C": "zh","L": "bj","O": "bj","OU": "bj"}]
}

5.生成服务端证书server.pem和私钥server-key.pem,也就是签发证书

cfssl gencert \-ca=ca.pem \-ca-key=ca-key.pem \-config=ca-config.json \-hostname=myhook.ops.svc \-profile=server \server-csr.json | cfssljson -bare server

注意hostname与service的fqdn域名相对应。

创建对象

1.cat ca.pem | base64

获取ca公钥填入admission webhook资源yaml的cabundle

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:name: myhook
webhooks:- clientConfig:caBundle: |LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURoakNDQW02Z0F3SUJBZ0lVZU41ZU5td29tRXZMREFvSUpnYjhTVUZUandFd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNlbWd4Q3pBSkJnTlZCQWNUQW1KcU1Rc3dDUVlEVlFRS0V3SmlhakVMTUFrRwpBMVVFQ3hNQ1EwRXhFekFSQmdOVkJBTVRDa3QxWW1WeWJtVjBaWE13SGhjTk1qSXdPREF5TURJeU56QXdXaGNOCk1qY3dPREF4TURJeU56QXdXakJKTVFzd0NRWURWUVFHRXdKNmFERUxNQWtHQTFVRUJ4TUNZbW94Q3pBSkJnTlYKQkFvVEFtSnFNUXN3Q1FZRFZRUUxFd0pEUVRFVE1CRUdBMVVFQXhNS1MzVmlaWEp1WlhSbGN6Q0NBU0l3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFMVk5rY1pUcWpkdHJDYzh3YkhqMXlVejhucHl2QkVECkJxNmFyZElOU2NBRldyT0wzNVRJVmNOZnFQRWlhbkxhbjJsbkFGdWp1UE9WaFhYOS9CMzhoSkU1QjJBeXhTYXgKYzdxM1lUWnpYS0xsQ2c1UVc0Wlk2SVFVdTdGbHZ2T0RIRjEyNTl6OEd4dGFsdjQ1Z0pYSDV5Nnp4WlNBMEJxZgpURnR3bHFRb1krMVN3QkhtZ2lBRWpXekxZV0cydHVRRndkZW9YR2tWd0Y2dkwzM1NoM09yb1ZHQTQ2aVRiMUdKCkgxenBKNWpVYlpZbFAxSFVta0R4dnF1NDJJaGJnN1lPNWIvUktMaWpvVVJza0p2d1dzb3dvVU03dU1GSllrdUsKdFc3MlJ3S1UwNnoxN20xUmxZZlpzWkt2NU9ONjd3RlR4M0plQUlTcno1OHBKN2cwaUwySTh0MENBd0VBQWFObQpNR1F3RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUl3SFFZRFZSME9CQllFCkZPWWRwMzJkL09VanM0VkpMMDZyRTJtZlAwZkZNQjhHQTFVZEl3UVlNQmFBRk9ZZHAzMmQvT1VqczRWSkwwNnIKRTJtZlAwZkZNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUFrYXVNT01OQU9DTUN2QmFqR0JhYUhXRFpBekdvTAo5cHJhWWRDWEwvQkw4Z25qczJsZmRjcWZZclpUM3pYQ3IzNXlmUEJSZitNUFRtKzdlQkF5bHlCeWlNK0xPcDRMCkM5MEVWT2NUM0hxK2EvUlBURjJEbmxBemwva1JkMGN3RFM2WTdLUGovQWxlc3FzVUNQVXVLbVlnb3hadmNqa04KU1NYVEs4VWk2Vncyekd2MzU5bFR0QjA3Y3paZjhYR09xeEZpQi9tUUVERldOOGxxYkF3b2k1NHVZbHlsaXowcApyWFp0cHhyN0tza0dHb08rcTVEdTRwVnZUNlFUakd6NzNlYktacnRieURsbzBnbDdCZmxPTHBEM1d0WWw0b3N4CmlYZ3NYaVNxdmNUcEkrbGJLNUQ3dlMrS1pybXpmaFozWWRWdW9mR083UCtsMFUwKzZaUytjMi9vCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0Kservice:name: myhooknamespace: opspath: /podsfailurePolicy: FailsideEffects: NoneOnDryRunname: myhook.xxx.comadmissionReviewVersions: ["v1", "v1beta1"]namespaceSelector: matchExpressions:- key: envoperator: Invalues: ["prod", "pre", "dev", "test"]rules:- apiGroups:   [""]apiVersions: ["v1"]operations:  ["CREATE"]resources:   ["pods"]

——service对应webhook的service,namespaceSelector通过label限定作用域命名空间,rules匹配GVK。

2.把上一节生成服务端私钥和证书创建成tls类型的secret

kubectl create secret tls myhook --cert=server.pem --key=server-key.pem  -n ops

3.在我们的webhook代码里添加tls认证

tlsConfig := lib.Config{CertFile: "/etc/webhook/certs/tls.crt",KeyFile:  "/etc/webhook/certs/tls.key",
}server := http.Server{Addr:      ":443",TLSConfig: lib.ConfigTLS(tlsConfig),
}err := server.ListenAndServeTLS("", "")
if err != nil {panic(err)
}

通过交叉编译我们得到myhook可执行文件。

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

4. 创建webhook deployment(仅测试用,需要指定节点)

apiVersion: apps/v1
kind: Deployment
metadata:name: myhooknamespace: ops
spec:replicas: 1selector:matchLabels:app: myhooktemplate:metadata:labels:app: myhookspec:nodeName: k8s1containers:- name: myhookimage: alpine:3.12imagePullPolicy: IfNotPresentcommand: ["/app/myhook"]volumeMounts:- name: hooktlsmountPath: /etc/webhook/certsreadOnly: true- name: appmountPath: /appports:- containerPort: 443volumes:- name: apphostPath:path: /root/app- name: hooktlssecret:secretName: myhook
---
apiVersion: v1
kind: Service
metadata:name: myhooknamespace: opslabels:app: myhook
spec:type: ClusterIPports:- port: 443targetPort: 443selector:app: myhook

——把服务端证书和私钥通过secret的方式挂在到容器里,并通过443端口对外提供服务。

测试api

最后可以通过apply一个新的pod来测试是否通过webhook的验证规则,如果未通过,则不会继续持久化对象。如果通过,则会触发对apiserver的回调。

% kubectl apply -f newpod.yaml
Error from server: error when creating "newpod.yaml": admission webhook "myhook.xxx.com" denied the request: container's image must be from private hub.

Kubernetes 开发【1】——webhook 实现 API Server 请求拦截和修改相关推荐

  1. Kubernetes集群安全:Api Server认证

    全栈工程师开发手册 (作者:栾鹏) 架构系列文章 kube api serverd 启动参数解析 https://kubernetes.io/docs/reference/command-line-t ...

  2. Kubernetes开发(4)-webhook 实现拦截请求

    什么是webhook Kubernetes 通过rbac进行权限控制,实现了哪些account对哪些资源具有哪些权限的控制,但它并不是万能的, 因为rbac控制的操作权限类型是有限的,需要再进行一些细 ...

  3. Kubernetes控制平面组件:API Server

    文章目录 一.API Server简介 1.访问控制流程概览 2.访问控制细节 二.认证 1.认证插件(附x509认证实现原理) 2.基于webhook的认证服务集成 (1)构建符合Kubernete ...

  4. 深度剖析Kubernetes API Server三部曲 - part 2

    欢迎来到深入学习Kubernetes API Server的系列文章的第二部分.在上一部分中我们对APIserver总体,相关术语及request请求流进行探讨说明.在本部分文章中,我们主要聚焦于探究 ...

  5. 资深专家深度剖析Kubernetes API Server第2章(共3章)

    欢迎来到深入学习Kubernetes API Server的系列文章的第二部分.在上一部分中我们对APIserver总体,相关术语及request请求流进行探讨说明.在本部分文章中,我们主要聚焦于探究 ...

  6. k8s 组件介绍-API Server

    API Server简介 k8s API Server提供了k8s各类资源对象(pod,RC,Service等)的增删改查及watch等HTTP Rest接口,是整个系统的数据总线和数据中心. kub ...

  7. K8S Api Server认证

    目录 认证类型 基于CA证书的双向认证 apiserver端配置 生成客户端私钥和证书 master核心组件与apiserver的认证方式 HTTP Token认证 HTTP Basic认证 kube ...

  8. 关于 Kubernetes中API Server授权(RBAC)管理的一些笔记

    写在前面 学习K8s涉及,整理笔记记忆 博文偏实战,内容涉及: k8s中API Server的授权策略简述 RBAC授权策略中涉及到的资源对象创建删除 Role ClusterRole RoleBin ...

  9. 资深专家深度剖析Kubernetes API Server第1章(共3章)

    欢迎来到深入学习Kubernetes API Server的系列文章,在本系列文章中我们将深入的探究Kubernetes API Server的相关实现.如果你对Kubernetes的内部实现机制比较 ...

最新文章

  1. Github | NumPy手写全部主流机器学习模型
  2. 【控制】《现代控制理论》谢克明老师-第7章-线性系统的状态估计
  3. VTK:可视化算法之CutWithScalars
  4. [Python图像处理] 十五.图像的灰度线性变换
  5. 全国大学生数学建模2014年A题嫦娥三号软着陆轨道设计与控制策略论文与代码
  6. C++中cin、cin.get()、cin.getline()、getline()等函数的用法
  7. 微软出品,文科生也能学得懂的Python免费入门视频
  8. Java案例:生成指定目录下某种类型文件的列表
  9. (40)System Verilog线程停止(disable fork)
  10. AudioScheduledSourceNode
  11. Unity3D基础38:角色控制器组件
  12. 在windows平台上编写的python程序无法在_【判断题】在Windows平台上编写的Python程序无法在Unix平台运行。...
  13. 今年护网蓝队防御具体实施方案
  14. ionicapp开场动画_动画开场类型
  15. 强连通分量分解详解 超级详细
  16. FAT12文件系统 理解
  17. ddn高性能服务器,DDN是什么,DDN专线的优势详解
  18. xmind怎么导出甘特图_张兵导图:xmind如何绘制甘特图?
  19. 怎样用计算机打出Abc,智能ABC输入法如何安装?win7智能ABC输入法安装步骤
  20. android简单的颜色选择器制作

热门文章

  1. Ubuntu下设置键盘背光灯
  2. 昆石VOS2009/VOS3000 2.1.7.03 Web 接口说明
  3. 按键精灵 android版运行异常,按键精灵安卓版 tap、touch命令 不好用的解决办法!...
  4. luatos的一些说明
  5. Win10系统重装过程(一键装机)
  6. 高通骁龙875夺安卓处理器桂冠,但外挂5G基带成为它的弊病
  7. 工作的工资是怎么算的
  8. 【Win32 API】GetPixel函数返回的颜色值不正确
  9. 如何在WPS中打开多个窗口
  10. struct结构体里能放函数吗?