pixelpiloten "articles and tutorials into all things Docker containers, Kubernetes,
CI/CD and automating infrastructure"
Go back to blogposts
Sunday, 10 November 2019

RBAC - Controlling user access in Kubernetes - Part 2

RBAC - Controlling user access in Kubernetes - Part 2

This is the second part in my series of articles on “RBAC - Controlling user access in Kubernetes”, you can find Part 1 here: https://www.pixelpiloten.se/blog/rbac-in-kubernetes-part-1/.

In this article I will go through on how you can create a ClusterRole and a ServiceAccount, bind that to the ClusterRole but tied to a Namespace. And to top it of I will show how you can generate a Kubeconfig file that you can use or give to a Developer that should have access to your cluster, but limited to that specific Namespace and the permissions that the ClusterRole gives it.

Step 1 - Create the ClusterRole

  1. Create a file called clusterrole--developer.yml, this role can basically create, delete and update all the most common things that a Developer would use.
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: developer-role
  rules:
    # Ingresses
    - apiGroups: ["networking.k8s.io", "extensions"]
      resources: ["ingresses"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Services
    - apiGroups: [""]
      resources: ["services"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Deployments
    - apiGroups: ["extensions", "apps"]
      resources: ["deployments", "replicasets"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Pods & Logs
    - apiGroups: [""]
      resources: ["pods", "pods/log", "pods/portforward", "pods/exec"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Jobs & Cronjobs
    - apiGroups: ["batch"]
      resources: ["cronjobs", "jobs"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Daemonsets
    - apiGroups: ["apps"]
      resources: ["daemonsets"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Replication controllers
    - apiGroups: [""]
      resources: ["replicationcontrollers"]
      verbs: ["get", "watch", "list"]
    # Stateful sets
    - apiGroups: ["apps"]
      resources: ["statefulsets"]
      verbs: ["get", "watch", "list"]
    # Configmaps
    - apiGroups: [""]
      resources: ["configmaps"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
    # Secrets
    - apiGroups: [""]
      resources: ["secrets"]
      verbs: ["get", "watch", "list", "create", "update", "delete"]
  1. Create that ClusterRole with Kubectl.
  $ kubectl apply clusterrole.yaml

Step 2 - Create Namespace, ServiceAccount and ClusterRoleBinding.

  1. Create a file called serviceaccount--developer.yaml with this content, this defines a Namespace, a ServiceAccount and a binds that to our ClusterRole we created before, but only within our Namespace. So this ServiceAccount only has the permissions within this Namespace, not cluster wide.
  apiVersion: v1
  kind: Namespace
  metadata:
    name: developer-namespace
  ---
  apiVersion: v1
  kind: ServiceAccount
  metadata:
    name: developer
    namespace: developer-namespace
  ---
  apiVersion: rbac.authorization.k8s.io/v1
  kind: RoleBinding
  metadata:
    name: developer-rolebinding
    namespace: developer-namespace
  roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: developer
  subjects:
  - kind: ServiceAccount
    name: developer
    namespace: developer-namespace
  1. Create the Namespace, ServiceAccount and ClusterRoleBinding with Kubectl.
  $ kubectl apply -f serviceaccount--developer.yaml

Step 3 - Create bash script that can generate a kubeconfig file.

  1. Create a file called kubeconfig.sh and paste this code.
  #!/bin/sh

  SERVICE_ACCOUNT=$1
  NAMESPACE=$2
  SERVER=$3

  SERVICE_ACCOUNT_TOKEN_NAME=$(kubectl -n ${NAMESPACE} get serviceaccount ${SERVICE_ACCOUNT} -o jsonpath='{.secrets[].name}')
  SERVICE_ACCOUNT_TOKEN=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data.token}" | base64 --decode)
  SERVICE_ACCOUNT_CERTIFICATE=$(kubectl -n ${NAMESPACE} get secret ${SERVICE_ACCOUNT_TOKEN_NAME} -o "jsonpath={.data['ca\.crt']}")

  cat <<END
  apiVersion: v1
  kind: Config
  clusters:
  - name: default-cluster
    cluster:
      certificate-authority-data: ${SERVICE_ACCOUNT_CERTIFICATE}
      server: ${SERVER}
  contexts:
  - name: default-context
    context:
      cluster: default-cluster
      namespace: ${NAMESPACE}
      user: ${SERVICE_ACCOUNT}
  current-context: default-context
  users:
  - name: ${SERVICE_ACCOUNT}
    user:
      token: ${SERVICE_ACCOUNT_TOKEN}
  END
  1. Make kubeconfig.sh executable.
  chmod +x kubeconfig.sh

Step 4 - Generate a kubeconfig file

  1. Run the script and point the output to a file and location where you want to put it (usually in the ~/.kube-folder).
  ./kubeconfig developer developer-namespace <KUBERNETES-API-URL> > ~/.kube/mycluster.conf
  1. Set the path of the generated kubeconfig file in the KUBECONFIG variable.
  $ export KUBECONFIG="~/.kube/mycluster.conf"

Tadaa!

You have now created a ServiceAccount with all the tools your Developer needs but limited to a specific Namespace so they cannot deploy or modify anything outside that.