Technical Theory

Managing RBAC

Introduction

This tutorial will guide you through managing Role-Based Access Control (RBAC) in Kubernetes. We will cover creating users, service accounts, roles, role bindings, and cluster role bindings. Basic familiarity with Kubernetes concepts like pods, namespaces, and deployments is assumed. You should have a working Kubernetes cluster and kubectl configured.

Task 1: Understanding RBAC Concepts

Before diving into the practical steps, let’s briefly review the core concepts of RBAC in Kubernetes:

  • Users: Represents a human user or an external identity. Kubernetes doesn’t have its own user management system; it relies on external authentication mechanisms.
  • Service Accounts: Represents an identity for processes that run inside pods. Service accounts are managed by Kubernetes.
  • Roles: Define a set of permissions within a specific namespace. A role grants access to Kubernetes resources, such as pods, deployments, and services.
  • ClusterRoles: Similar to Roles, but they are cluster-wide. They can grant access to cluster-scoped resources and can be referenced from within any namespace.
  • RoleBindings: Grants the permissions defined in a Role to a user or a set of users. It operates within a single namespace.
  • ClusterRoleBindings: Grants the permissions defined in a ClusterRole to a user or a set of users. It operates cluster-wide.
graph LR
    A[User/ServiceAccount] --> B{Authentication}
    B --> C{Authorization}
    C --> D{RBAC Check}
    D -- Granted --> E((Access Resource))
    D -- Denied --> F((Access Denied))

Task 2: Creating a Service Account

Service accounts provide an identity for pods. Let’s create a service account in the default namespace.

  1. Create a YAML file named serviceaccount.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: example-sa
      namespace: default
  2. Apply the configuration:

    NODE_TYPE // bash
    kubectl apply -f serviceaccount.yaml
    NODE_TYPE // output
    serviceaccount/example-sa created
  3. Verify the service account:

    NODE_TYPE // bash
    kubectl get serviceaccount example-sa -n default -o yaml

    This will display the details of the newly created service account.

    Service accounts are automatically mounted into pods that don’t specify a different service account.

Task 3: Creating a Role

Now, let’s define a Role that grants permissions to read pods within the default namespace.

  1. Create a YAML file named role.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: default
      name: pod-reader
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]
  2. Apply the configuration:

    NODE_TYPE // bash
    kubectl apply -f role.yaml
    NODE_TYPE // output
    role.rbac.authorization.k8s.io/pod-reader created
  3. Verify the role:

    NODE_TYPE // bash
    kubectl get role pod-reader -n default -o yaml

    This command will show the details of the pod-reader role.

    Roles are namespace-scoped. This role only applies to the default namespace.

Task 4: Creating a RoleBinding

A RoleBinding links the Role with the Service Account. Let’s create a RoleBinding to grant the pod-reader role to the example-sa service account.

  1. Create a YAML file named rolebinding.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: read-pods
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: example-sa
      namespace: default
    roleRef:
      kind: Role
      name: pod-reader
      apiGroup: rbac.authorization.k8s.io
  2. Apply the configuration:

    NODE_TYPE // bash
    kubectl apply -f rolebinding.yaml
    NODE_TYPE // output
    rolebinding.rbac.authorization.k8s.io/read-pods created
  3. Verify the RoleBinding:

    NODE_TYPE // bash
    kubectl get rolebinding read-pods -n default -o yaml

    This will display the details of the read-pods RoleBinding.

    RoleBindings connect subjects (users, groups, service accounts) to roles.

Task 5: Testing the Permissions

To test the permissions, we’ll create a pod that uses the example-sa service account and attempts to list pods. We will need to extract the token associated with the service account and use it to authenticate.

  1. Get the service account token:

    NODE_TYPE // bash
    kubectl get serviceaccount example-sa -n default -o jsonpath='{.secrets[0].name}'

    This will output the name of the secret containing the token (e.g., example-sa-token-xxxxx). Let’s assume the token name is example-sa-token-xxxxx.

  2. Extract the token:

    NODE_TYPE // bash
    kubectl get secret example-sa-token-xxxxx -n default -o jsonpath='{.data.token}' | base64 -d

    This will output the actual token. Store this token in a secure location. Let’s assume the token is eyJhbGciOiJIUzI1NiIsImtpZCI6Im9Va....

  3. Create a kubeconfig file for the service account. Replace the cluster details, user details, and token with your actual values. The cluster CA data can be obtained with: kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}'

    NODE_TYPE // yaml
    apiVersion: v1
    kind: Config
    clusters:
    - cluster:
        certificate-authority-data: <base64 encoded CA data>
        server: <your cluster endpoint>
      name: example-cluster
    contexts:
    - context:
        cluster: example-cluster
        user: example-sa
      name: example-context
    current-context: example-context
    users:
    - name: example-sa
      user:
        token: eyJhbGciOiJIUzI1NiIsImtpZCI6Im9Va...
  4. Test the access using the new kubeconfig file. Save the above yaml as sa-kubeconfig.yaml.

    NODE_TYPE // bash
    kubectl --kubeconfig=sa-kubeconfig.yaml get pods -n default

    This command, using the service account’s credentials, should successfully list the pods in the default namespace.

    Make sure to replace placeholders with your actual values!

