> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cynative.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Kubernetes Connector: Self-Managed Cluster Security Research

> Configure Cynative's self-managed Kubernetes connector: kubeconfig discovery, live view RBAC enforcement, custom ClusterRole, and API server access.

The `kubernetes` connector provides Kubernetes API access to self-managed clusters (k3s, kubeadm, k0s, on-prem, bare-metal, dev) that are not fronted by a cloud provider API. It discovers credentials from the local kubeconfig, exactly as `kubectl` does, and enforces the cluster's own RBAC policy client-side before attaching any credential.

**Connector id:** `kubernetes`

For managed Kubernetes clusters on cloud providers, use the dedicated connectors instead:

* **EKS** → [AWS connector](/connectors/aws)
* **GKE** → [GCP connector](/connectors/gcp)
* **AKS** → [Azure connector](/connectors/azure)

## Quick start

Run Cynative from an environment with a working kubeconfig for the target cluster (the same one `kubectl` uses):

```bash theme={null}
kubectl config use-context my-cluster
cynative -p "review RBAC policies in my Kubernetes cluster"
```

## Credential discovery

### Credential sources

The connector discovers the kubeconfig exactly as `kubectl` does:

* `KUBECONFIG` — a list of kubeconfig files client-go loads and merges (colon-separated on Unix, semicolon-separated on Windows) before falling back to `~/.kube/config`. This is the sole targeting and discovery signal.

It selects the `current-context` and supports exactly two credential modes:

* **Static bearer token** — `users[].user.token`, or `users[].user.tokenFile` (re-read per request, so a rotated ServiceAccount token is picked up).
* **Client certificate (mTLS)** — `users[].user.client-certificate[-data]` plus `users[].user.client-key[-data]`.

The connector **fails closed and is not registered** when the selected context uses an `exec` credential plugin, an `auth-provider` plugin, impersonation, basic-auth username/password, `insecure-skip-tls-verify: true`, or a `proxy-url`. This means typical cloud kubeconfig contexts (GKE/EKS/AKS use `exec` plugins such as `gke-gcloud-auth-plugin`) are intentionally rejected — use the dedicated `eks`, `gke`, or `aks` connectors for those clusters.

It also fails closed on: a non-`https` scheme, a non-ASCII host (write internationalized names in punycode), an address literal carrying a zone identifier, a port outside 1–65535, or no usable bearer/client-cert credential.

**References:**

* [Organizing cluster access using kubeconfig files](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) — `KUBECONFIG`, the default `~/.kube/config`, and `current-context`.
* [kubectl config use-context](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_config/kubectl_config_use-context/) — switching which cluster this connector targets.

### Registration and validation

After parsing the kubeconfig, Cynative **eagerly validates the cluster at startup** with a dial-guarded `GET /apis/rbac.authorization.k8s.io/v1/clusterroles/<configured-role>` fetch before registering. The connector registers only if the cluster is reachable **and** the fetch succeeds.

This contrasts with the managed connectors (`eks`, `gke`, `aks`), whose validation is gated on the parent-cloud credential and whose Kubernetes API is validated only on the first request.

### Setup prerequisite: reading the ClusterRole

The configured identity must be allowed to **read the configured ClusterRole** (default `view`). The built-in `view` role does **not** include `get clusterroles`, so an identity bound only to `view` cannot read its own policy source. Grant read access explicitly:

```yaml theme={null}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cynative-read-view-role
rules:
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["clusterroles"]
    resourceNames: ["view"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cynative-read-view-role
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cynative-read-view-role
subjects:
  - kind: ServiceAccount
    name: <your-connector-serviceaccount>
    namespace: <its-namespace>
```

In practice, cloud connector credentials often already carry the needed read access, so this prerequisite most often affects hand-crafted self-managed identities.

## Target selection

The `kubernetes` connector targets the single cluster named by the local kubeconfig's `current-context`. There is no Cynative-level override — the connector targets the one cluster the selected context names.

* To target a different cluster file, set `$KUBECONFIG`.
* To select a different context, run `kubectl config use-context <name>`.

Cloud-style contexts (GKE/EKS/AKS use `exec` plugins) are rejected fail-closed — use the dedicated managed connectors for those.

### TLS server name (IP endpoints with DNS-only certificates)

When the cluster endpoint is an IP literal but its CA-issued serving certificate has only DNS Subject Alternative Names (no IP SAN), set `clusters[].cluster.tls-server-name` in the kubeconfig. Cynative honors it: the dial stays pinned to the IP, while TLS verification uses the configured server name.

## Request usage

Use `auth_provider: "kubernetes"`. `kubernetes_auth` is optional and empty — the connector targets the single configured cluster:

```json theme={null}
{
  "kubernetes_auth": {}
}
```

The request authority (host and port) must match the kubeconfig cluster `server`. A self-managed API server typically listens on port 6443, so a request that omits the port is rejected rather than sent to 443.

**Minimal example:**

```javascript theme={null}
const resp = await http_request({
  method: "GET",
  url: "https://203.0.113.10:6443/api/v1/namespaces/default/pods",
  headers: [],
  body: "",
  auth_provider: "kubernetes",
  kubernetes_auth: {}
});
console.log(resp.body);
```

## Hardening

### RBAC authorization

