feat(aws): add 2 new Amazon EKS checks from CIS (#3439)

Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Sergio Garcia
2024-02-27 17:48:56 +01:00
committed by GitHub
parent 6d44eea11c
commit 354677bc7a
13 changed files with 362 additions and 33 deletions

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "eks_cluster_network_policy_enabled",
"CheckTitle": "Ensure Network Policy is Enabled and Set as Appropriate",
"CheckType": [
"Security",
"Configuration"
],
"ServiceName": "eks",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "high",
"ResourceType": "AwsEksCluster",
"Description": "Ensure that Network Policy is enabled and set as appropriate in Amazon EKS clusters. Network Policy provides pod-level firewalling to restrict traffic between sources, enhancing network security within the cluster.",
"Risk": "Without proper Network Policy settings, pods within the cluster may be vulnerable to unauthorized access and communication.",
"RelatedUrl": "https://docs.aws.amazon.com/eks/latest/userguide/eks-networking-add-ons.html",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/EKS/security-groups.html",
"Terraform": "https://docs.bridgecrew.io/docs/bc_aws_kubernetes_1#terraform"
},
"Recommendation": {
"Text": "Enable and configure Network Policy to enhance network security within the EKS cluster.",
"Url": "https://docs.aws.amazon.com/eks/latest/userguide/eks-networking-add-ons.html"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Enabling Network Policy may cause a rolling update of all cluster nodes and consumes additional resources."
}

View File

@@ -0,0 +1,22 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.eks.eks_client import eks_client
class eks_cluster_network_policy_enabled(Check):
def execute(self):
findings = []
for cluster in eks_client.clusters:
report = Check_Report_AWS(self.metadata())
report.region = cluster.region
report.resource_id = cluster.name
report.resource_arn = cluster.arn
report.resource_tags = cluster.tags
report.status = "FAIL"
report.status_extended = f"EKS cluster {cluster.name} does not have a Network Policy. Cluster security group ID is not set."
if cluster.security_group_id:
report.status = "PASS"
report.status_extended = f"EKS cluster {cluster.name} has a Network Policy with the security group {cluster.security_group_id}."
findings.append(report)
return findings

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "eks_cluster_private_nodes_enabled",
"CheckTitle": "Ensure Clusters are created with Private Nodes",
"CheckType": [
"Security",
"Configuration"
],
"ServiceName": "EKS",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "AwsEksCluster",
"Description": "Ensure that clusters are created with private nodes, disabling public IP addresses for cluster nodes. Private nodes have no public IP addresses, restricting access to internal networks and enhancing security.",
"Risk": "Without private nodes, cluster nodes may have public IP addresses, increasing the attack surface and exposing them to potential threats from the internet.",
"RelatedUrl": "https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html",
"Remediation": {
"Code": {
"CLI": "aws eks update-cluster-config --region region-code --name my-cluster --resources-vpc-config endpointPrivateAccess=true",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Update the cluster configuration to enable private nodes, disabling public IP addresses for cluster nodes.",
"Url": "https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "Enabling private nodes restricts outbound access to the public internet. If outbound internet access is required, Cloud NAT or a NAT gateway can be used."
}

View File

@@ -0,0 +1,23 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.eks.eks_client import eks_client
class eks_cluster_private_nodes_enabled(Check):
def execute(self):
findings = []
for cluster in eks_client.clusters:
report = Check_Report_AWS(self.metadata())
report.region = cluster.region
report.resource_id = cluster.name
report.resource_arn = cluster.arn
report.resource_tags = cluster.tags
report.status = "PASS"
report.status_extended = (
f"EKS cluster {cluster.name} is created with private nodes."
)
if not cluster.endpoint_private_access:
report.status = "FAIL"
report.status_extended = f"Cluster endpoint private access is not enabled for EKS cluster {cluster.name}."
findings.append(report)
return findings

View File

@@ -54,6 +54,13 @@ class EKS(AWSService):
"clusterLogging" "clusterLogging"
][0]["enabled"], ][0]["enabled"],
) )
if (
"clusterSecurityGroupId"
in describe_cluster["cluster"]["resourcesVpcConfig"]
):
cluster.security_group_id = describe_cluster["cluster"][
"resourcesVpcConfig"
]["clusterSecurityGroupId"]
if ( if (
"endpointPublicAccess" "endpointPublicAccess"
in describe_cluster["cluster"]["resourcesVpcConfig"] in describe_cluster["cluster"]["resourcesVpcConfig"]
@@ -95,6 +102,7 @@ class EKSCluster(BaseModel):
arn: str arn: str
region: str region: str
logging: EKSClusterLoggingEntity = None logging: EKSClusterLoggingEntity = None
security_group_id: str = None
endpoint_public_access: bool = None endpoint_public_access: bool = None
endpoint_private_access: bool = None endpoint_private_access: bool = None
public_access_cidrs: list[str] = None public_access_cidrs: list[str] = None

View File

@@ -2,12 +2,15 @@ from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.eks.eks_service import EKSCluster from prowler.providers.aws.services.eks.eks_service import EKSCluster
from tests.providers.aws.audit_info_utils import (
AWS_REGION = "eu-west-1" AWS_ACCOUNT_NUMBER,
AWS_ACCOUNT_NUMBER = "123456789012" AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test" cluster_name = "cluster_test"
cluster_arn = f"arn:aws:eks:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}" cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_cluster_kms_cmk_encryption_in_secrets_enabled: class Test_eks_cluster_kms_cmk_encryption_in_secrets_enabled:
@@ -33,7 +36,7 @@ class Test_eks_cluster_kms_cmk_encryption_in_secrets_enabled:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
encryptionConfig=False, encryptionConfig=False,
) )
) )
@@ -64,7 +67,7 @@ class Test_eks_cluster_kms_cmk_encryption_in_secrets_enabled:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
encryptionConfig=True, encryptionConfig=True,
) )
) )

