api结构设计&认证授权&安全上下文

K8s APIServer与安全管控完整进阶笔记

一、API组织形式

APIServer是Kubernetes集群的统一控制入口,核心职责是对外提供API接口,所有集群组件、客户端与集群的交互都必须通过APIServer完成,是集群控制平面的核心枢纽。

1.1 APIServer三大核心功能

APIServer的能力可以分为三个核心维度,分别对接底层存储、外部客户端与集群安全管控。

第一,对底层组件的支撑能力。APIServer向下封装了对etcd数据库的所有操作,集群所有资源的增删改查都通过APIServer落地到etcd,禁止其他组件直接操作etcd;同时对上为所有集群组件提供了list-watch机制,各组件可以通过监听的方式实时感知资源变化,实现事件驱动的协作模式。

第二,对客户端的接口能力。APIServer对客户端统一提供API接口,总体基于RESTful风格设计,所有资源操作都遵循标准的HTTP请求规范。典型的API请求路径格式如下:

https://集群IP:端口/api/apps/v1/namespace/default/pods

第三,对访问安全的管控能力。所有客户端访问APIServer都需要经过一系列权限校验流程,共分为三层校验:

1、检查凭证:验证用户是否持有合法身份凭证,确认账号身份的合法性,凭证代表的是用户的身份信息。

2、检验授权:验证用户是否对请求中指定的API资源拥有对应的操作权限。

3、准入控制器:请求会经过准入控制器处理,包含两类Webhook:变更类mutate与校验类validate。

1.2 API设计的核心挑战与解决思路

Kubernetes需要持续迭代新功能、新增资源类型,同时还要兼容旧版本API,因此API设计面临两大核心挑战:

1、新增的API如何能够方便的扩展与演进,支持新功能快速迭代;

2、新老API并存场景下,如何能做到彼此互不影响,某个特定的资源既有稳定版也有实验版可用,可以非常方便的进行切换。

针对以上挑战,核心解决思路可以总结为一句话:同一种资源分配给不同的API路径,通过路径区分版本。例如Deployment资源可以同时存在两个版本路径:

apps/v1/deployment  稳定的/老的
v1beta1/v1/deployment  新的

1.3 API层级组织架构

Kubernetes API在RESTful风格的基础上,做了分组+多版本的设计,这是其API设计的核心亮点,既支持灵活扩展,又兼顾版本兼容。

API整体采用分层组织的结构,从顶层到具体资源依次为:

1、顶级路径大类,共四类:

/api  核心组
/apis/ 扩展组,核心组以外的资源
/metrics 性能指标,获取apiserver的性能指标数据
/healthz 用于检查api server 的健康状态

2、细分组名:就是kubectl explain看到的group,全局唯一不会重复。非核心组下的资源分组,细分组名全局唯一,编写资源清单时指定的就是细分组名,例如apps、batch、extensions。

apiVersion: apps/v1

3、具体版本:每个分组下可以同时存在多个版本,例如v1、v2alpha1。

4、具体资源:对应各类Kubernetes资源对象,例如job、namespace。

需要特别强调,根据是否属于核心组,API完整路径分为两种构成形式:

非核心组API构成:/顶级大类/细分组名/版本/namespaces/具体的名称空间/资源类型

核心组API构成:/api/版本/namespaces/具体的名称空间/资源类型

# 完整api
https://apiserver_ip:port/apis/apps/v1/namespaces/default/deployment

# 完整api的组成
- /apis/apps/v1/namespaces/default/deployment 这一部分由yaml文件给出
- https://apiserver_ip:port/  这一部分由/root/.kube/config文件给出

注意:某些资源没有细分组,比如service, pod, secret, namespace。上面的完整API表达是非核心组的,如果是核心组的资源,没有细分组,直接对接版本。

apiVersion: v1
kind: Pod
metadata:
spec:

查看API层级的命令:

# 查看顶级大类
kubectl get --raw / 

# 查看某个顶级大类的细分组
kubectl get --raw /apis/ | python -m json.tool
kubectl get --raw /api/ | python -m json.tool

# 查看细分组里的版本
kubectl get --raw /apis/apps | python -m json.tool
kubectl get --raw /api/v1 | python -m json.tool

# 查看版本下的资源
kubectl get --raw /apis/apps/v1 | python -m json.tool

API是通用入口,除了kubectl之外,也可以用curl等工具直接访问K8s资源,需要先启动代理:

kubectl proxy
curl http://127.0.0.1:8081/apis/v1

1.4 API版本分级与演进