Cynative fetches the cluster's live configured ClusterRole (default `view`) once per cluster via `GET /apis/rbac.authorization.k8s.io/v1/clusterroles/<name>`, parses it into an allow-only RBAC policy, and caches it for the rest of the session. Each request is then classified the way the kube-apiserver classifies it — verb, API group, resource, subresource, namespace, name — and authorized against that policy before any credential is attached. Non-resource requests (such as `/version`, `/healthz`, or discovery paths) are allowed only when they are GET or HEAD in a small, safe read-only allow-set.

If the ClusterRole cannot be fetched or parsed, Cynative fails closed and denies every request.

### Host and port pinning

The request host **and** port must both equal the configured cluster endpoint's. The port is checked separately because the host check receives a port-stripped hostname and the dial guard sees only an IP — without a port check a request could reach a different TLS listener on the same address.

### Dial-time IP authorization

The dialed IP must pass the shared forbidden-address floor (loopback, link-local, cloud metadata, IPv6 ULA are always rejected) and then match the endpoint pin: an IP-literal endpoint pins to that exact address; an FQDN endpoint is re-resolved per dial and the dialed IP must be in the resolved set. RFC1918 private addresses are allowed (on-prem clusters legitimately use them).

### Credential injection

Model-supplied credentials are rejected before injection: requests carrying an `Authorization`, `Proxy-Authorization`, or `X-Ms-Authorization-Auxiliary` header, or URL userinfo (`user:pass@`), fail closed. Cynative attaches the bearer token (or presents the client certificate for mTLS) only after host and RBAC authorization pass.

## Cynative configuration

The authorization ClusterRole is configurable (default `view`):

```yaml theme={null}
connectors:
  kubernetes:
    cluster_role: view   # env CYNATIVE_CONNECTORS_KUBERNETES_CLUSTER_ROLE
```

The configured identity must be able to read whichever ClusterRole you select (`get clusterroles/<name>`). Widening past `view` widens Cynative's permitted verbs — including writes.

The configured ClusterRole is shown in the startup inventory, for example:

```text theme={null}
✓ kubernetes  access=default(read-only) · enforced=client · cluster role=view · cluster-host:6443
```

* `access=default(read-only)` when the ClusterRole is `view`; `access=custom` for any other ClusterRole.
* `enforced=client` — Kubernetes decouples authn from authz; the in-process ClusterRole-based authorization check is the sole client-side control.
* The trailing value is the API-server authority from the kubeconfig `server` — `https://` plus that value is the base URL requests must use.

## Defense at a glance

| Control | Status |
| - | - |
| Read-only by default | ✓ |
| Enforcement model | the cluster's live configured ClusterRole (default `view`; allow-only RBAC), via kube-apiserver-style request classification |
| Configurable exposure | ✓ · `connectors.kubernetes.cluster_role` selects the authorization ClusterRole (default `view`) |
| Credential downscoping | — · Kubernetes decouples authn from authz; no client-side downscoping primitive |
| Host pinning | ✓ host and port (kubeconfig cluster `server`) |
| Dial-time IP authorization | ✓ (exact IP for an IP-literal server, resolved set for an FQDN; RFC1918 private ranges allowed) |
| Model-supplied-credential rejection | ✓ |
| Response redaction | ✓ |

## Managed Kubernetes connectors

For managed Kubernetes services, Cynative provides dedicated connectors that resolve cluster credentials and endpoints from the cloud provider API:

| Cluster type | Connector | Cloud credential | |
| - | - | - | - |
| Amazon EKS | `eks` | AWS SDK default chain; STS-presigned Kubernetes token | → [AWS connector](/connectors/aws) |
| Google GKE | `gke` | Google Application Default Credentials | → [GCP connector](/connectors/gcp) |
| Azure AKS | `aks` | Azure credential chain; ARM `ListClusterUserCredentials` | → [Azure connector](/connectors/azure) |

All four Kubernetes connectors (including `kubernetes`) share the same RBAC authorization model, host/port pinning, dial-time IP authorization, and response redaction described in this page.

## Limitations

* **The gate trusts the fetched role.** Cynative enforces exactly the rules of the configured ClusterRole and adds no independent mutation-verb floor. Kubernetes' built-in `view` is an **aggregated** role (`rbac.authorization.k8s.io/aggregate-to-view`), so if an administrator has aggregated write rules into `view`, Cynative honors them. For least privilege, point the connector at a dedicated, non-aggregated read-only ClusterRole.
* **The configured identity must be able to read the configured ClusterRole** — see [Setup prerequisites](#setup-prerequisite-reading-the-clusterrole) above.
* **Only static bearer-token and client-certificate (mTLS) credentials** are supported. Exec credential plugins, auth-provider plugins, `insecure-skip-tls-verify`, and `proxy-url` are deliberately rejected.
* **One cluster per run** (the selected context). Multi-cluster selection is not supported in this version.
* **A `server` URL carrying a path prefix** (as some managed-cluster proxies emit) has that prefix dropped; such a cluster is not supported.
* **Credential rotation:** a bearer `tokenFile` is re-read per request, but a rotated literal `token` or rotated client certificate requires re-running Cynative.
* **IDNA / internationalized hosts** are not supported — write the hostname in punycode so the name Cynative pins is the name it dials.
* **RFC1918 dials are allowed** (on-prem clusters legitimately use private addresses), but loopback, link-local, cloud metadata addresses, and IPv6 ULA remain unconditionally denied.
* **There is no client-side credential downscoping.** Kubernetes decouples authentication from authorization; the in-process ClusterRole gate, host pinning, and dial-time IP authorization are the only client-side controls.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.