View File

@@ -0,0 +1,96 @@
from unittest import mock
from prowler.providers.aws.services.eks.eks_service import EKSCluster
from tests.providers.aws.audit_info_utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test"
cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_cluster_network_policy_enabled:
def test_no_clusters(self):
eks_client = mock.MagicMock
eks_client.clusters = []
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_network_policy_enabled.eks_cluster_network_policy_enabled import (
eks_cluster_network_policy_enabled,
)
check = eks_cluster_network_policy_enabled()
result = check.execute()
assert len(result) == 0
def test_cluster_without_sg(self):
eks_client = mock.MagicMock
eks_client.clusters = []
eks_client.clusters.append(
EKSCluster(
name=cluster_name,
arn=cluster_arn,
region=AWS_REGION_EU_WEST_1,
logging=None,
)
)
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_network_policy_enabled.eks_cluster_network_policy_enabled import (
eks_cluster_network_policy_enabled,
)
check = eks_cluster_network_policy_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"EKS cluster {cluster_name} does not have a Network Policy. Cluster security group ID is not set."
)
assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_EU_WEST_1
def test_cluster_with_sg(self):
eks_client = mock.MagicMock
eks_client.clusters = []
eks_client.clusters.append(
EKSCluster(
name=cluster_name,
arn=cluster_arn,
region=AWS_REGION_EU_WEST_1,
logging=None,
security_group_id="sg-123456789",
)
)
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_network_policy_enabled.eks_cluster_network_policy_enabled import (
eks_cluster_network_policy_enabled,
)
check = eks_cluster_network_policy_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"EKS cluster {cluster_name} has a Network Policy with the security group sg-123456789."
)
assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_EU_WEST_1

View File

@@ -0,0 +1,98 @@
from unittest import mock
from prowler.providers.aws.services.eks.eks_service import EKSCluster
from tests.providers.aws.audit_info_utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test"
cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_cluster_private_nodes_enabled:
def test_no_clusters(self):
eks_client = mock.MagicMock
eks_client.clusters = []
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_private_nodes_enabled.eks_cluster_private_nodes_enabled import (
eks_cluster_private_nodes_enabled,
)
check = eks_cluster_private_nodes_enabled()
result = check.execute()
assert len(result) == 0
def test_cluster_with_private_nodes(self):
eks_client = mock.MagicMock
eks_client.clusters = []
eks_client.clusters.append(
EKSCluster(
name=cluster_name,
arn=cluster_arn,
region=AWS_REGION_EU_WEST_1,
logging=None,
public_access_cidrs=["203.0.113.5/32"],
endpoint_private_access=True,
)
)
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_private_nodes_enabled.eks_cluster_private_nodes_enabled import (
eks_cluster_private_nodes_enabled,
)
check = eks_cluster_private_nodes_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"EKS cluster {cluster_name} is created with private nodes."
)
assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_EU_WEST_1
def test_endpoint_without_private_nodes(self):
eks_client = mock.MagicMock
eks_client.clusters = []
eks_client.clusters.append(
EKSCluster(
name=cluster_name,
arn=cluster_arn,
region=AWS_REGION_EU_WEST_1,
logging=None,
endpoint_private_access=False,
)
)
with mock.patch(
"prowler.providers.aws.services.eks.eks_service.EKS",
eks_client,
):
from prowler.providers.aws.services.eks.eks_cluster_private_nodes_enabled.eks_cluster_private_nodes_enabled import (
eks_cluster_private_nodes_enabled,
)
check = eks_cluster_private_nodes_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Cluster endpoint private access is not enabled for EKS cluster {cluster_name}."
)
assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == []
assert result[0].region == AWS_REGION_EU_WEST_1

View File

