diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_always_pull_images_plugin/apiserver_always_pull_images_plugin.py b/prowler/providers/kubernetes/services/apiserver/apiserver_always_pull_images_plugin/apiserver_always_pull_images_plugin.py index 3fa2c3aaf1..f75d7a16b2 100644 --- a/prowler/providers/kubernetes/services/apiserver/apiserver_always_pull_images_plugin/apiserver_always_pull_images_plugin.py +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_always_pull_images_plugin/apiserver_always_pull_images_plugin.py @@ -23,6 +23,7 @@ class apiserver_always_pull_images_plugin(Check): if command.startswith("--enable-admission-plugins"): if "AlwaysPullImages" in command: plugin_set = True + break if not plugin_set: break if not plugin_set: diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_audit_log_path_set/apiserver_audit_log_path_set.py b/prowler/providers/kubernetes/services/apiserver/apiserver_audit_log_path_set/apiserver_audit_log_path_set.py index d2b387febd..779d9f565a 100644 --- a/prowler/providers/kubernetes/services/apiserver/apiserver_audit_log_path_set/apiserver_audit_log_path_set.py +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_audit_log_path_set/apiserver_audit_log_path_set.py @@ -22,7 +22,6 @@ class apiserver_audit_log_path_set(Check): # Check if "--audit-log-path" is set if "--audit-log-path" in str(container.command): audit_log_path_set = True - break if not audit_log_path_set: break diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.metadata.json new file mode 100644 index 0000000000..7ce9e4a528 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_client_ca_file_set", + "CheckTitle": "Ensure that the --client-ca-file argument is set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "TLS Authentication", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is configured with the --client-ca-file argument, specifying the CA file for client authentication. This setting enables the API server to authenticate clients using certificates signed by the CA and is crucial for secure communication.", + "Risk": "If the client CA file is not set, the API server may not properly authenticate clients, potentially leading to unauthorized access.", + "RelatedUrl": "https://kubernetes.io/docs/setup/best-practices/certificates/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to include the --client-ca-file parameter with the appropriate CA file. Example: --client-ca-file=", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Ensure the API server is configured with a client CA file for secure client authentication.", + "Url": "https://kubernetes.io/docs/setup/best-practices/certificates/#certificate-paths" + } + }, + "Categories": [ + "Access Control", + "TLS Configuration" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "The client CA file is a critical component of TLS authentication and should be properly managed and securely stored." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.py b/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.py new file mode 100644 index 0000000000..2f825cc043 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_client_ca_file_set/apiserver_client_ca_file_set.py @@ -0,0 +1,31 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_client_ca_file_set(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = f"Client CA file is set appropriately in the API server in pod {pod.name}." + client_ca_file_set = False + for container in pod.containers.values(): + client_ca_file_set = False + # Check if "--client-ca-file" is set + if "--client-ca-file" in str(container.command): + client_ca_file_set = True + if not client_ca_file_set: + break + + if not client_ca_file_set: + report.status = "FAIL" + report.status_extended = f"Client CA file is not set in pod {pod.name}." + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.metadata.json new file mode 100644 index 0000000000..eee1fb19a1 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_deny_service_external_ips", + "CheckTitle": "Ensure that the DenyServiceExternalIPs is set", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "Admission Controllers", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures the DenyServiceExternalIPs admission controller is enabled, which rejects all new usage of the Service field externalIPs. Enabling this controller enhances security by preventing the misuse of the externalIPs field.", + "Risk": "Not setting the DenyServiceExternalIPs admission controller could allow users to create Services with external IPs, potentially exposing services to security risks.", + "RelatedUrl": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#denyserviceexternalips", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver manifest to include '--disable-admission-plugins=DenyServiceExternalIPs' in the API server's command arguments.", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable the DenyServiceExternalIPs admission controller by setting the '--disable-admission-plugins' argument in the kube-apiserver configuration.", + "Url": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#how-do-i-turn-off-an-admission-controller" + } + }, + "Categories": [ + "Network Policy", + "Security Best Practices" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Consider the impact on existing services before enabling this admission controller, as it can restrict the usage of external IPs in the cluster." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.py b/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.py new file mode 100644 index 0000000000..b2323bf641 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_deny_service_external_ips/apiserver_deny_service_external_ips.py @@ -0,0 +1,30 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_deny_service_external_ips(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = f"API Server has DenyServiceExternalIPs admission controller enabled in pod {pod.name}." + deny_service_external_ips = False + for container in pod.containers.values(): + deny_service_external_ips = False + for command in container.command: + if command.startswith("--disable-admission-plugins"): + if "DenyServiceExternalIPs" in (command.split("=")[1]): + deny_service_external_ips = True + if not deny_service_external_ips: + break + if not deny_service_external_ips: + report.status = "FAIL" + report.status_extended = f"API Server does not have DenyServiceExternalIPs enabled in pod {pod.name}." + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.metadata.json new file mode 100644 index 0000000000..b498fa3d0f --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_disable_profiling", + "CheckTitle": "Ensure that the --profiling argument is set to false", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "Performance", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that profiling is disabled in the Kubernetes API server. Profiling generates extensive data about the system's performance and operations, which, if not needed, should be disabled to reduce the attack surface.", + "Risk": "Enabled profiling can potentially expose detailed system and program data, which might be exploited for malicious purposes.", + "RelatedUrl": "https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to set the --profiling argument to false. Example: --profiling=false", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Disable profiling in the API server unless it is necessary for troubleshooting performance bottlenecks.", + "Url": "https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/" + } + }, + "Categories": [ + "Security Best Practices", + "Configuration Management" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Profiling is enabled by default in Kubernetes. Disabling it when not needed helps in securing the cluster." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.py b/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.py new file mode 100644 index 0000000000..fcb16739b7 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_disable_profiling/apiserver_disable_profiling.py @@ -0,0 +1,29 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_disable_profiling(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = f"Profiling is disabled in pod {pod.name}." + profiling_enabled = False + for container in pod.containers.values(): + profiling_enabled = False + # Check if "--profiling" is set to false + if "--profiling=false" not in str(container.command): + profiling_enabled = True + break + if profiling_enabled: + report.status = "FAIL" + report.status_extended = f"Profiling is enabled in pod {pod.name}." + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.metadata.json new file mode 100644 index 0000000000..2c08c0534a --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_encryption_provider_config_set", + "CheckTitle": "Ensure that the --encryption-provider-config argument is set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "Data Encryption", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is configured with the --encryption-provider-config argument to encrypt sensitive data at rest in the etcd key-value store. Encrypting data at rest prevents potential unauthorized disclosures and ensures that the sensitive data is secure.", + "Risk": "Without proper configuration of the encryption provider, sensitive data stored in etcd might not be encrypted, posing a risk of data breaches.", + "RelatedUrl": "https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to include the --encryption-provider-config parameter with the path to the EncryptionConfig file. Example: --encryption-provider-config=/path/to/EncryptionConfig/File", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure and enable encryption for data at rest in etcd using a suitable EncryptionConfig file.", + "Url": "https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#determining-whether-encryption-at-rest-is-already-enabled" + } + }, + "Categories": [ + "Data Security", + "Configuration Optimization" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Ensure that the EncryptionConfig file is correctly configured and securely stored." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.py b/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.py new file mode 100644 index 0000000000..8d78ea64b0 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_encryption_provider_config_set/apiserver_encryption_provider_config_set.py @@ -0,0 +1,34 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_encryption_provider_config_set(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = ( + f"Encryption provider config is set appropriately in pod {pod.name}." + ) + + encryption_provider_config_set = True + for container in pod.containers.values(): + # Check if "--encryption-provider-config" is set + if "--encryption-provider-config" not in str(container.command): + encryption_provider_config_set = False + break + + if not encryption_provider_config_set: + report.status = "FAIL" + report.status_extended = ( + f"Encryption provider config is not set in pod {pod.name}." + ) + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.metadata.json new file mode 100644 index 0000000000..63a5678aeb --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_etcd_cafile_set", + "CheckTitle": "Ensure that the --etcd-cafile argument is set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "etcd Connection", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is configured with the --etcd-cafile argument, specifying the Certificate Authority file for etcd client connections. This setting is important for secure communication with etcd and ensures that the API server connects to etcd with an SSL Certificate Authority file.", + "Risk": "Without proper TLS configuration, communication between the API server and etcd can be unencrypted, leading to potential security vulnerabilities.", + "RelatedUrl": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to include the --etcd-cafile parameter with the appropriate CA file. Example: --etcd-cafile=", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Ensure etcd connections from the API server are secured using the appropriate CA file.", + "Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters" + } + }, + "Categories": [ + "Data Security", + "TLS Configuration" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "It is crucial to manage and rotate the CA file securely as part of your cluster's security practices." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.py b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.py new file mode 100644 index 0000000000..bd4b65bf85 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_cafile_set/apiserver_etcd_cafile_set.py @@ -0,0 +1,31 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_etcd_cafile_set(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = ( + f"etcd CA file is set appropriately in pod {pod.name}." + ) + etcd_cafile_set = True + for container in pod.containers.values(): + # Check if "--etcd-cafile" is set + if "--etcd-cafile" not in str(container.command): + etcd_cafile_set = False + break + + if not etcd_cafile_set: + report.status = "FAIL" + report.status_extended = f"etcd CA file is not set in pod {pod.name}." + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.metadata.json new file mode 100644 index 0000000000..9806e382ab --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_etcd_tls_config", + "CheckTitle": "Ensure that the --etcd-certfile and --etcd-keyfile arguments are set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "etcd Connection", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is configured with TLS encryption for etcd client connections, using --etcd-certfile and --etcd-keyfile arguments. Setting up TLS for etcd is crucial for securing the sensitive data stored in etcd as it's the primary datastore for Kubernetes.", + "Risk": "Without TLS encryption, data stored in etcd is susceptible to eavesdropping and man-in-the-middle attacks, potentially leading to data breaches.", + "RelatedUrl": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to include TLS parameters for etcd connection. Example: --etcd-certfile= --etcd-keyfile=", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable TLS encryption for etcd client connections to secure sensitive data.", + "Url": "https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#limiting-access-of-etcd-clusters" + } + }, + "Categories": [ + "Data Security", + "Configuration Optimization" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "TLS encryption for etcd is not enabled by default and should be explicitly configured." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.py b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.py new file mode 100644 index 0000000000..0597d5d82e --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_etcd_tls_config/apiserver_etcd_tls_config.py @@ -0,0 +1,35 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_etcd_tls_config(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = ( + f"TLS configuration for etcd is set appropriately in pod {pod.name}." + ) + etcd_tls_config_set = True + for container in pod.containers.values(): + # Check if "--etcd-certfile" and "--etcd-keyfile" are set + if "--etcd-certfile" not in str( + container.command + ) and "--etcd-keyfile" not in str(container.command): + etcd_tls_config_set = False + break + + if not etcd_tls_config_set: + report.status = "FAIL" + report.status_extended = ( + f"TLS configuration for etcd is not set in pod {pod.name}." + ) + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.metadata.json new file mode 100644 index 0000000000..4ac35b054d --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_event_rate_limit", + "CheckTitle": "Ensure that the admission control plugin EventRateLimit is set", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "Admission Control", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "KubernetesAPIServer", + "Description": "This check verifies if the Kubernetes API server is configured with the EventRateLimit admission control plugin. This plugin limits the rate of events accepted by the API Server, preventing potential DoS attacks by misbehaving workloads.", + "Risk": "Without EventRateLimit, the API server could be overwhelmed by a high number of events, leading to DoS and performance issues.", + "RelatedUrl": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to include EventRateLimit in the --enable-admission-plugins argument and specify a configuration file. Example: --enable-admission-plugins=...,EventRateLimit,... --admission-control-config-file=/path/to/configuration/file", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Configure EventRateLimit as an admission control plugin for the API server to manage the rate of incoming events effectively.", + "Url": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#eventratelimit" + } + }, + "Categories": [ + "Resource Management", + "Security Best Practices" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Tuning EventRateLimit requires careful consideration of the specific requirements of your environment." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.py b/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.py new file mode 100644 index 0000000000..2e491dfcc0 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_event_rate_limit/apiserver_event_rate_limit.py @@ -0,0 +1,34 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_event_rate_limit(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = ( + f"EventRateLimit admission control plugin is set in pod {pod.name}." + ) + plugin_set = False + for container in pod.containers.values(): + plugin_set = False + for command in container.command: + if command.startswith("--enable-admission-plugins"): + if "EventRateLimit" not in (command.split("=")[1]): + plugin_set = True + break + if not plugin_set: + break + if not plugin_set: + report.status = "FAIL" + report.status_extended = f"EventRateLimit admission control plugin is not set in pod {pod.name}." + + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.metadata.json new file mode 100644 index 0000000000..733c5f768d --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_kubelet_cert_auth", + "CheckTitle": "Ensure that the --kubelet-certificate-authority argument is set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "TLS Verification", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is set up with a specified certificate authority for kubelet connections, using the --kubelet-certificate-authority argument. This setup is crucial for verifying the kubelet's certificate to prevent man-in-the-middle attacks during connections from the apiserver to the kubelet.", + "Risk": "Without the --kubelet-certificate-authority argument, connections to kubelets are not verified, increasing the risk of man-in-the-middle attacks, especially over untrusted networks.", + "RelatedUrl": "https://kubernetes.io/docs/setup/best-practices/certificates/", + "Remediation": { + "Code": { + "CLI": "Set the --kubelet-certificate-authority argument in the kube-apiserver configuration to the path of the CA certificate file. Example: --kubelet-certificate-authority=/path/to/ca-file", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable TLS verification between the apiserver and kubelets by specifying the certificate authority in the kube-apiserver configuration.", + "Url": "https://kubernetes.io/docs/setup/best-practices/certificates/#configure-certificates-manually" + } + }, + "Categories": [ + "Cluster Security", + "Communication Security" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "By default, the kube-apiserver does not verify kubelet certificates. Enabling this setting enhances the security of master-node communications." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.py b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.py new file mode 100644 index 0000000000..c10c17c789 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_cert_auth/apiserver_kubelet_cert_auth.py @@ -0,0 +1,23 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_kubelet_cert_auth(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = f"API Server has appropriate kubelet certificate authority configured in pod {pod.name}." + for container in pod.containers.values(): + if "--kubelet-certificate-authority" not in str(container.command): + report.status = "FAIL" + report.status_extended = f"API Server is missing kubelet certificate authority configuration in pod {pod.name}." + break + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.metadata.json new file mode 100644 index 0000000000..c254fdb12f --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_kubelet_tls_auth", + "CheckTitle": "Ensure that the --kubelet-client-certificate and --kubelet-client-key arguments are set as appropriate", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "TLS Authentication", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "KubernetesAPIServer", + "Description": "This check ensures that the Kubernetes API server is set up with certificate-based authentication to the kubelet. This setup requires the --kubelet-client-certificate and --kubelet-client-key arguments in the kube-apiserver configuration to be set, ensuring secure communication between the API server and kubelets.", + "Risk": "Without certificate-based authentication to kubelets, requests from the apiserver are treated as anonymous, which could lead to unauthorized access and manipulation of node resources.", + "RelatedUrl": "https://kubernetes.io/docs/setup/best-practices/certificates/", + "Remediation": { + "Code": { + "CLI": "Set the --kubelet-client-certificate and --kubelet-client-key arguments in the kube-apiserver configuration. Example: --kubelet-client-certificate=/path/to/client-certificate-file --kubelet-client-key=/path/to/client-key-file", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable TLS authentication between the apiserver and kubelets by specifying the client certificate and key in the kube-apiserver configuration.", + "Url": "https://kubernetes.io/docs/setup/best-practices/certificates/#configure-certificates-manually" + } + }, + "Categories": [ + "Cluster Security", + "Communication Security" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "By default, the kube-apiserver does not authenticate to kubelets using certificates. Enabling this increases the security posture of the cluster." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.py b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.py new file mode 100644 index 0000000000..f0ecb0f479 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_kubelet_tls_auth/apiserver_kubelet_tls_auth.py @@ -0,0 +1,26 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_kubelet_tls_auth(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = f"API Server has appropriate kubelet TLS authentication configured in pod {pod.name}." + for container in pod.containers.values(): + if "--kubelet-client-certificate" not in str( + container.command + ) and "--kubelet-client-key" not in str(container.command): + + report.status = "FAIL" + report.status_extended = f"API Server is missing kubelet TLS authentication arguments in pod {pod.name}." + break + findings.append(report) + return findings diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/__init__.py b/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.metadata.json b/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.metadata.json new file mode 100644 index 0000000000..9fc191f190 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "kubernetes", + "CheckID": "apiserver_namespace_lifecycle_plugin", + "CheckTitle": "Ensure that the admission control plugin NamespaceLifecycle is set", + "CheckType": [ + "Security", + "Configuration" + ], + "ServiceName": "apiserver", + "SubServiceName": "Admission Control", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "KubernetesAPIServer", + "Description": "This check verifies that the NamespaceLifecycle admission control plugin is enabled in the Kubernetes API server. This plugin prevents the creation of objects in non-existent or terminating namespaces, enforcing the integrity of the namespace lifecycle and availability of new objects.", + "Risk": "Without NamespaceLifecycle, objects may be created in namespaces that are being terminated, potentially leading to inconsistencies and resource conflicts.", + "RelatedUrl": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/", + "Remediation": { + "Code": { + "CLI": "Edit the kube-apiserver configuration to ensure that NamespaceLifecycle is included in the --enable-admission-plugins argument. Remove the plugin from --disable-admission-plugins if present.", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable the NamespaceLifecycle admission control plugin in the API server to enforce proper namespace management.", + "Url": "https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#namespacelifecycle" + } + }, + "Categories": [ + "Namespace Management", + "Cluster Security" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "NamespaceLifecycle plugin is usually enabled by default, ensuring proper management of namespace creation and termination." +} diff --git a/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.py b/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.py new file mode 100644 index 0000000000..7c4d7b4a49 --- /dev/null +++ b/prowler/providers/kubernetes/services/apiserver/apiserver_namespace_lifecycle_plugin/apiserver_namespace_lifecycle_plugin.py @@ -0,0 +1,40 @@ +from prowler.lib.check.models import Check, Check_Report_Kubernetes +from prowler.providers.kubernetes.services.apiserver.apiserver_client import ( + apiserver_client, +) + + +class apiserver_namespace_lifecycle_plugin(Check): + def execute(self) -> Check_Report_Kubernetes: + findings = [] + for pod in apiserver_client.apiserver_pods: + report = Check_Report_Kubernetes(self.metadata()) + report.namespace = pod.namespace + report.resource_name = pod.name + report.resource_id = pod.uid + report.status = "PASS" + report.status_extended = ( + f"NamespaceLifecycle admission control plugin is set in pod {pod.name}." + ) + + namespace_lifecycle_plugin_set = False + for container in pod.containers.values(): + namespace_lifecycle_plugin_set = False + # Check if "--enable-admission-plugins" includes "NamespaceLifecycle" + # and "--disable-admission-plugins" does not include "NamespaceLifecycle" + for command in container.command: + if command.startswith("--enable-admission-plugins"): + if "NamespaceLifecycle" in (command.split("=")[1]): + namespace_lifecycle_plugin_set = True + elif command.startswith("--disable-admission-plugins"): + if "NamespaceLifecycle" in (command.split("=")[1]): + namespace_lifecycle_plugin_set = False + if not namespace_lifecycle_plugin_set: + break + + if not namespace_lifecycle_plugin_set: + report.status = "FAIL" + report.status_extended = f"NamespaceLifecycle admission control plugin is not set in pod {pod.name}." + + findings.append(report) + return findings