I have been in DevOps related jobs for past 6 years dealing mainly with Kubernetes in AWS and on-premise as well. I spent quite a lot …
Kubernetes RBAC
There are namespaced and non namespaced resources in Kubernetes.
- Role (namespaced) -> RoleBinding
- ClusterRole (non namespaced) -> ClusterRoleBinding
Be extra careful with ClusterRole
and ClusterRoleBinding
because these are not only assigned to currently existing namespaces but also to namespaces created in future.
Valid combinations:
- Role -> RoleBinding
- ClusterRole -> ClusterRoleBinding
- ClusterRole -> RoleBinding
Make sure that user Jane can only read secretes in namespace red.
# Create role secret-manager in namespace red to be able to get secrets
k create role -n red secret-manager --verb get --resource secrets -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: red
creationTimestamp: null
name: secret-manager
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
# Create rolebinding to associate a role with a user "jane"
k create rolebinding -n red secret-manager --role=secret-manager --user=jane -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: secret-manager
namespace: red
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: secret-manager
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: jane
Make sure that user Jane can only list, get
secretes in namespace blue.
k create role -n blue secret-manager --verb=get,list --resource secrets -o
yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: null
name: secret-manager
namespace: blue
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
# Create rolebinding to associate a role with a user "jane" in blue namespace
k create rolebinding -n blue secret-manager --role=secret-manager --user=jane -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: secret-manager
namespace: blue
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: secret-manager
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: jane
Test roles and rolebindings
k -n red auth can-i list secrets --as jane
no
Create and test ClusterRole
named deploy-deleter which allows to delete deployments
- User “jane” can delete deployments in all namespaces
- User “jim” can delete deployments only in namespace red
# Create and test `ClusterRole` named **deploy-deleter** which allows to delete deployments
k create clusterrole deploy-deleter --verb delete --resource deployment -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: deploy-deleter
rules:
- apiGroups:
- apps
resources:
- deployments
verbs:
- delete
# 1. User "jane" can delete deployments in all namespaces
k create clusterrolebinding deploy-deleter --clusterrole deploy-deleter --user jane -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
creationTimestamp: null
name: deploy-deleter
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: deploy-deleter
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: jane
# 2. User "jim" can delete deployments only in namespace red
k create rolebinding -n red deploy-deleter --clusterrole deploy-deleter --user jim -o yaml --dry-run=client
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: deploy-deleter
namespace: red
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: deploy-deleter
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: jim
Test clusterrole and clusterrolebinding/rolebindings
k -n kube-system auth can-i delete deployment --as jane
yes
k -n red auth can-i delete deployment --as jim
yes
There are two kind of accounts in Kubenretes:
- ServiceAccounts
- “normal users” (need to issue a certificate)
There is no k8s User resource in Kubenretes
The idea behind issuing a certificate to a user e.g. jane
There is no way to invalidate a certificate If a certificate has been leaked:
- remove all access via RBAC
- username cannot be used intil certificate would expire
- create new CA and re-issue all certificates
How to issue a certificate for a normal user “jane”
# Generate a private key
openssl genrsa -out jane.key 2048
# Generate a CSR :)
openssl req -new -key jane.key -out jane.csr
...
-----
Country Name (2 letter code) []:
State or Province Name (full name) []:
...
Common Name (eg, fully qualified host name) []:jane
...
to be sent with your certificate request
A challenge password []:
---
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: jane
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400 # one day
usages:
- client auth
:read ! cat jane.csr | base64 | tr -d "\n"
Then save the content above and kubectl create -f ...
k create -f csr.yaml
certificatesigningrequest.certificates.k8s.io/jane created
saq4a@W0188FMI173 blog % k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
jane 3s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Pending
Approve CertificateSigningRequest
k certificate approve jane
certificatesigningrequest.certificates.k8s.io/jane approved
saq4a@W0188FMI173 blog % k get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
jane 6m52s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Approved,Issued
Extract TLS certificate
kubectl get csr jane -o jsonpath='{.status.certificate}'| base64 -d > jane.crt
Now, we need to setup context in KUBECONFIG
k config view
k config set-credentials jane --client-key=jane.key --client-certificate=jane.crt --embed-certs
k config set-context jane --user jane --cluster kubernetes
k config use-context jane
# Now it is up to you setup proper RBAC to grant certain permissions to user "jane"
k get secrets -n blue
NAME TYPE DATA AGE
default-token-nlxct kubernetes.io/service-account-token 3 3d21h
k get secrets
Error from server (Forbidden): secrets is forbidden: User "jane" cannot list resource "secrets" in API group "" in the namespace "default"
- There are existing Namespaces ns1 and ns2. Create ServiceAccount pipeline in both Namespaces. These SAs should be allowed to view almost everything in the whole cluster. You can use the default ClusterRole view for this. These SAs should be allowed to create and delete Deployments in Namespaces ns1 and ns2. Verify everything using kubectl auth can-i.
k create sa pipeline -n ns1
k create sa pipeline -n ns2
kubectl create clusterrolebinding pipeline-ns1 --clusterrole=view --serviceaccount=ns1:pipeline
kubectl create clusterrolebinding pipeline-ns2 --clusterrole=view --serviceaccount=ns2:pipeline
kubectl create clusterrole deleter --verb=create,delete --resource=deployments
kubectl create rolebinding -n ns1 dele-11 --serviceaccount=ns1:pipeline --clusterrole=deleter
kubectl create rolebinding -n ns2 dele-22 --serviceaccount=ns2:pipeline --clusterrole=deleter
# namespace ns1 deployment manager
k auth can-i delete deployments --as system:serviceaccount:ns1:pipeline -n ns1 # YES
k auth can-i create deployments --as system:serviceaccount:ns1:pipeline -n ns1 # YES
k auth can-i update deployments --as system:serviceaccount:ns1:pipeline -n ns1 # NO
k auth can-i update deployments --as system:serviceaccount:ns1:pipeline -n default # NO
# namespace ns2 deployment manager
k auth can-i delete deployments --as system:serviceaccount:ns2:pipeline -n ns2 # YES
k auth can-i create deployments --as system:serviceaccount:ns2:pipeline -n ns2 # YES
k auth can-i update deployments --as system:serviceaccount:ns2:pipeline -n ns2 # NO
k auth can-i update deployments --as system:serviceaccount:ns2:pipeline -n default # NO
# cluster wide view role
k auth can-i list deployments --as system:serviceaccount:ns1:pipeline -n ns1 # YES
k auth can-i list deployments --as system:serviceaccount:ns1:pipeline -A # YES
k auth can-i list pods --as system:serviceaccount:ns1:pipeline -A # YES
k auth can-i list pods --as system:serviceaccount:ns2:pipeline -A # YES
k auth can-i list secrets --as system:serviceaccount:ns2:pipeline -A # NO (default view-role doesn't allow)
- Manually sign the CSR with the K8s CA file to generate the CRT at /root/60099.crt. Create a new context for kubectl named 60099@internal.users which uses this CRT to connect to K8s.
# my solution
openssl genrsa -out /root/60099.key 2048
openssl req -new -key /root/60099.key -out /root/60099.csr
# Official explainations
openssl x509 -req -in 60099.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out 60099.crt -days 500
k config set-credentials 60099@internal.users --client-key=60099.key --client-certificate=60099.crt
k config set-context 60099@internal.users --cluster=kubernetes --user=60099@internal.users
k config get-contexts
k config use-context 60099@internal.users
k get ns # fails because no per
- Setup suer 60099 via API CSR
openssl genrsa -out /root/60099.key 2048
openssl req -new -key /root/60099.key -out /root/60099.csr
...
k certificate approve userx
k get certificatesigningrequests.certificates.k8s.io userx -o jsonpath='{.status.certificate}' | base64 -d > 60099.crt
k config set-credentials 60099@internal.users --client-key=60099.key --client-certificate=60099.crt --embed-certs
k config set-context 60099@internal.users@kubernetes --cluster=kubernetes --user=60099@internal.users
k config use-context 60099@internal.users@kubernetes
k config current-context
k config get-contexts
k get ns