同一资源的不同版本按照稳定性分为三个级别,新特性会按照固定路径逐步迭代成熟:

1、Alpha版:尝鲜版本,新特性一定最先出现在这里,功能不稳定、可能随时调整或删除,仅用于测试验证。

2、Beta版:稳定性提高版本,相对Alpha版更稳定一些,经过了初步验证,但仍可能存在变更,用于灰度使用。

3、稳定级别:正式发布的稳定版本,比如v1版本表示是最稳定版,功能向后兼容,可用于生产环境。

版本演进规律可以总结为:出生在Alpha版,成长在Beta版,毕业在v1稳定版。

二、认证、授权与准入控制

承接APIServer的安全管控能力,Kubernetes通过认证、授权、准入控制三层机制,实现对集群访问的全链路安全管控,核心基于RBAC权限模型实现精细化权限管理。

2.1 权限控制基础维度(储备知识)

授权的三要素:Kubernetes的权限管控围绕三个核心维度展开,限制用户对某个API下的某种类型资源拥有某种权限:

1、API分组(apiGroups):对应API组织中的分组,Kubernetes API是分组、多版本的结构,权限按分组进行隔离。

2、资源类型(resource type / resources):对应各类Kubernetes资源对象,常见的资源类型包括Pods、Configmaps、Deployments、Nodes、Secrets、Namespaces等。

3、具体的操作(verbs):对资源可以执行的具体动作,常见操作包括:

create
delete
update、edit、patch
get、list&watch
exec

2.2 用户实体与身份凭证

权限管控的主体是用户实体(subject),也就是各类账号。Kubernetes中每种账号都只是代号而已,必须关联上凭证才能使用,集群确认用户身份用的是凭证,而不是账号名本身。

凭证中包含用户的身份信息,例如证书、Token、归属的名称空间等。

账号分为两大类,分别对应不同的使用场景:

1、用户名、用户组:给人用的,用于运维人员、开发人员操作集群的场景。

2、ServiceAccount(服务账号):给Pod用的,让Pod内的运行环境拥有对Kubernetes的操作权限,实现Pod与集群的安全交互。

2.3 RBAC权限控制机制

RBAC是Kubernetes权限管理的核心机制,核心逻辑是:将账号与角色进行绑定,角色中定义具体的权限规则,实现身份与权限的解耦。简单来说,账号相当于人,角色相当于职位,绑定相当于任职。

2.3.1 两类角色

角色是权限规则的载体,根据可见范围分为两类:

1、Role(私有角色):此类角色只能被单个名称空间看到并加以引用,即该类角色是私有的,属于命名空间级资源。

2、ClusterRole(公共角色):此类角色能够被所有名称空间看到并加以引用,即该类角色是公有的,属于集群级资源。

需要特别强调:角色本身声明的命名空间,只是声明自己这个角色能够被一个还是所有名称空间看到;至于角色下的rules规则能在哪个名称空间执行,取决于绑定方式。

rules规则集:定义对哪些api groups里的哪些resource资源拥有什么操作权限,完整Role示例:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""] # "" 表示核心API组
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
  resourceNames: [] # 可选,限制到具体资源名称,空代表所有

2.3.2 三种绑定方式

绑定操作的作用是将账号与角色关联起来,使账号获得角色对应的权限,共分为三种绑定场景:

1、RoleBinding绑定Role:授予的权限只能在一个名称空间中使用,对应私有角色的命名空间内授权。

2、ClusterRoleBinding绑定ClusterRole:授予的权限能在所有名称空间中使用,对应集群级公共角色的全局授权。

3、RoleBinding绑定ClusterRole:应对的场景是,拿到一个公共的权限声明,然后用RoleBinding绑定死在一个名称空间,实现公共权限模板的单命名空间复用。

注意:Role + ClusterRoleBinding 是不允许的,ClusterRoleBinding 不能引用命名空间级 Role。

2.4 APIServer完整请求验证流程

所有发往APIServer的请求,都会依次经过三层安全校验,全部通过后才会被真正执行:

1、认证(Authentication):基于凭证确认用户身份,校验凭证的合法性,身份不合法直接拒绝请求。

2、授权(Authorization):验证该凭证关联的权限,检查是否拥有对目标apiGroup、目标resources的对应verbs操作权限,权限不足直接拒绝。

3、准入控制(Admission Controllers):触发两类Webhook处理:mutate变更类Webhook会修改请求内容,补充默认配置;validate校验类Webhook会校验请求合规性,不符合规则则拒绝请求。

2.5 RBAC实战案例

2.5.1 用户实体为普通用户/用户组

