Search code examples
kubernetesgoogle-kubernetes-enginekubectl

Kubernetes read-only context


I have full admin access to a GKE cluster, but I want to be able to create a kubernetes context with just read only privileges. This way I can prevent myself from accidentally messing with the cluster. However, I still want to be able to switch into a mode with full admin access temporarily when I need to make changes (I would probably use cloud shell for this to fully distinguish the two)

I haven't much docs about this - it seems I can set up roles based on my email but not have two roles for one user.

Is there any way to do this? Or any other way to prevent fat-finger deleting prod?


Solution

  • There are a few ways to do this with GKE. A context in your KUBECONFIG consists of a cluster and a user. Since you want to be pointing at the same cluster, it's the user that needs to change. Permissions for what actions users can perform on various resources can be controlled in a couple ways, namely via Cloud IAM policies or via Kubernetes RBAC. The former applies project-wide, so unless you want to create a subject that has read-only access to all clusters in your project, rather than a specific cluster, it's preferable to use the more narrowly-scoped Kubernetes RBAC.

    The following types of subjects can authenticate with a GKE cluster and have Kubernetes RBAC policies applied to them (see here):

    1. a registered (human) GCP user
    2. a Kubernetes ServiceAccount
    3. a GCloud IAM service account
    4. a member of a G Suite Google Group

    Since you're not going to register another human to accomplish this read-only access pattern and G Suite Google Groups are probably overkill, your options are a Kubernetes ServiceAccount or a GCloud IAM service account. For this answer, we'll go with the latter.

    Here are the steps:

    1. Create a GCloud IAM service account in the same project as your Kubernetes cluster.
    2. Create a local gcloud configuration to avoid cluttering your default one. Just as you want to create a new KUBECONFIG context rather than modifying the user of your current context, this does the equivalent thing but for gcloud itself rather than kubectl. Run the command gcloud config configurations create <configuration-name>.
    3. Associate this configuration with your GCloud IAM service account: gcloud auth activate-service-account <service_account_email> --key-file=</path/to/service/key.json>.
    4. Add a context and user to your KUBECONFIG file so that you can authenticate to your GKE cluster as this GCloud IAM service account as follows:

      contexts:
      - ...
      - ...
      - name: <cluster-name>-read-only
        context:
          cluster: <cluster-name>
          user: <service-account-name>
      users:
      - ...
      - ...
      - name: <service-account-name>
        user:
          auth-provider:
            name: gcp
            config:
              cmd-args: config config-helper --format=json --configuration=<configuration-name>
              cmd-path: </path/to/gcloud/cli>
              expiry-key: '{.credential.token_expiry}'
              token-key: '{.credential.access_token}'
      
    5. Add a ClusterRoleBinding so that this subject has read-only access to the cluster:

      $ cat <<EOF | kubectl apply -f -
      kind: ClusterRoleBinding
      apiVersion: rbac.authorization.k8s.io/v1
      metadata:
        name: <any-name>
      subjects:
      - kind: User
        name: <service-account-email>
      roleRef:
        kind: ClusterRole
        name: view
        apiGroup: rbac.authorization.k8s.io
      EOF
      

    Try it out:

    $ kubectl use-context <cluster-name>-read-only
    $ kubectl get all --all-namespaces
    # see all the pods and stuff
    $ kubectl create namespace foo
    Error from server (Forbidden): namespaces is forbidden: User "<service-account-email>" cannot create resource "namespaces" in API group "" at the cluster scope: Required "container.namespaces.create" permission.
    
    $ kubectl use-context <original-namespace>
    $ kubectl get all --all-namespaces
    # see all the pods and stuff
    $ kubectl create namespace foo
    namespace/foo created