# kubectl whoami and Service Account Permission Checks

LLMS index: [llms.txt](/en/llms.txt)

---

When deploying an application to Kubernetes, the most common failure is a service account that cannot do what it should. Permission denied when creating a secret, rejection on list pods, refusal on update. kubectl whoami and kubectl auth can-i let you quickly identify exactly who cannot do what.

## kubectl whoami plugin

> [!NOTE]
> kubectl whoami is not a built-in kubectl command. It is a plugin from krew or a standalone binary. Install with `kubectl krew install whoami` or download from GitHub.

After installation the command shows the current context and associated service account:

```bash
kubectl whoami
```

Output:

```
system:serviceaccount:default:myapp
```

Without the plugin, the same information is available via:

```bash
kubectl auth can-i --list
```

The beginning of the output shows `User: system:serviceaccount:default:myapp` — that is your whoami.

## kubectl auth can-i: checking arbitrary subject permissions

The built-in `kubectl auth can-i` command checks permissions without impersonating the target subject. Syntax:

```bash
kubectl auth can-i <verb> <resource> --as=<subject>
```

The `--as` format for a service account:

```bash
# Within a namespace
kubectl auth can-i list pods \
  --as=system:serviceaccount:default:myapp

# Cross-namespace
kubectl auth can-i list pods \
  --as=system:serviceaccount:production:myapp \
  -n production
```

Responses: `yes` or `no`.

To check the full permission set of an SA:

```bash
kubectl auth can-i --list --as=system:serviceaccount:default:myapp
```

> [!TIP]
> Combining `--as` with `--list` is a fast SA permission audit. The output contains a table with resources, versions, and permitted actions.

### kubectl auth can-i flags

| Flag | Purpose |
|------|---------|
| `--as <subject>` | Subject to check |
| `-n <namespace>` | Subject's namespace (for SA) |
| `--list` | All permissions of the subject |
| `--quiet` / `-q` | Exit code only, no output |
| `--no-headers` | No table headers |

Exit code: 0 for `yes`, 1 for `no`. Useful for scripts:

```bash
kubectl auth can-i delete secrets --as=system:serviceaccount:default:myapp
if [ $? -eq 0 ]; then
  echo "SA can delete secrets — review the policy"
fi
```

## Binding ClusterRole to a Service Account: RoleBinding vs ClusterRoleBinding

A service account is bound to a role through two resources. The difference is scope.

**RoleBinding** binds a Role or ClusterRole to an SA within a single namespace:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: myapp-pod-reader
  namespace: default
subjects:
  - kind: ServiceAccount
    name: myapp
    namespace: default
roleRef:
  kind: ClusterRole        # reference to ClusterRole
  name: pod-reader        # ClusterRole name
  apiGroup: rbac.authorization.k8s.io
```

> [!NOTE]
> RoleBinding can reference either Role or ClusterRole. With Role, permissions are limited to that namespace. With ClusterRole, permissions apply within the RoleBinding's namespace but inherit all ClusterRole rules.

**ClusterRoleBinding** binds a ClusterRole cluster-wide:

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: myapp-cluster-reader
subjects:
  - kind: ServiceAccount
    name: myapp
    namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-reader
  apiGroup: rbac.authorization.k8s.io
```

| Scenario | Resource |
|----------|----------|
| SA needs permissions in one namespace only | RoleBinding → ClusterRole |
| SA needs cluster-wide permissions | ClusterRoleBinding |
| Shared role for different SA across namespaces | ClusterRoleBinding with multiple subjects |

## Subject in CSR: reading and verifying

A CSR (Certificate Signing Request) contains `spec.username` and `spec.groups`. For an SA it looks like this:

```bash
kubectl get csr <csr-name> -o jsonpath='{.spec}' | jq .
```

Output:

```json
{
  "request": "base64-encoded-certificate-request",
  "username": "system:serviceaccount:default:myapp",
  "groups": ["system:serviceaccount", "system:serviceaccounts", "system:serviceaccounts:default"],
  "uid": "...",
  "extra": {
    "authentication.kubernetes.io/credential-id": ["..."],
    "authentication.kubernetes.io/node-name": ["..."]
  }
}
```

Three components in username separated by colons: `system:serviceaccount:<namespace>:<name>`.

To check permissions of a specific SA from a CSR:

```bash
# Extract SA from CSR
SUBJECT=$(kubectl get csr <csr-name> -o jsonpath='{.spec.username}')
kubectl auth can-i list pods --as="$SUBJECT"
```

> [!WARNING]
> CSR is created by kubelet when a node connects or through ServiceAccount admission. If an SA uses the TokenRequest API (the modern approach), no CSR is generated — the token is issued directly.

## Quick check in CI/CD

In a pipeline you need to confirm the deploy service account has sufficient rights before running manifests:

```bash
#!/bin/bash
SA="system:serviceaccount:ci-runner:deployer"
RESOURCES=("pods" "services" "configmaps" "secrets")

for RES in "${RESOURCES[@]}"; do
  kubectl auth can-i create "$RES" --as="$SA" -n "$NAMESPACE" || {
    echo "ERROR: SA cannot create $RES"
    exit 1
  }
done

echo "Permissions confirmed"
kubectl auth can-i get pods --as="$SA" -n "$NAMESPACE"
```

> [!TIP]
> Add this check after `kubectl apply` or `helm template` — an early exit is cheaper than a crashed pod with ImagePullBackOff due to missing imagePullSecrets.

Useful output for diagnostics in CI logs:

```bash
kubectl auth can-i --list --as="system:serviceaccount:default:myapp" --no-headers
```

Lines without headers are easy to grep for suspicious wildcards like `*/*` or `*/delete`.
