问题
工作中遇到应用pod如下的报错,业务容器无法正常工作
Warning FailedMount 22s (x8 over 86s) kubelet, nodexxx MountVolume.SetUp failed for volume "xxx-conf-volume" : configmaps "abc-jobmanager-conf-map" is forbidden: User "system:node:nodexxx" cannot get resource "configmaps" in API group "" in the namespace "xxx-operator": no relationship found between node "nodexxx" and this object
从报错上来看,这个pod似乎要挂载一个configmap作为volume,但是这个报错还是第一次看到。如果说pod创建的时候就有这个配置,要么就已经完成volume绑定,要么绑定失败直接就报错了,no relationship是什么鬼?
原因
咨询了kubelet大拿,也查询了一下k8s的代码,发现问题是这样发生的:
补充: k8s代码查询的过程
直接搜报错关键字,居然完全一样的报错点有4个地方,这个就不太友好了,还得一个一个看具体哪里爆出来的。
这四处都位于kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go文件中。从路径上也可以看出,这是授权模块authz的一个插件功能,针对node。文件开头就说清楚了这里判断的逻辑:
// NodeAuthorizer authorizes requests from kubelets, with the following logic:
// 1. If a request is not from a node (NodeIdentity() returns isNode=false), reject
// 2. If a specific node cannot be identified (NodeIdentity() returns nodeName=""), reject
// 3. If a request is for a secret, configmap, persistent volume or persistent volume claim, reject unless the verb is get, and the requested object is related to the requesting node:
// node <- configmap
// node <- pod
// node <- pod <- secret
// node <- pod <- configmap
// node <- pod <- pvc
// node <- pod <- pvc <- pv
// node <- pod <- pvc <- pv <- secret
// 4. For other resources, authorize all nodes uniformly using statically defined rules
我们注意到其中第3点所述,如果kubelet是请求一个configmap,那么必须这个configmap是关联到节点(或者上面的容器),并且这个请求本身是get操作。其他情况下一律拒绝。
而我们的case里面,pod在调度完成之后新加一个configmap volume,并不满足“已关联到pod”的要求,所以被拒绝了。
// AddPod should only be called once spec.NodeName is populated. // It sets up edges for the following relationships (which are immutable for a pod once bound to a node):
//
// pod -> node
//
// secret -> pod
// configmap -> pod
// pvc -> pod
// svcacct -> pod
一旦调度完成,如下关系就不可修改。 为什么??
复现
复现环境
- apiserver版本v1.14
- 开启参数—authorization-mode=Node(我们的环境下是—authorization-mode=Node,RBAC)
复现步骤
1)创建一个pod
最简单的pod形式即可
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx1
spec:
containers:
- image: reg.docker.alibaba-inc.com/antk8s/nginx
name: nginx
resources:
limits:
cpu: "20"
ephemeral-storage: 2Gi
memory: 100Mi
requests:
cpu: "20"
ephemeral-storage: 1Gi
memory: 100Mi
2)完成调度之后,更新pod
添加一个不存在的configmap,pod yaml更改如下:
可以看到
长期解决方案
扩展
1.—authorization-mode官方支持哪些选项
官方支持多种鉴权方式,常用的有如下6种鉴权模式,可以同时开启一个或者多个。
- Node
- 针对kubelet,基于分配在node上的pod,给这个node授权。他允许kubelet对service/endpoint/nodes等进行读,对node/node status/pod/pod status/event进行写操作
- Node的设计目标应该是将kubelet的操作权限最小化控制
- kubelet的证书用户都属于system:nodes分组,在这个分组基础上进行权限控制
- 需要apiserver指定参数—authorization-mode=Node
- ABAC-Attribute-based access control
- 通过配置规则来控制用户对属性的访问。
- 需要apiserver指定参数—authorization-mode=ABAC,以及配套其他参数
- RBAC-Role-based access control
- 为用户、分组、ServiceAccount分配角色,基于角色来做权限控制
- 需要apiserver指定参数—authorization-mode=RBAC
- Webhook
- 通过对接一个远程服务校验是否允许进行操作,传递的参数描述了操作对象,操作动作等信息。通过这种方式可以对接一个外部扩展的鉴权能力。
- 通过配置指定操作—authorization-mode=Webhook
- AlwaysDeny,拒绝所有请求,用于测试;
- AlwaysAllow,允许所有情况,完全不设防;
我们的场景下,设置为—authorization-mode=Node,RBAC
2.apiserver的authz鉴权架构
整体api访问路径如下图所示(引自https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/)
用户请求api对象,需要依次经过如下步骤
1)认证Authentication,简称authn,此步骤用来确认用户身份,通过x.509/token等方式
2)鉴权Authorization,简称authz,此步骤用来判断用户增大请求的资源是否有对应的操作权限
3)变更与校验Admission Control,此步骤是在api对象持久化之前进行自动化变更(mutating)以及合法性校验(validating),系统自带了一些控制能力,可以通过apiserver的—enable-admission-plugins/—disable-admission-plugins选择性的开启和关闭。
- 除了内置的admission插件,可以通过外部webhook服务实现动态变更与控制的能力。
4)然后才真正存储或者返回数据
了解到authz所处的位置,我们再看下authz中进行鉴权的架构示意:
