Use Role-Based Access Control with Oracle Cloud Native Environment

Introduction

As the number of deployments to your Kubernetes cluster increases, you may need help managing it. The Kubernetes API provides the ability to add users and define their permissions to the cluster.

Once the user has authenticated, Kubernetes verifies what actions the user is authorized to perform. Role-Based Access Control (RBAC) is natively supported by Kubernetes and enabled by default in Oracle Cloud Native Environment (Oracle CNE). Making it one of the access control methods commonly used. RBAC allows you to manage access to resources deployed to the Kubernetes environment by applying rules that restrict users’ access to cluster resources. These rules can be either namespace-restricted using a Role, or cluster-wide using a ClusterRole.

You will find it helpful to have an understanding of the key components of Role-Based Access Control (RBAC) in Kubernetes, which are:

Another way to manage access to the Kubernetes cluster is Attribute-Based Access Control (ABAC), which allows finer tuning of policies compared to RBAC. But this is outside the scope of this tutorial.

This tutorial covers the basics of using RBAC to manage access to your Kubernetes cluster and demonstrates a simple use case.

For more information about Oracle Cloud Native Environment 2, please refer to the current Release Documentation site.

Objectives

In this tutorial, you’ll learn to:

Prerequisites

Configure Oracle Cloud Native Environment

Note: If running in your own tenancy, read the linux-virt-labs GitHub project README.md and complete the prerequisites before deploying the lab environment.

  1. Open a terminal on the Luna Desktop.

  2. Clone the linux-virt-labs GitHub project.

    git clone https://github.com/oracle-devrel/linux-virt-labs.git
    
  3. Change into the working directory.

    cd linux-virt-labs/ocne2
    
  4. Install the required collections.

    ansible-galaxy collection install -r requirements.yml
    
  5. Deploy the lab environment.

    ansible-playbook create_instance.yml -e localhost_python_interpreter="/usr/bin/python3.6" -e install_ocne_rpm=true -e create_ocne_cluster=true -e "ocne_cluster_node_options='-n 1 -w 1'"
    

    The free lab environment requires the extra variable local_python_interpreter, which sets ansible_python_interpreter for plays running on localhost. This variable is needed because the environment installs the RPM package for the Oracle Cloud Infrastructure SDK for Python, located under the python3.6 modules.

    The default deployment shape uses the AMD CPU and Oracle Linux 8. To use an Intel CPU or Oracle Linux 9, add -e instance_shape="VM.Standard3.Flex" or -e os_version="9" to the deployment command.

    Important: Wait for the playbook to run successfully and reach the pause task. At this stage of the playbook, the installation of the Oracle CNE is complete, and the instances are ready. Take note of the previous play, which prints the public and private IP addresses of the nodes it deploys and any other deployment information needed while running the lab.

Access the Kubernetes Cluster

  1. Open a terminal and connect via SSH to the ocne instance.

    ssh oracle@<ip_address_of_instance>
    
  2. Wait for the cluster to stabilize and all pods to report in a running state.

    watch kubectl get pods -A
    

    Once all the pods show a STATUS of Running, type Ctrl-C to exit the watch command.

  3. Confirm how many nodes are present.

    kubectl get nodes
    

Confirm The Current RBAC Configuration

RBAC manages the permissions for the actions you perform on resources deployed onto your Kubernetes cluster. You will check that RBAC is enabled and review the default roles in your cluster.

  1. Confirm RBAC is enabled.

    If the rbac.authorization.k8s.io API is visible, it means that RBAC is configured and is used to control which actions users or service accounts can undertake on cluster resources.

    kubectl api-versions | grep rbac
    

    Example Output:

    [oracle@ocne ~]$ kubectl api-versions | grep rbac
    rbac.authorization.k8s.io/v1
    
  2. Show the built-in cluster roles.

    kubectl get clusterroles | grep admin
    

    Example Output:

    [oracle@ocne ~]$ kubectl get clusterroles | grep admin
    admin                                                                  2025-07-23T10:21:55Z
    cluster-admin                                                          2025-07-23T10:21:55Z
    system:aggregate-to-admin                                              2025-07-23T10:21:55Z
    system:kubelet-api-admin                                               2025-07-23T10:21:55Z
    

    Normal users use the admin and cluster-admin roles, while the RBAC API reserves the system: roles for internal components.

  3. List the permissions for cluster-admin.

    kubectl describe clusterrole cluster-admin
    

    Example Output:

    [oracle@ocne ~]$ kubectl describe clusterrole cluster-admin
    Name:         cluster-admin
    Labels:       kubernetes.io/bootstrapping=rbac-defaults
    Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
    PolicyRule:
      Resources  Non-Resource URLs  Resource Names  Verbs
      ---------  -----------------  --------------  -----
      *.*        []                 []              [*]
                 [*]                []              [*]
    

    The asterisks in the Verbs and the Resources columns mean that the cluster-admin role can perform any action. A high-level list of the operations (Verbs) available for each Kubernetes release is available in the API Overview for that release. For example, in Kubernetes v1.33.0. A detailed list of valid operations (Verbs) available for each Resource type is available by executing kubectl api-resources -o wide from the command line.

    However, because you don’t log in to kubectl, how does Kubernetes know who the user executing the command is? Production systems usually use an LDAP server for authentication. A ServiceAccount can be used if an external authentication system is not available.

    Note: ServiceAccounts are used by automated workloads, such as CI/CD pipelines, but can also be used for testing.

Create a Role

A Role is a namespace-restricted resource defining the permissions for accessing Kubernetes resources within a single namespace.

Create a Namespace and a Role

  1. Create a Namespace.

    Create a new namespace for this example.

    kubectl create namespace rbac-example
    
  2. Create a Role.

    Create a Role that grants read-only access (‘get’ and ‘list’ permissions) to pods and deployments within the rbac-example namespace.

    cat << EOF | tee pod-reader-role.yaml > /dev/null
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: pod-reader
      namespace: rbac-example
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]
    - apiGroups: ["apps"]
      resources: ["deployments"]
      verbs: ["get", "list"]
    EOF
    

    Where:

    • rules: - Defines the permissions granted to the Role. This example defines two rules (see below).
    • apiGroups: [""] - Permits the user or service account bound to this rule to retrieve and list pods in the rbac-example namespace.
    • apiGroups: ["apps"] - Permits the user or service account bound to this rule to retrieve and list deployments in the rbac-example namespace.
  3. Apply the file.

    kubectl apply -f pod-reader-role.yaml
    
  4. Check the permissions for the newly created pod-reader role.

    kubectl describe role/pod-reader -n rbac-example
    

    Example Output:

    [oracle@ocne ~]$ kubectl describe role/pod-reader -n rbac-example
    Name:         pod-reader
    Labels:       <none>
    Annotations:  <none>
    PolicyRule:
      Resources         Non-Resource URLs  Resource Names  Verbs
      ---------         -----------------  --------------  -----
     pods              []                 []              [get list]
     deployments.apps  []                 []              [get list]
    

Create a User and Bind to the Role

  1. Create a new ServiceAccount user called pod-reader-user and bind it to the pod-reader role.

    cat << EOF | tee pod-reader-user.yaml > /dev/null
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: pod-reader-user
      namespace: rbac-example
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: pod-reader-binding
      namespace: rbac-example
    roleRef:
      name: pod-reader
      kind: Role
    subjects:
    - kind: ServiceAccount
      name: pod-reader-user
      namespace: rbac-example
    EOF
    

    Where:

    • The ServiceAccount creates the service account.
      • name: - The service account user’s name (pod-reader-user).
      • namespace - The namespace where it is created (rbac-example).
    • The RoleBinding grants the namespace-related permissions to the service account.
      • roleRef: - Specifies the binding Role. In this example, it references a Role called pod-reader.
      • subjects: - Specifies the entity to grant permissions. In this example, a service account called pod-reader-user.
  2. Apply the file.

    kubectl apply -f pod-reader-user.yaml
    

Test the RoleBinding

Next, test the RoleBinding by creating a new Pod and accessing it using the newly created pod-reader-user service account.

  1. Create a new test deployment.

    cat << EOF | tee testpod.yaml > /dev/null
    apiVersion: v1
    kind: Pod
    metadata:
      name: test-pod
      namespace: rbac-example
    spec:
      containers:
      - name: test-container
        image: ghcr.io/oracle/oraclelinux9-nginx:1.20
        ports:
        - containerPort: 80
      serviceAccountName: pod-reader-user
    EOF
    

    Where:

    • The spec: defines the pod’s desired state.
      • containers: - Specifies the list of containers to run in the pod. In this example, there is only one container.
        • name: - The name of the container (test-container).
        • image: - The image to use (ghcr.io/oracle/oraclelinux9-nginx:1.20).
        • ports: - Specifies the port exposed by the container. In this example, it is port 80 (containerPort: 80).
      • serviceAccountName: - Specifies the service account to use for the Pod. In this configuration, the Pod uses the permissions and credentials assigned to the pod-reader-user service account.
  2. Apply the file.

    kubectl apply -f testpod.yaml
    
  3. Now, try to access the Pod using the pod-reader-user ServiceAccount.

    kubectl auth can-i get pod/test-pod --as=system:serviceaccount:rbac-example:pod-reader-user -n rbac-example
    

    You should see yes as the output, indicating that the pod-reader-user has permission to access the pod.

    Note: Use the kubectl auth can-i functionality to execute commands as the newly created ServiceAccount user account to verify that an action is allowed.

  4. Confirm the ServiceAccount works as expected.

    kubectl --as=system:serviceaccount:rbac-example:pod-reader-user get pods -n rbac-example
    

    Example Output:

    [oracle@ocne ~]$ kubectl --as=system:serviceaccount:rbac-example:pod-reader-user get pods -n rbac-example
    NAME       READY   STATUS    RESTARTS   AGE
    test-pod   1/1     Running   0          109s
    
  5. Try to delete a Pod using the pod-reader-user role.

    kubectl --as=system:serviceaccount:rbac-example:pod-reader-user delete pod test-pod -n rbac-example
    

    Example Output:

    [oracle@ocne ~]$ kubectl --as=system:serviceaccount:rbac-example:pod-reader-user delete pod test-pod -n rbac-example
    Error from server (Forbidden): pods "test-pod" is forbidden: User "system:serviceaccount:rbac-example:pod-reader-user" cannot delete resource "pods" in API group "" in the namespace "rbac-example"
    

    The pod-reader-user role cannot delete pods in the rbac-example. Perhaps they can start new pods?

  6. Try to run a Pod using the pod-reader-role role.

    kubectl run nginx1 --image=ghcr.io/oracle/oraclelinux9-nginx:1.20 --as=system:serviceaccount:default:my-serviceaccount -n rbac-example
    

    Example Output:

    [oracle@ocne ~]$ kubectl run nginx1 --image=ghcr.io/oracle/oraclelinux9-nginx:1.20 --as=system:serviceaccount:rbac-example:pod-reader-user -n rbac-example
    Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:rbac-example:pod-reader-user" cannot create resource "pods" in API group "" in the namespace "rbac-example"
    

    This error is correct because the pod-reader-user role is only allowed to perform get and list actions against Pod resources in the rbac-example namespace of the cluster. Any other actions, delete or create, are not allowed.

Create a ClusterRole

ClusterRoles are similar to Roles, but they define allowed permissions on resources across the entire cluster.

Note: Be cautious when granting permissions that are too broad. Instead, use the least ‘principle of least privilege’ to grant users and service accounts only the permissions needed to complete their assigned tasks.

Create a ClusterRole and ClusterRoleBinding

The previous example showed how to create a namespace-specific user and role. The following steps demonstrate how to create a ClusterRole and bind it to a user to grant them cluster-wide admin access.

  1. Create a new ClusterRole.

    This ClusterRole allows users added to it to conduct any action on all resources across the cluster.

    cat << EOF | tee cluster-admin-clusterrole.yaml > /dev/null
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: cluster-admin-cr
    rules:
    - apiGroups: ["*"]
      resources: ["*"]
      verbs: ["*"]
    EOF
    

    Where:

    • rules: - Defines the permissions granted to the ClusterRole. This example only has one rule:
      • apiGroups: ["*"] - Specifies the rule applies to all API groups.
      • resources: ["*"] - Specifies that the rule applies to all resources of the API groups.
      • verbs: ["*"] - Specifies that the rule grants all possible verbs on the resources, including get, list, create, delete, etc.
  2. Apply the file.

    kubectl apply -f cluster-admin-clusterrole.yaml
    
  3. Create a ClusterRoleBinding to bind the ClusterRole to a new user.

    The ClusterRoleBinding is similar to the RoleBinding you created earlier, but it’s cluster-scoped.

    cat << EOF | tee cluster-admin-user.yaml > /dev/null
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: cluster-admin-user
      namespace: rbac-example
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: cluster-admin-crb
    roleRef:
      name: cluster-admin-cr
      kind: ClusterRole
    subjects:
    - kind: ServiceAccount
      name: cluster-admin-user
      namespace: rbac-example
    EOF
    

    Where:

    • subjects: - Defines the entities to grant permissions in the ClusterRole. In this example, it is the cluster-admin-user service account.
  4. Apply the file.

    kubectl apply -f cluster-admin-user.yaml
    

Test the ClusterRoleBinding

  1. Test the cluster role by trying to access a resource in a different namespace. For example, the default namespace.

    kubectl auth can-i list pods --as=system:serviceaccount:rbac-example:cluster-admin-user -n default
    

    You should see yes as the output, indicating that the cluster-admin-user has cluster-admin access.

  2. Confirm the newly created ClusterRole works as expected.

    kubectl --as=system:serviceaccount:rbac-example:cluster-admin-user get pods -A
    

    Example Output:

    [oracle@ocne ~]$ kubectl --as=system:serviceaccount:rbac-example:cluster-admin-user get pods -A
    NAMESPACE      NAME                                           READY   STATUS    RESTARTS   AGE
    kube-flannel   kube-flannel-ds-ptwkz                          1/1     Running   0          6h58m
    kube-flannel   kube-flannel-ds-wn2g6                          1/1     Running   0          6h58m
    kube-system    coredns-7cbdbfd99c-7xqkl                       1/1     Running   0          6h59m
    kube-system    coredns-7cbdbfd99c-k2ssb                       1/1     Running   0          6h59m
    kube-system    etcd-ocne-control-plane-1                      1/1     Running   0          6h59m
    kube-system    kube-apiserver-ocne-control-plane-1            1/1     Running   0          6h59m
    kube-system    kube-controller-manager-ocne-control-plane-1   1/1     Running   0          6h59m
    kube-system    kube-proxy-48rm5                               1/1     Running   0          6h59m
    kube-system    kube-proxy-h4kd2                               1/1     Running   0          6h58m
    kube-system    kube-scheduler-ocne-control-plane-1            1/1     Running   0          6h59m
    ocne-system    ocne-catalog-577b7cd5f9-bnvtm                  1/1     Running   0          6h58m
    ocne-system    ui-846bddd4b-lnrwm                             1/1     Running   0          6h58m
    rbac-example   test-pod                                       1/1     Running   0          6h34m
    

    This output confirms that the newly created ClusterRole can access all namespaces defined on the cluster.

Confirm the ClusterRole User can Create a New Deployment

Test the ClusterRole to see if it allows assigned users to create a new deployment. Then, try to access it using the newly created ClusterRole.

  1. Create a new test deployment.

    cat << EOF | tee testpod2.yaml > /dev/null
    apiVersion: v1
    kind: Pod
    metadata:
      name: test-pod2
      namespace: default
    spec:
      containers:
      - name: test-container
        image: ghcr.io/oracle/oraclelinux9-nginx:1.20
        ports:
        - containerPort: 8080
    EOF
    

    Note that this deployment deploys to the ‘default’ namespace, and not the rbac-example namespace used previously.

  2. Apply the file.

    kubectl apply -f testpod2.yaml
    
  3. Confirm the deployment completed successfully.

    kubectl --as=system:serviceaccount:rbac-example:cluster-admin-user get pods -A
    

    Example Output:

    [oracle@ocne ~]$ kubectl --as=system:serviceaccount:rbac-example:cluster-admin-user get pods -A
    NAMESPACE      NAME                                           READY   STATUS    RESTARTS   AGE
    default        test-pod2                                      1/1     Running   0          28s
    kube-flannel   kube-flannel-ds-shgh7                          1/1     Running   0          38m
    kube-flannel   kube-flannel-ds-zzrgh                          1/1     Running   0          38m
    kube-system    coredns-7cbdbfd99c-jg6lz                       1/1     Running   0          38m
    kube-system    coredns-7cbdbfd99c-wh5g4                       1/1     Running   0          38m
    kube-system    etcd-ocne-control-plane-1                      1/1     Running   0          38m
    kube-system    kube-apiserver-ocne-control-plane-1            1/1     Running   0          38m
    kube-system    kube-controller-manager-ocne-control-plane-1   1/1     Running   0          38m
    kube-system    kube-proxy-5qngx                               1/1     Running   0          38m
    kube-system    kube-proxy-6fz2q                               1/1     Running   0          38m
    kube-system    kube-scheduler-ocne-control-plane-1            1/1     Running   0          38m
    ocne-system    ocne-catalog-577b7cd5f9-vz782                  1/1     Running   0          38m
    ocne-system    ui-846bddd4b-bbhtj                             1/1     Running   0          38m
    rbac-example   test-pod                                       1/1     Running   0          21m
    

    Confirming that the new ClusterRole allows new deployments to a different namespace.

Next Steps

This tutorial demonstrated how to set up and use Role-Based Access Control (RBAC) with Kubernetes by creating Roles to manage access to Kubernetes resources. You achieved this by defining roles and bindings to control what permitted actions were allowed on resources within a namespace, or on a cluster-wide basis. In this way, the granular control RBAC provides is essential for securing your Kubernetes environment, especially for large deployments. Check out the Oracle Linux Training Station for additional tutorials and content.

More Learning Resources

Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.

For product documentation, visit Oracle Help Center.