实验1:RoleBinding绑定Role类型角色,通过自签证书实现自定义用户。

# 1、创建私钥文件,得到egon.key
(umask 077;openssl genrsa -out egon.key 2048) 

# 2、使用egon.key创建证书签名请求文件,得到egon.csr
# CN表示用户名,O表示组
openssl req -new -key egon.key -out egon.csr -subj "/CN=egon/O=techstar"

# 3、使用k8s的CA证书批准上述证书请求,k8s的CA证书位于/etc/kubernetes/pki/目录下
openssl x509 -req -in egon.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out egon.crt -days 500

# 二进制安装的集群,CA证书路径
# /opt/kubernetes/ssl/ca.pem 对应ca.crt
# /opt/kubernetes/ssl/ca-key.pem 对应ca.key

# 4、查看证书详情
openssl x509 -in egon.crt -text -noout 

# 5、使用证书和私钥在集群中创建新的凭证和上下文
kubectl config set-credentials egon --client-certificate=./egon.crt --client-key=./egon.key --embed-certs=true 

# 6、为用户设置新的Context,指定特定名称空间
kubectl config set-context egon-context --cluster=kubernetes --namespace=kube-system --user=egon

创建Role角色:

# dry-run模式生成yaml模板
kubectl create role role1 --verb=get,list,watch --resource=pods --dry-run=client -o yaml > role-demo.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role1
  namespace: kube-system
rules:
- apiGroups: ['', 'apps']
  # pod属于核心API组,使用空字符串;Deployment、ReplicaSet属于apps api组
  resources: ['deployments', 'replicasets', 'pods']
  verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
  # 也可以直接使用 ['*'] 代表全部权限
# 创建role
kubectl apply -f role-demo.yaml
kubectl get role -n kube-system

RoleBinding角色绑定:

# dry-run模式生成yaml模板
kubectl create rolebinding rolebinding1 --role=role1 --user=egon --dry-run=client -o yaml > rolebinding-demo.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: kube-system
  name: rolebinding1
subjects: # 用户实体,授权对象
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: egon # 证书创建的用户名
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: role1 # 关联的角色名称
kubectl apply -f rolebinding-demo.yaml

kubectl上下文相关命令与优先级:

优先级最高:kubectl --kubeconfig=/xx.yaml get pod

优先级其次:export KUBECONFIG=/xx.yaml,默认为/etc/kubernetes/admin.conf

优先级最低:~/.kube/config

kubectl config view
kubectl config get-contexts
kubectl config use-context kubernetes-admin@kubernetes
kubectl config delete-context xxx
kubectl config view -o jsonpath='{.users[*].name}'
kubectl config unset users.<user-name>

# 导出配置给他人使用
kubectl config view --minify --flatten --context=egon-context > egon-context-kubeconfig.yaml

总结:有了上下文之后,用户只需要安装kubectl就可以访问k8s了。

实验2:用户通过ClusterRoleBinding绑定ClusterRole类型角色。

# 生成集群角色模板
kubectl create clusterrole cluster-read --verb=get,list,watch --resource=pods --dry-run=client -o yaml > clusterrole-demo.yaml

# 创建集群角色
kubectl apply -f clusterrole-demo.yaml

# 删除之前的命名空间级绑定
kubectl delete rolebinding rolebinding1 -n kube-system

# 生成集群角色绑定模板
kubectl create clusterrolebinding egon-read-all-pods --clusterrole=cluster-read --user=egon --dry-run=client -o yaml > clusterrolebinding-demo.yaml

# 应用清单完成绑定
kubectl apply -f clusterrolebinding-demo.yaml

绑定完成后,用户 egon 在所有命名空间下,都拥有 pods 的 get、list、watch 权限。

针对用户账号的规则总结:账号拥有的权限取决于两种绑定方式,一个账号有单个名称空间级别的权限,还是全局名称空间下的权限由绑定方式决定;账号本身可以在哪里看见,取决于角色。

实验3:用户通过RoleBinding绑定ClusterRole类型角色。

# 删除全局绑定
kubectl delete clusterrolebinding egon-read-all-pods

# 生成RoleBinding绑定ClusterRole的模板
kubectl create rolebinding rolebinding2 --clusterrole=cluster-read --user=egon --dry-run=client -o yaml > test.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rolebinding2
  namespace: kube-system # 关键:权限仅限制在此命名空间
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole # 引用集群角色ClusterRole
  name: cluster-read
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: egon
kubectl apply -f test.yaml

2.5.2 用户实体为ServiceAccount