@@ -2,12 +2,15 @@ from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.eks.eks_service import EKSCluster from prowler.providers.aws.services.eks.eks_service import EKSCluster
from tests.providers.aws.audit_info_utils import (
AWS_REGION = "eu-west-1" AWS_ACCOUNT_NUMBER,
AWS_ACCOUNT_NUMBER = "123456789012" AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test" cluster_name = "cluster_test"
cluster_arn = f"arn:aws:eks:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}" cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_control_plane_endpoint_access_restricted: class Test_eks_control_plane_endpoint_access_restricted:
@@ -33,7 +36,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=False, endpoint_public_access=False,
endpoint_private_access=True, endpoint_private_access=True,
@@ -60,7 +63,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1
def test_control_plane_access_restricted(self): def test_control_plane_access_restricted(self):
eks_client = mock.MagicMock eks_client = mock.MagicMock
@@ -69,7 +72,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=True, endpoint_public_access=True,
endpoint_private_access=False, endpoint_private_access=False,
@@ -96,7 +99,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1
def test_control_plane_public(self): def test_control_plane_public(self):
eks_client = mock.MagicMock eks_client = mock.MagicMock
@@ -105,7 +108,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=True, endpoint_public_access=True,
endpoint_private_access=False, endpoint_private_access=False,
@@ -132,7 +135,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1
def test_control_plane_public_and_private(self): def test_control_plane_public_and_private(self):
eks_client = mock.MagicMock eks_client = mock.MagicMock
@@ -141,7 +144,7 @@ class Test_eks_control_plane_endpoint_access_restricted:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=True, endpoint_public_access=True,
endpoint_private_access=True, endpoint_private_access=True,
@@ -168,4 +171,4 @@ class Test_eks_control_plane_endpoint_access_restricted:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1

View File

@@ -5,12 +5,15 @@ from prowler.providers.aws.services.eks.eks_service import (
EKSCluster, EKSCluster,
EKSClusterLoggingEntity, EKSClusterLoggingEntity,
) )
from tests.providers.aws.audit_info_utils import (
AWS_REGION = "eu-west-1" AWS_ACCOUNT_NUMBER,
AWS_ACCOUNT_NUMBER = "123456789012" AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test" cluster_name = "cluster_test"
cluster_arn = f"arn:aws:eks:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}" cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_control_plane_logging_all_types_enabled: class Test_eks_control_plane_logging_all_types_enabled:
@@ -36,7 +39,7 @@ class Test_eks_control_plane_logging_all_types_enabled:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
) )
) )
@@ -67,7 +70,7 @@ class Test_eks_control_plane_logging_all_types_enabled:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=EKSClusterLoggingEntity( logging=EKSClusterLoggingEntity(
types=["api", "audit", "authenticator", "controllerManager"], types=["api", "audit", "authenticator", "controllerManager"],
enabled=True, enabled=True,
@@ -101,7 +104,7 @@ class Test_eks_control_plane_logging_all_types_enabled:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=EKSClusterLoggingEntity( logging=EKSClusterLoggingEntity(
types=[ types=[
"api", "api",

View File

@@ -2,12 +2,15 @@ from re import search
from unittest import mock from unittest import mock
from prowler.providers.aws.services.eks.eks_service import EKSCluster from prowler.providers.aws.services.eks.eks_service import EKSCluster
from tests.providers.aws.audit_info_utils import (
AWS_REGION = "eu-west-1" AWS_ACCOUNT_NUMBER,
AWS_ACCOUNT_NUMBER = "123456789012" AWS_REGION_EU_WEST_1,
)
cluster_name = "cluster_test" cluster_name = "cluster_test"
cluster_arn = f"arn:aws:eks:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}" cluster_arn = (
f"arn:aws:eks:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster/{cluster_name}"
)
class Test_eks_endpoints_not_publicly_accessible: class Test_eks_endpoints_not_publicly_accessible:
@@ -33,7 +36,7 @@ class Test_eks_endpoints_not_publicly_accessible:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=True, endpoint_public_access=True,
endpoint_private_access=False, endpoint_private_access=False,
@@ -59,7 +62,7 @@ class Test_eks_endpoints_not_publicly_accessible:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1
def test_endpoint_not_public_access(self): def test_endpoint_not_public_access(self):
eks_client = mock.MagicMock eks_client = mock.MagicMock
@@ -68,7 +71,7 @@ class Test_eks_endpoints_not_publicly_accessible:
EKSCluster( EKSCluster(
name=cluster_name, name=cluster_name,
arn=cluster_arn, arn=cluster_arn,
region=AWS_REGION, region=AWS_REGION_EU_WEST_1,
logging=None, logging=None,
endpoint_public_access=False, endpoint_public_access=False,
endpoint_private_access=True, endpoint_private_access=True,
@@ -94,4 +97,4 @@ class Test_eks_endpoints_not_publicly_accessible:
assert result[0].resource_id == cluster_name assert result[0].resource_id == cluster_name
assert result[0].resource_arn == cluster_arn assert result[0].resource_arn == cluster_arn
assert result[0].resource_tags == [] assert result[0].resource_tags == []
assert result[0].region == AWS_REGION assert result[0].region == AWS_REGION_EU_WEST_1