Proper use of Role.rules.resourceNames for creating pods with limited access to resources

12/8/2020

I am trying to create a Pod that is able to create and update a specific configmap using Role.rules.resourceNames. I am able to perform a get request for the resource to the API from within the pod, but I'm not able to create the resource, instead getting

$ kubectl logs rbac-test
Error from server (Forbidden): error when creating "/config/aaa.yaml": configmaps is forbidden: User "system:serviceaccount:default:rbac-test" cannot create resource "configmaps" in API group "" in the namespace "default"

If I remove the resourceNames attribute I am able to create the configmap, but I don't necessarily want this pod to be able to create and update configmaps willy-nilly. How can I restrict the serviceAccount's roles so that this pod can manipulate only the named configmap aaa?

EDIT

I got some help in the kubernetes slack (thank you, Alan). RBAC happens at the level of the URL so resourceName-restricted authorization can't happen for create URLs since there is no resource name in the URL!

kind: Pod
apiVersion: v1
metadata:
  name: rbac-test
spec:
  restartPolicy: Never
  serviceAccountName: rbac-test
  imagePullSecrets:
    - name: docker-hub-creds 
  containers:
    - name: rbac-test
      image: bitnami/kubectl
      command: [ "kubectl" ]
      args: [ "apply", "-f", "/config/aaa.yaml" ]
      volumeMounts:
        - name: aaa-config
          mountPath: /config/
  volumes:
    - name: aaa-config
      configMap:
        name: aaa-config

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: aaa-config
data:
  aaa.yaml: |
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: aaa
    data:
      value: na

---
apiVersion: v1     
kind: ServiceAccount
metadata:
  name: rbac-test

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbac-test
rules:
  - apiGroups: [ "" ]
    resources: [ "configmaps" ]
    verbs: [ "get", "create", "update" ]
    resourceNames: [ "aaa" ]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rbac-test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rbac-test
subjects:
  - kind: ServiceAccount
    name: rbac-test
-- littlebenlittle
kubernetes
kubernetes-pod
kubernetes-rbac

2 Answers

12/9/2020

As mention by Shai Katz in previous answer according to documentation: Using RBAC Authorization - Referring to resources

You cannot restrict create or deletecollection requests by resourceName. For create, this limitation is because the object name is not known at authorization time.

Solution provided in the edit section - still will not work.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbac-test
rules:
  - apiGroups: [ "" ]
    resources: [ "configmaps" ]
    verbs: [ "get", "create", "update" ]
    resourceNames: [ "aaa" ]

This config allows you to only get and update existing configmap named aaa while the create operation is pointless as this resource cannot be created..

How can I restrict the serviceAccount's roles so that this pod can manipulate only the named configmap

You can achieve what you want if you will edit your Role

Solution 1

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbac-test
rules:
  - apiGroups: [ "" ]             #this rule will allow to update existing configmap aaa referenced by resourcename 
    resources: [ "configmaps" ]
    verbs: [ "get", "update" ]    
    resourceNames: [ "aaa" ]
  - apiGroups: [ "" ]             #this rule will allow you to create configmaps
    resources: [ "configmaps" ]
    verbs: [ "get", "create" ]   

It can be verified by executing commands below:

$ kubectl auth can-i update configmaps/aaa --as=system:serviceaccount:default:rbac-test
yes
$ kubectl auth can-i create configmaps/new-aaa --as=system:serviceaccount:default:rbac-test
yes

However, in this scenario rbac-test pod/sa can perform update action by executing kubectl replace command f.e:

$ kubectl replace -f /config/aaa.yaml

I you are interested with $ kubectl apply probably you should use verb patch like in the example below.

  - apiGroups: [ "" ]       
    resources: [ "configmaps" ]
    verbs: [ "get", "patch" ]    
    resourceNames: [ "aaa" ]

Solution 2 - More restrictive approach.

Instead of giving your serviceAccount: rbac-test permission to create configs in k8s api. Please consider creating two configmaps:

a)

$ kubectl create cm aaa ### an empty "aaa" configmap

b)

kubectl apply -f - <<EOF
kind: ConfigMap
apiVersion: v1
metadata:
  name: aaa-config
data:
  aaa.yaml: |
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: aaa
    data:
      value: na
EOF

This configmap will be populated to the POD using volumes. Configmap.data will be stored inside the pods directory under /config/aaa.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: aaa
data:
  value: na

Configure this RBAC Role:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbac-test
rules:
  - apiGroups: [ "" ]             #this rule will allow to patch existing empty "aaa" configmap aaa referenced by resourcename 
    resources: [ "configmaps" ]
    verbs: [ "get", "patch" ]    
    resourceNames: [ "aaa" ]

In this scenario you will have preconfigured aaa empty configmap and your pod with associated rbac-test serviceAccount will be able to perform patch request using provided YAML manifest stored in /config/aaa.yaml inside the Pod.

$ kubectl auth can-i patch configmaps/aaa --as=system:serviceaccount:default:rbac-test
yes
$ kubectl auth can-i create configmaps/new-aaa --as=system:serviceaccount:default:rbac-test
no

To check which verbs you can use in RBAC for ConfigMap execute:

$ kubectl api-resources -o wide | grep configmap
configmaps                        cm                                          true         ConfigMap                        [create delete deletecollection get list patch update watch]
-- PjoterS
Source: StackOverflow

12/8/2020

Note: You cannot restrict create or deletecollection requests by resourceName. For create, this limitation is because the object name is not known at authorization time.

Reference: https://kubernetes.io/docs/reference/access-authn-authz/rbac/

-- Shai Katz
Source: StackOverflow