实验4:RoleBinding绑定Role角色。ServiceAccount不需要配置上下文,只能在某个名称空间下可见,不能跨名称空间关联ServiceAccount。

# 创建服务账号
kubectl create sa egon-sa -n kube-system
# 创建命名空间级 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role3
  namespace: kube-system
rules:
- apiGroups: [''] # 核心组("")
  resources: ['pods']
  verbs: ['get', 'watch', 'list']
- apiGroups: ['apps']
  resources: ['deployments']
  verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']

# 创建 RoleBinding,绑定 Role 与 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rolebinding3
  namespace: kube-system
subjects:
- kind: ServiceAccount
  name: egon-sa
  namespace: kube-system
roleRef:
  kind: Role
  name: role3
  apiGroup: rbac.authorization.k8s.io

验证方式1:kubectl auth can-i 预先校验权限,快速排查问题。

kubectl auth can-i <动词> <资源> --as=system:serviceaccount:<命名空间>:<sa名称>

kubectl auth can-i get pods -n default --as=system:serviceaccount:kube-system:egon-sa
# 输出 no

kubectl auth can-i get pods -n kube-system --as=system:serviceaccount:kube-system:egon-sa
# 输出 yes

# 模拟普通用户egon查看pod权限
kubectl auth can-i list pods --as=egon -n kube-system

验证方式2:Pod中通过serviceAccountName指定ServiceAccount,启动后自动挂载对应Token,容器内程序用Token访问APIServer验证权限。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test1
  namespace: kube-system # 必须与SA、权限绑定的命名空间保持一致
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test1
  template:
    metadata:
      labels:
        app: test1
    spec:
      serviceAccountName: egon-sa # 指定Pod使用的服务账号
      containers:
      - image: centos
        name: centos
        command: ["/bin/sh","-c","sleep 10000"]
kubectl -n kube-system exec -ti test1-5478bb8944-52f6s sh
# Pod指定serviceAccountName后,k8s自动把服务账号凭证挂载至容器内固定路径
cat /var/run/secrets/kubernetes.io/serviceaccount/token

# 将token赋值给变量
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# 使用token直接调用K8s APIServer接口验证权限
curl --header "Authorization: Bearer $TOKEN" --insecure \
https://kubernetes.default.svc/api/v1/namespaces/default/pods

curl --header "Authorization: Bearer $TOKEN" --insecure \
https://kubernetes.default.svc/apis/apps/v1/namespaces/kube-system/deployments

注意:如果指定的ServiceAccount不存在,Pod直接启动失败。

实验5:ServiceAccount通过ClusterRoleBinding绑定ClusterRole。

# 创建ServiceAccount
kubectl create sa egon-sa2 -n kube-system

# 查看集群内置ClusterRole cluster-admin
kubectl get clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
# 创建ClusterRoleBinding,绑定SA与cluster-admin
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: egon-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
  name: egon-sa2
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
# 权限验证
kubectl auth can-i get pods -n default --as=system:serviceaccount:kube-system:egon-sa2
# 返回 yes

kubectl auth can-i get pods -n kube-system --as=system:serviceaccount:kube-system:egon-sa2
# 返回 yes

RBAC绑定规则总结记忆:

Role + RoleBinding:权限限制在绑定的namespace

ClusterRole + ClusterRoleBinding:权限全局,全部namespace

ClusterRole + RoleBinding:复用集群权限模板,但仅在RoleBinding所在namespace生效

Role + ClusterRoleBinding:不允许,ClusterRoleBinding不能引用命名空间级Role。

三、Security Context安全上下文

Security Context是Pod与容器层面的安全配置机制,属于集群安全体系在工作负载侧的落地,用于定义Pod或容器的运行权限、安全属性,从负载层面限制容器的行为,降低安全风险。

3.1 Linux Capability基础

Linux内核2.2之前,进程的权限只有root和非root,root永远拥有一切权限,非root不能执行任何特权操作。但很多时候,需要非root用户能有部分特权。

于是诞生了suid,可以让用户拿到文件属主的身份,但这种机制很可能成为漏洞被利用。

之后诞生了Linux capability机制,把root权限的特权做了细分,可以对细分后的权限单独授权,实现基础权限是普通的,但能够拥有部分特权。

宿主机上的capability设置命令:

getcap /bin/ping
su - egon -c "ping -c1 www.baidu.com"

setcap cap_net_admin,cap_net_raw+p /bin/ping
getcap /bin/ping

ping -c1 www.baidu.com
# +p代表增加,-p代表减少 permitted

容器内的权限说明:容器内只有rootfs,没有内核,宿主机上的内核改动会影响所有容器。容器内的root账号并不等同于宿主机的root账号,容器内部的root只拥有宿主机上root账号的部分权限,而非全部。

如果想让容器内root账号拥有所有宿主机的root权限,可以开启特权模式:

docker run --privileged

docker container run -it --rm --cap-add NET_ADMIN --name test test_cap:v1.0 sh
# 也可以用--cap-drop去掉一些特权

nerdctl run -it --rm busybox /bin/sh
# 默认无法创建虚拟网卡
nerdctl run -it --rm --cap-add=NET_ADMIN busybox /bin/sh
# 添加NET_ADMIN能力后可以创建虚拟网卡
ip link add temp_eth0 type dummy

K8s中配置Linux Capabilities的示例:

apiVersion: v1
kind: Pod
metadata:
  name: cpb-demo
spec:
  containers:
  - name: cpb
    image: busybox
    args:
    - sleep
    - '3600'
    securityContext:
      capabilities:
        add:
        - NET_ADMIN   # 新增网络管理能力,允许创建虚拟网卡、修改网络接口
        drop:
        - KILL        # 删除默认的KILL能力,容器内无法发送kill信号
kubectl exec -it cpb-demo /bin/sh
ip link add temp-dummy0 type dummy

3.2 核心作用

安全上下文是K8s用于定义pod或容器安全设置的配置,可以对容器的运行身份、权限范围、系统调用能力等进行约束,避免容器拥有过高权限、防止容器逃逸、减少攻击面,保障节点与集群的整体安全。

3.3 常用配置项

安全上下文可以配置在Pod级别和容器级别,Pod级别配置对Pod内所有容器生效,容器级别仅对单个容器生效,核心配置项包括:

1、运行用户与用户组:指定容器运行时使用的UID、GID,限制容器运行身份,避免默认使用root用户。

2、特权模式控制:禁止容器运行在特权模式,防止容器获得节点的root权限。

3、只读根文件系统:将容器的根文件系统设置为只读,防止容器内程序修改系统文件。

4、权限提升限制:禁止容器进程获得比启动时更高的权限。

5、Linux能力限制:按需添加内核能力,默认移除所有不必要的能力,缩小权限范围。

6、Seccomp配置:限制容器可以调用的系统调用,减少内核层面的攻击面。

3.4 配置实验案例

实验1:Pod级别的security context

直接限制容器中的用户身份,以非root启动,Pod级配置对所有容器生效,fsGroup仅Pod级别支持。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-pod-demo
spec:
  securityContext: # pod级别配置,作用于pod内所有容器
    runAsUser: 1000      # pod内全部容器进程以UID=1000运行
    runAsGroup: 3000     # pod内全部容器进程以GID=3000运行;省略默认GID为0(root组)
    fsGroup: 2000        # pod挂载的volume,文件归属组ID修改为2000
  volumes:
  - name: my-volume
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: ['sh', '-c', 'sleep 60m']
    volumeMounts:
    - name: my-volume
      mountPath: /pod/demo # 目录内文件GID受fsGroup控制
    securityContext: # 容器级配置
      allowPrivilegeEscalation: false # 禁止进程提权

创建完成后可进入容器执行id命令查看用户身份,验证配置生效。

实验2:Container级别的security context

容器内securityContext优先级高于Pod级别;容器级别配置不会影响volume(fsGroup仅Pod级别生效)。

apiVersion: v1
kind: Pod
metadata:
  name: security-context-container-demo
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: demo1
    image: busybox
    command: ['sh','-c','sleep 60m']
    securityContext:
      runAsUser: 2000 # 当前容器覆盖pod级别配置,以2000为准
      allowPrivilegeEscalation: false
  - name: demo2
    image: busybox
    command: ['sh','-c','sleep 60m']
    # 无容器级配置,沿用pod级 runAsUser:1000

创建完成后可分别进入两个容器执行id命令,验证不同容器的用户身份差异。

实验3:特权模式

特权模式只能在container级别设置,Pod级别不支持。

典型场景:部署Elasticsearch,容器内需要修改内核sysctl参数;默认容器文件系统只读,开启特权模式后拥有宿主机内核操作权限。

apiVersion: v1
kind: Pod
metadata:
  name: demo3
spec:
  containers:
  - name: demo3
    image: busybox
    command: ['sh','-c','sleep 60m']
    securityContext:
      privileged: true # 开启特权模式

安全提醒:特权容器权限极高,生产环境尽量少使用。

posted @ 2026-07-16 21:19  cuckooyang  阅读(14)  评论(0)    收藏  举报