From 1be9a58e9341bef26683f3b46d0bd9e3347d5da8 Mon Sep 17 00:00:00 2001 From: pedrooot Date: Tue, 6 Oct 2026 16:33:03 +0200 Subject: [PATCH] docs(kubernetes): document private cluster allowlist --- docs/user-guide/providers/kubernetes/misc.mdx | 15 +++++++++++++++ .../providers/kubernetes/exceptions/exceptions.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/providers/kubernetes/misc.mdx b/docs/user-guide/providers/kubernetes/misc.mdx index 49d80ea69a..0df4556f24 100644 --- a/docs/user-guide/providers/kubernetes/misc.mdx +++ b/docs/user-guide/providers/kubernetes/misc.mdx @@ -43,3 +43,18 @@ prowler kubernetes ... This will allow Prowler to connect to the cluster even if the proxy uses a self-signed certificate. These environment variables are supported both when using an external `kubeconfig` and in in-cluster mode. + +## Private Cluster Endpoints + +By default, Prowler rejects a `kubeconfig` whose `clusters[*].cluster.server` resolves to a non-public address, as an SSRF defense. Clusters reached over a private network are a legitimate setup, so to scan them declare the trusted ranges explicitly: + +```console +export PROWLER_ALLOWED_PRIVATE_NETWORKS="10.20.0.0/16,192.168.65.254/32" +prowler kubernetes ... +``` + +The value is a comma-separated list of IPs and CIDRs. A resolved address inside an allowlisted range is permitted; every other non-public address stays blocked, so link-local (`169.254.169.254`), loopback, and the rest of the internal network remain protected. A kubeconfig declaring several clusters is rejected when any one of them resolves outside the allowlist. Malformed entries are rejected, and a non-empty allowlist is logged as a relaxed security control. When unset, only public addresses are reachable. + +The variable is read by the process that runs the scan. In Prowler App that is the worker, not the API, so setting it only on the API container has no effect. The same variable applies to the IaC and OpenStack providers. + +The check resolves the cluster hostname locally, before the Kubernetes client connects. If egress is only possible through `HTTPS_PROXY` and the hostname cannot be resolved locally, declare the cluster's address range in `PROWLER_ALLOWED_PRIVATE_NETWORKS` or make the name resolvable to the scanning process. diff --git a/prowler/providers/kubernetes/exceptions/exceptions.py b/prowler/providers/kubernetes/exceptions/exceptions.py index 1cadbf431f..2b2fd92a07 100644 --- a/prowler/providers/kubernetes/exceptions/exceptions.py +++ b/prowler/providers/kubernetes/exceptions/exceptions.py @@ -36,7 +36,7 @@ class KubernetesBaseException(ProwlerException): }, (4007, "KubernetesKubeConfigServerNotAllowedError"): { "message": "The provided kube-config points to a cluster server that is not an allowed destination.", - "remediation": "Make sure every cluster server in the kube-config is a public HTTP or HTTPS endpoint. Please, refer to the Kubernetes config documentation: https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/#Config", + "remediation": "Make sure every cluster server in the kube-config is a public HTTP or HTTPS endpoint. To scan a cluster that lives on a private network, declare the trusted ranges in the PROWLER_ALLOWED_PRIVATE_NETWORKS environment variable of the process running the scan. Please, refer to the Kubernetes config documentation: https://kubernetes.io/docs/reference/config-api/kubeconfig.v1/#Config", }, }