feat(kubernetes): allow setting cluster name in in-cluster mode (#7695)

This commit is contained in:
Sergio Garcia
2025-05-12 06:28:04 -04:00
committed by GitHub
parent 0a71628298
commit c8d2a44ab0
5 changed files with 84 additions and 4 deletions
+16
View File
@@ -20,3 +20,19 @@ kubectl logs prowler-XXXXX --namespace prowler-ns
???+ note
By default, `prowler` will scan all namespaces in your active Kubernetes context. Use the [`--namespace`](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/kubernetes/namespace/) flag to specify the namespace(s) to be scanned.
???+ tip "Identifying the cluster in reports"
When running in in-cluster mode, the Kubernetes API does not expose the actual cluster name by default.
To uniquely identify the cluster in logs and reports, you can:
- Use the `--cluster-name` flag to manually set the cluster name:
```bash
prowler -p kubernetes --cluster-name production-cluster
```
- Or set the `CLUSTER_NAME` environment variable:
```yaml
env:
- name: CLUSTER_NAME
value: production-cluster
```
+1
View File
@@ -207,6 +207,7 @@ class Provider(ABC):
kubeconfig_file=arguments.kubeconfig_file,
context=arguments.context,
namespace=arguments.namespace,
cluster_name=arguments.cluster_name,
config_path=arguments.config_file,
mutelist_path=arguments.mutelist_file,
fixer_config=fixer_config,
@@ -70,6 +70,7 @@ class KubernetesProvider(Provider):
kubeconfig_file: str = None,
context: str = None,
namespace: list = None,
cluster_name: str = None,
config_path: str = None,
config_content: dict = {},
fixer_config: dict = {},
@@ -84,6 +85,7 @@ class KubernetesProvider(Provider):
kubeconfig_file (str): Path to the kubeconfig file.
kubeconfig_content (str or dict): Content of the kubeconfig file.
context (str): Context name.
cluster_name (str): Cluster name.
namespace (list): List of namespaces.
config_content (dict): Audit configuration.
config_path (str): Path to the configuration file.
@@ -147,7 +149,9 @@ class KubernetesProvider(Provider):
"""
logger.info("Instantiating Kubernetes Provider ...")
self._session = self.setup_session(kubeconfig_file, kubeconfig_content, context)
self._session = self.setup_session(
kubeconfig_file, kubeconfig_content, context, cluster_name
)
if not namespace:
logger.info("Retrieving all namespaces ...")
self._namespaces = self.get_all_namespaces()
@@ -227,6 +231,7 @@ class KubernetesProvider(Provider):
kubeconfig_file: str = None,
kubeconfig_content: Union[dict, str] = None,
context: str = None,
cluster_name: str = None,
) -> KubernetesSession:
"""
Sets up the Kubernetes session.
@@ -235,7 +240,7 @@ class KubernetesProvider(Provider):
kubeconfig_file (str): Path to the kubeconfig file.
kubeconfig_content (str or dict): Content of the kubeconfig file.
context (str): Context name.
cluster_name (str): Cluster name.
Returns:
Tuple: A tuple containing the API client and the context.
@@ -270,11 +275,15 @@ class KubernetesProvider(Provider):
# If the kubeconfig file is not found, try to use the in-cluster config
logger.info("Using in-cluster config")
config.load_incluster_config()
# Use CLI flag or env var to set cluster name
resolved_cluster_name = cluster_name or os.getenv(
"CLUSTER_NAME", "in-cluster"
)
context = {
"name": "In-Cluster",
"context": {
"cluster": "in-cluster", # Placeholder, as the real cluster name is not available
"user": "service-account-name", # Also a placeholder
"cluster": resolved_cluster_name,
"user": "service-account-name",
},
}
return KubernetesSession(
@@ -27,3 +27,9 @@ def init_parser(self):
metavar="NAMESPACES",
help="The namespaces where to scan for the Kubernetes resources. By default, Prowler will scan all namespaces available.",
)
k8s_auth_subparser.add_argument(
"--cluster-name",
nargs="?",
metavar="CLUSTER_NAME",
help="Manually specify the cluster name in in-cluster mode, by default it will be 'in-cluster'",
)
@@ -1,6 +1,8 @@
from argparse import Namespace
from unittest.mock import patch
from kubernetes.config.config_exception import ConfigException
from kubernetes import client
from prowler.config.config import (
default_config_file_path,
@@ -305,3 +307,49 @@ class TestKubernetesProvider:
assert connection.is_connected
assert connection.error is None
def test_kubernetes_provider_incluster_with_env_var(self, monkeypatch):
monkeypatch.setenv("CLUSTER_NAME", "env-cluster-name")
with (
patch(
"kubernetes.config.load_kube_config",
side_effect=ConfigException("No kubeconfig"),
),
patch("kubernetes.config.load_incluster_config", return_value=None),
patch("prowler.providers.kubernetes.kubernetes_provider.client.ApiClient"),
patch(
"prowler.providers.kubernetes.kubernetes_provider.KubernetesProvider.get_all_namespaces",
return_value=["default"],
),
):
session = KubernetesProvider.setup_session(
kubeconfig_file=None,
kubeconfig_content=None,
context=None,
cluster_name=None,
)
assert isinstance(session, KubernetesSession)
assert session.context["context"]["cluster"] == "env-cluster-name"
def test_kubernetes_provider_incluster_with_cli_flag(self):
with (
patch(
"kubernetes.config.load_kube_config",
side_effect=ConfigException("No kubeconfig"),
),
patch("kubernetes.config.load_incluster_config", return_value=None),
patch("prowler.providers.kubernetes.kubernetes_provider.client.ApiClient"),
patch(
"prowler.providers.kubernetes.kubernetes_provider.KubernetesProvider.get_all_namespaces",
return_value=["default"],
),
):
session = KubernetesProvider.setup_session(
kubeconfig_file=None,
kubeconfig_content=None,
context=None,
cluster_name="cli-cluster-name",
)
assert isinstance(session, KubernetesSession)
assert session.context["context"]["cluster"] == "cli-cluster-name"