k8s-self-managed-controller-exposure
Check a self-managed Kubernetes cluster's controller manager and scheduler for exposed endpoints and for credentials shared across every controller.
The file the binary runs.
This is the whole agent, unabridged, exactly as it ships.
Agent file · built-in · k8s-self-managed-controller-exposure.mdResearch whether this cluster's controller manager and scheduler expose their endpoints on the node network and whether every controller runs as one identity. Both components are read from their own pods, so this agent applies to a self-managed control plane only: where a pod enumeration that answered returns no such pods, this cluster does not run the controller manager and scheduler as pods, whether the control plane is provider-managed or self-managed and runs them as host processes, and the answer is that this agent does not apply rather than a page of unresolved arguments. An enumeration that failed is not that answer and settles nothing: report it as unresolved rather than as an absent control plane. This agent has a single stage, and the reason is that none of the arguments it reads means anything on its own: each one has to be qualified by a cluster-wide read - which pods are scheduled where, which NetworkPolicies exist, which service accounts exist, which ConfigMap carries the cluster's certificate authority bundle - before it can be reported at all. There is no subset of this work that settles the question more cheaply, so there is nothing for a gate to defer.
Take --bind-address and --secure-port on both components, --authentication-kubeconfig and --authorization-kubeconfig on both, and --use-service-account-credentials, --service-account-private-key-file and --root-ca-file on the controller manager.
An argument the command does not carry is not unread: each component takes its own documented default for that version, so resolve the effective value and report the argument as defaulted rather than as configured or as missing. --bind-address defaults to 0.0.0.0 on both components and --use-service-account-credentials defaults to false, so an absent argument in either place is the finding rather than a gap in the read, and it is the same finding as the argument present and open. --secure-port defaults to 10257 on the controller manager and 10259 on the scheduler; --secure-port=0 disables that endpoint outright, so resolve it before treating --bind-address as exposing anything.
A denied, unreachable, partial or empty read is not a clean result: name the resource and the field you could not read and mark it unresolved rather than reporting clean, and name the bound beside the finding. Where every read above is denied, the report is that list of unresolved reads.
Where nothing meets the question above, say so in the report's first sentence and before any count or inventory, naming the objects it asks about rather than referring to them, and say there which of three answers it is: they are absent, or they are present and clean, or they were not read. An enumeration that answered with nothing still answered, and only a read that did not complete is unread.
On a component whose --secure-port is not 0, report --bind-address values outside the loopback range (127.0.0.0/8 or ::1) together with whether --authentication-kubeconfig and --authorization-kubeconfig are set on the same component, whether --secure-port carries a custom nonzero value, and with whether the component's pod sets hostNetwork: true and whether any NetworkPolicy restricts pod traffic to node addresses. A --secure-port of 0 disables the endpoint, and there is no port to report exposed. A bound address with neither kubeconfig set denies every path but /healthz, /readyz and /livez by default, so it exposes only those health paths on the node network rather than the metrics and debug endpoints. With only --authentication-kubeconfig set, that same default deny still applies, since it is the authorization delegation that admits any path beyond the three; report it the same as neither set. With only --authorization-kubeconfig set, the component authenticates the caller as anonymous and checks that identity against the cluster's RBAC, so reachability beyond the three paths depends on whether the cluster authorizes system:anonymous for them; report that grant rather than assuming denial. With both kubeconfigs set, the metrics and debug endpoints become reachable too, and the finding is their reachability rather than an open surface, since authentication and authorization still gate them. A NetworkPolicy that names node addresses is not evidence the endpoint is restricted, in either network mode, since whether a CNI enforces it against the pod's traffic is outside what this agent reads; report the policy and the hostNetwork setting rather than reporting the endpoint restricted. Report which of those states each component is in rather than reporting the address alone.
Report --use-service-account-credentials=false together with the per-controller service accounts present in kube-system, which are the identities not being used while every controller runs as the controller-manager identity.
Read which pods mount the directory containing the file named by --service-account-private-key-file, and report with their namespaces the ones that are not a component the key belongs to. The API server and the controller manager mount that directory because they sign and verify with what is in it, so reporting them is reporting the control plane running. That file is the signing key for legacy secret-based tokens, so a hostPath mount of the directory holding it into anything else permits minting such a token for any service account in the cluster.
Report --root-ca-file unset together with the count of service accounts in the cluster. It fills the bundle in a legacy token secret only; a projected token takes its bundle from the kube-root-ca.crt ConfigMap, so read that ConfigMap in the namespaces those accounts sit in and report which of the two paths carries a bundle to verify the API server against.
Report a --bind-address outside the loopback range as intentional where the evidence supports it: --authentication-kubeconfig and --authorization-kubeconfig both set on that component. A NetworkPolicy that names node addresses is not that evidence on its own, since whether a CNI enforces it against traffic to a node is outside what this agent reads. Name the evidence. A bound endpoint with no such evidence is not intentional.
Order findings by risk, most consequential first.
Call shapes a run has proven:
Kubernetes API: GET /api/v1/pods, GET /api/v1/configmaps, GET /api/v1/serviceaccounts and GET /api/v1/namespaces; network policies at GET /apis/networking.k8s.io/v1/networkpolicies; the server version at GET /version.
Copy and edit.
A file in ~/.cynative/agents/ wins over a built-in of the same name. Give your copy a distinct name to keep both.
An agent supplies text only. Approvals, connector authorization and the read-only ceilings are enforced at every tool call regardless of what the agent says, and the LLM can neither introduce nor select an agent.