The Kubernetes provider supports two authentication paths to the kube-API server:
- In-cluster - ORB itself runs as a pod and uses its mounted service-account token.
- kubeconfig - ORB runs outside the cluster and loads a kubeconfig
file (the same one
kubectluses).
The decision happens at provider initialisation and is influenced by the
in_cluster, kubeconfig_path, and context fields on
K8sProviderConfig.
in_cluster config |
/var/run/secrets/kubernetes.io exists |
Outcome |
|---|---|---|
None (default) |
yes | In-cluster auth |
None (default) |
no | kubeconfig (path / KUBECONFIG / ~/.kube/config) |
True |
yes | In-cluster auth |
True |
no | KubernetesAuthError - sentinel missing |
False |
yes | kubeconfig (the in-cluster mount is ignored) |
False |
no | kubeconfig |
The sentinel path is the canonical one used by the upstream
kubernetes Python client, so detection matches the wider ecosystem.
This is the recommended deployment shape for ORB-as-a-controller: ORB runs inside the same cluster it manages, scoped to a ServiceAccount with a tightly-scoped Role.
in_cluster: Trueis set explicitly, or- the sentinel
/var/run/secrets/kubernetes.ioexists at process start (which is automatically the case for every pod that has a mounted service-account token).
- A ServiceAccount in the namespace ORB will manage.
- A Role (or ClusterRole, for cluster-scoped watches) granting the verbs
listed in
rbac.yaml. - A RoleBinding (or ClusterRoleBinding) tying the two together.
The minimum-viable manifest lives at rbac.yaml and can be
applied directly.
Modern Kubernetes uses projected ServiceAccount tokens with
automatic rotation. The kubernetes Python client picks up the
rotated token on the next API call, so ORB does not need to restart on
rotation. No action required.
| Symptom | Likely cause | Fix |
|---|---|---|
KubernetesAuthError: Failed to load in-cluster config |
Pod's ServiceAccount token is missing or unreadable | Confirm the pod spec mounts a SA token; check automountServiceAccountToken. |
403 Forbidden on list_namespaced_pod |
Role missing pods/list |
Apply rbac.yaml. |
404 Not Found on delete_namespaced_pod |
Operating in the wrong namespace | Set ORB_K8S_NAMESPACE to the namespace the SA is bound to. |
| Watch task dies repeatedly | API-server timing out long-running connections | The provider auto-restarts watches; persistent failures usually point to an upstream proxy with aggressive idle timeouts. |
This path is used for development against a remote cluster
(kind/minikube/EKS via aws eks update-kubeconfig) and for
out-of-cluster control planes.
ORB resolves the kubeconfig file path in this order:
- The
kubeconfig_pathfield in provider config. - The
KUBECONFIGenvironment variable (the standard kubernetes precedence). - The default
~/.kube/config.
The context is resolved in this order:
- The
contextfield in provider config. - The
current-contextfield in the kubeconfig itself.
- A user / token / certificate entry in the kubeconfig that maps to a
ClusterRole or Role with the verbs in
rbac.yaml. - For EKS, the standard
aws eks update-kubeconfigflow plus an IAM user/role mapped viaaws-auth.
kind create cluster --name orb-dev
kubectl --context kind-orb-dev apply -f docs/root/providers/k8s/rbac.yaml
export ORB_K8S_CONTEXT="kind-orb-dev"
export ORB_K8S_NAMESPACE="orb"
orb machines request my-template 3aws eks update-kubeconfig --region eu-west-1 --name prod
kubectl --context arn:aws:eks:eu-west-1:123456789012:cluster/prod \
apply -f docs/root/providers/k8s/rbac.yaml
export ORB_K8S_CONTEXT="arn:aws:eks:eu-west-1:123456789012:cluster/prod"
export ORB_K8S_NAMESPACE="orb"
orb machines request my-template 3export ORB_K8S_KUBECONFIG_PATH="/etc/orb/kubeconfig-prod"
export ORB_K8S_CONTEXT="prod"| Symptom | Likely cause | Fix |
|---|---|---|
KubernetesAuthError: Failed to load kubeconfig (config_file=None, ...) |
No kubeconfig discoverable | Set kubeconfig_path or KUBECONFIG, or place a file at ~/.kube/config. |
KubernetesAuthError: ... unknown context |
Typo in context field |
Run kubectl config get-contexts to list valid names. |
| Auth works locally but fails when ORB runs as a system service | The service env does not inherit $HOME |
Set ORB_K8S_KUBECONFIG_PATH to an absolute path. |
| Auth works initially then 401s after some hours (EKS) | Federated token expired | Use aws eks update-kubeconfig with a long-lived identity, or run ORB in-cluster with IRSA. |
ORB can optionally validate Bearer tokens on its own REST API by
submitting them to the Kubernetes
authentication.k8s.io/v1 TokenReview endpoint. This lets callers
of ORB's REST API authenticate with a Kubernetes ServiceAccount token
instead of a separate credential system.
Set inbound_auth_enabled: true in the provider config (or
ORB_K8S_INBOUND_AUTH_ENABLED=true) to register the
KubeAuthStrategy with ORB's auth registry.
{
"providers": {
"k8s": {
"provider_type": "k8s",
"namespace": "orb",
"inbound_auth_enabled": true
}
}
}- An inbound REST request arrives with an
Authorization: Bearer <token>header. - ORB extracts the token and submits it to
authentication.k8s.io/v1 TokenReviewon the cluster. - If the API server reports the token is authenticated, the
system:serviceaccount:<namespace>:<name>principal is extracted. - The principal is mapped to an ORB role using the
sa_role_mappingin the auth config. Principals not in the map receive the default["user"]role. Exact matches (namespace:sa-name) take priority; wildcard namespace matches (*:sa-name) are tried next.
TokenReview is preferred over local JWKS validation because the API server is the authoritative signer — there is no possibility of ORB accepting a token the cluster itself would reject.
The ORB pod's ServiceAccount must be able to create TokenReview
objects. The recommended approach is to bind the
system:auth-delegator ClusterRole (a standard Kubernetes role that
grants precisely this permission):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: orb-auth-delegator
subjects:
- kind: ServiceAccount
name: orb
namespace: orb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegatorAlternatively, grant only tokenreviews: create on the
authentication.k8s.io API group via a targeted ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: orb-tokenreview
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["tokenreviews"]
verbs: ["create"]Both options are included (commented out) in rbac.yaml.
Configure which ServiceAccounts map to which ORB roles in the auth config section (not the provider config):
{
"auth": {
"provider_auth": {
"kubernetes": {
"sa_role_mapping": {
"orb-system:orb-admin": ["admin"],
"orb-system:orb-operator": ["operator"],
"*:ci-runner": ["user"]
}
}
}
}
}Principals not matched by any entry default to ["user"].
| Symptom | Likely cause | Fix |
|---|---|---|
403 Forbidden when ORB submits a TokenReview |
ORB's ServiceAccount lacks tokenreviews: create |
Apply the auth-delegator ClusterRoleBinding from rbac.yaml. |
Callers get Token rejected by Kubernetes API server |
Token has expired or is not a valid projected SA token | The caller must obtain a fresh projected token via kubectl create token. |
| Auth succeeds but the caller gets the wrong role | SA principal not in sa_role_mapping |
Add the namespace:sa-name entry to the mapping; or it matched a wildcard entry — check the order. |
ORB confines every kubernetes SDK import to
src/orb/providers/k8s/. The two auth helpers
(auth/in_cluster.py and auth/kubeconfig.py) are the only modules
that call into kubernetes.config; the rest of the provider uses the
configured global client. This makes the SDK trivially mockable from
unit tests and keeps the architecture-test allowlist small.