Task 6: Creating a ClusterRole

ClusterRoles are used to define permissions that apply cluster-wide. Let’s create a ClusterRole that allows reading nodes.

  1. Create a YAML file named clusterrole.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: node-reader
    rules:
    - apiGroups: [""]
      resources: ["nodes"]
      verbs: ["get", "list"]
  2. Apply the configuration:

    NODE_TYPE // bash
    kubectl apply -f clusterrole.yaml
    NODE_TYPE // output
    clusterrole.rbac.authorization.k8s.io/node-reader created
  3. Verify the ClusterRole:

    NODE_TYPE // bash
    kubectl get clusterrole node-reader -o yaml

Task 7: Creating a ClusterRoleBinding

Now, let’s create a ClusterRoleBinding to grant the node-reader ClusterRole to a specific user. For this example, we will grant it to the example-sa service account. Note: in a real-world scenario, consider granting this to a dedicated user or group instead of a service account.

  1. Create a YAML file named clusterrolebinding.yaml with the following content:

    NODE_TYPE // yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: read-nodes
    subjects:
    - kind: ServiceAccount
      name: example-sa
      namespace: default
    roleRef:
      kind: ClusterRole
      name: node-reader
      apiGroup: rbac.authorization.k8s.io
  2. Apply the configuration:

    NODE_TYPE // bash
    kubectl apply -f clusterrolebinding.yaml
    NODE_TYPE // output
    clusterrolebinding.rbac.authorization.k8s.io/read-nodes created
  3. Verify the ClusterRoleBinding:

    NODE_TYPE // bash
    kubectl get clusterrolebinding read-nodes -o yaml
  4. Test the access using the sa-kubeconfig.yaml created in Task 5:

    NODE_TYPE // bash
    kubectl --kubeconfig=sa-kubeconfig.yaml get nodes

    This should now successfully list the nodes in the cluster, using the example-sa’s credentials.

Task 8: Creating a User

Kubernetes relies on external identity providers for authenticating users. You can configure your cluster to use various authentication methods, such as:

  • X.509 Client Certificates: Each user has a unique certificate.
  • Static Password File: (Not recommended for production)
  • Bootstrap Tokens: For initial setup.
  • OIDC (OpenID Connect): Integrate with identity providers like Google, Azure AD, etc.
  • Webhook Token Authentication: Custom authentication logic.

For this example, we’ll demonstrate how to create a user using client certificates.

  1. Generate a Private Key:

    NODE_TYPE // bash
    openssl genrsa -out jane.key 2048
  2. Create a Certificate Signing Request (CSR):

    NODE_TYPE // bash
    openssl req -new -key jane.key -out jane.csr -subj "/CN=jane/O=example-org"
    • CN (Common Name): Set to the username (jane).
    • O (Organization): Set to an organization identifier (example-org).
  3. Sign the CSR with the Kubernetes CA:

    This step requires access to the Kubernetes CA certificate and key. Typically, these are located on the control plane nodes. Replace the paths with the actual locations on your system.

    NODE_TYPE // bash
    openssl x509 -req -in jane.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out jane.crt -days 365
    Securely store the jane.key file. Anyone with this key can impersonate the user. Treat it like a password.
  4. Configure kubectl to use the certificate:

    NODE_TYPE // bash
    kubectl config set-credentials jane --client-certificate=jane.crt --client-key=jane.key
  5. Create a context for the user:

    NODE_TYPE // bash
    kubectl config set-context jane-context --cluster=<your cluster name> --user=jane --namespace=default

    Replace <your cluster name> with the name of your cluster. You can find this in your existing kubectl configuration.

  6. Switch to the new context:

    NODE_TYPE // bash
    kubectl config use-context jane-context

Now, any commands you run will be executed as user jane. Initially, jane has no permissions.

This approach demonstrates user creation. For production setups, strongly consider using OIDC or a similar centralized authentication system.

Task 9: Granting Permissions to the User

Let’s grant jane the permission to view pods in the default namespace.

  1. Create a RoleBinding:

    NODE_TYPE // yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: jane-pod-reader
      namespace: default
    subjects:
    - kind: User
      name: jane
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: pod-reader
      apiGroup: rbac.authorization.k8s.io
  2. Apply the RoleBinding:

    NODE_TYPE // bash
    kubectl apply -f rolebinding-jane.yaml
  3. Verify jane can now list pods:

    Ensure that the jane-context is selected using kubectl config use-context jane-context.

    NODE_TYPE // bash
    kubectl get pods -n default

    Jane should now be able to view pods in the default namespace.

    Ensure the user jane is defined correctly in the subjects section of the RoleBinding. The name must match the common name (CN) in the user’s certificate.

Conclusion

In this tutorial, you learned how to manage RBAC in Kubernetes. You created service accounts, roles, role bindings, cluster roles, cluster role bindings, and demonstrated how to create and grant permissions to a user. You tested these configurations to ensure they were functioning correctly. Remember that proper RBAC configuration is critical for securing your Kubernetes cluster. Always follow the principle of least privilege, granting only the necessary permissions to users and service accounts.

Next Topic