Namespaces allow dividing a Kubernetes cluster on logical segments with an isolated set of resources. This mechanism enables creation of multi-tenant environments within one cluster, enabling multiple teams to use one infrastructure. At the same time it raises the problem of granular access to cluster resources.

 

In this article I will demonstrate how to grant user access to a specific namespace by using an authorization token and define their permissions with the Role-Based Access Control (RBAC) policies.

Granting user access with token

To create a user within a Kubernetes namespace and grant them access to specific resources, you begin by creating a ServiceAccount in your desired namespace. This ServiceAccount acts as the “user” for your applications or services running in Kubernetes. Next, define a ClusterRole that specifies the permissions you want to grant, such as access to pods, services, and other resources. Although cluster roles are cluster-wide, you can scope the permissions to a specific namespace by creating a RoleBinding within that namespace, linking the ClusterRole to your ServiceAccount. This setup ensures that the ServiceAccount has the necessary permissions only in the designated namespace, effectively creating a namespace-scoped user with access to particular resources.

Diagram of granting user access with token

Step 1: Define a ClusterRole

Define a ClusterRole with the permissions you want to grant. This cluster role does not specify namespaces because it’s designed to be applicable cluster-wide.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kube-dc-user-clusterrole
rules:
- apiGroups: [""]
  resources: ["pods", "services"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets", "statefulsets"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["cdi.kubevirt.io"]
  resources: ["datavolumes"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["kubevirt.io"]
  resources: ["virtualmachineinstances", "virtualmachines"]
  verbs: ["get", "list", "watch"]

Step 2: Create a ServiceAccount

Users should be kept in permanent namespace users, and access would be granted for particular namespace by setting RoleBinding in particular namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: users

apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube-dc-user
  namespace: users

 

Also a secret token should be created:

apiVersion: v1
kind: Secret
metadata:
  name: kube-dc-user-token
  namespace: users
  annotations:
    kubernetes.io/service-account.name: "kube-dc-user"
type: kubernetes.io/service-account-token

Step 3: Create RoleBindings in Specific Namespaces

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kube-dc-user-clusterrole-binding
  namespace: demo
subjects:
- kind: ServiceAccount
  name: kube-dc-user
  namespace: users
roleRef:
  kind: ClusterRole
  name: kube-dc-user-clusterrole
  apiGroup: rbac.authorization.k8s.io

Step 4: Retrieve the ServiceAccount Token

Created token name is with the suffix -token added to existing user:

# Get the token for the user
kubectl get secret kube-dc-user-token -n users -o=jsonpath='{.data.token}' | base64 --decode

Using curl to Query the API

Here’s an example using curl to query virtual machines in a specific namespace:

SA_TOKEN=$(kubectl get secret kube-dc-user-token -n users -o=jsonpath='{.data.token}' | base64 --decode)
curl -k -H "Authorization: Bearer $SA_TOKEN" \
https://demo.kube-dc.com:6443/apis/kubevirt.io/v1/namespaces/demo/virtualmachines

Create a kubeconfig from token

If you’re using kubectl on your local machine, you can temporarily set the KUBECONFIG environment variable or modify your kubeconfig file to use the Service Account token. This approach is useful for manual inspections or when scripting interactions with Kubernetes.

 

First, create a new kubeconfig file or modify an existing one to include the Service Account token. Here’s a basic example of how to set up a context that uses the Service Account token.

 

Get the API server address:

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')

Get the CA certificate (if needed):

kubectl get secret <service-account-secret-name> -n <namespace> -o jsonpath='{.data.ca\.crt}' | base64 --decode > ca.crt

 

Replace <service-account-secret-name> and <namespace> with the actual secret name and namespace.

 

Create a kubeconfig file.

 

Here’s an example of adding a new user, context, and cluster in your kubeconfig. You might want to add this to an existing kubeconfig file or create a new one specifically for this purpose.

apiVersion: v1
kind: Config
clusters:
- name: my-cluster
  cluster:
    certificate-authority: /path/to/ca.crt  # Adjust the path as needed
    server: https://<api-server-address>    # Use the APISERVER variable
users:
- name: kube-dc-user
  user:
    token: <your-service-account-token>
contexts:
- name: kube-dc-user@my-cluster
  context:
    cluster: my-cluster
    namespace: <namespace>  # Specify the namespace if desired
    user: kube-dc-user
current-context: kube-dc-user@my-cluster

Replace <api-server-address>, <your-service-account-token>, and <namespace> with your actual API server address, the token you retrieved, and the namespace you want to use.

Conclusion

The RBAC policies and authorization via tokens are critical features of Kubernetes cluster security that allow you to control who can do what in each namespace. Following the steps described in this article will help you define user permissions within a certain namespace granting granular user access to cluster resources.