Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ec2): Amazon EC2 Instances Should Not Use Multiple ENIs #4935

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ec2_instance_uses_single_eni",
"CheckTitle": "Amazon EC2 instances should not use multiple ENIs",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:instance/resource-id",
"Severity": "low",
"ResourceType": "AwsEc2Instance",
"Description": "This control checks whether an EC2 instance uses multiple Elastic Network Interfaces (ENIs) or Elastic Fabric Adapters (EFAs). This control passes if a single network adapter is used. The control includes an optional parameter list to identify the allowed ENIs. This control also fails if an EC2 instance that belongs to an Amazon EKS cluster uses more than one ENI. If your EC2 instances need to have multiple ENIs as part of an Amazon EKS cluster, you can suppress those control findings.",
"Risk": "Multiple ENIs can cause dual-homed instances, meaning instances that have multiple subnets. This can add network security complexity and introduce unintended network paths and access.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ec2-instance-multiple-eni-check.html",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ec2-controls.html#ec2-17",
"Terraform": ""
},
"Recommendation": {
"Text": "To detach a network interface from an EC2 instance, follow the instructions in the Amazon EC2 User Guide.",
"Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#detach_eni"
}
},
"Categories": [
"trustboundaries"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client


class ec2_instance_uses_single_eni(Check):
def execute(self):
findings = []
for instance in ec2_client.instances:
report = Check_Report_AWS(self.metadata())
report.region = instance.region
report.resource_id = instance.id
report.resource_arn = instance.arn
report.resource_tags = instance.tags
eni_types = {"efa": [], "interface": [], "trunk": []}
if not instance.network_interfaces:
report.status = "PASS"
report.status_extended = (
f"EC2 Instance {instance.id} has no network interfaces attached."
)
else:
for eni_id in instance.network_interfaces:
if ec2_client.network_interfaces[eni_id].type in eni_types:
eni_types[ec2_client.network_interfaces[eni_id].type].append(
eni_id
)

message_status_extended = ""
if (
len(eni_types["efa"])
+ len(eni_types["interface"])
+ len(eni_types["trunk"])
> 1
):
report.status = "FAIL"
message_status_extended = (
f"EC2 Instance {instance.id} uses multiple ENIs: ("
)
else:
report.status = "PASS"
message_status_extended = (
f"EC2 Instance {instance.id} uses only one ENI: ("
)

if eni_types["efa"]:
message_status_extended += f" EFAs: {eni_types['efa']}"
if eni_types["interface"]:
message_status_extended += f" Interfaces: {eni_types['interface']}"
if eni_types["trunk"]:
message_status_extended += f" Trunks: {eni_types['trunk']}"
report.status_extended = message_status_extended + " )."

findings.append(report)

return findings
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def execute(self):
for version in template.versions:
if not version.template_data.user_data:
continue

user_data = b64decode(version.template_data.user_data)

try:
Expand Down
7 changes: 7 additions & 0 deletions prowler/providers/aws/services/ec2/ec2_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ def _describe_instances(self, regional_client):
if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources)
):
enis = []
for eni in instance.get("NetworkInterfaces", []):
network_interface_id = eni.get("NetworkInterfaceId")
if network_interface_id:
enis.append(network_interface_id)
self.instances.append(
Instance(
id=instance["InstanceId"],
Expand Down Expand Up @@ -100,6 +105,7 @@ def _describe_instances(self, regional_client):
for sg in instance.get("SecurityGroups", [])
],
subnet_id=instance.get("SubnetId", ""),
network_interfaces=enis,
virtualization_type=instance.get(
"VirtualizationType"
),
Expand Down Expand Up @@ -640,6 +646,7 @@ class Instance(BaseModel):
security_groups: list[str]
subnet_id: str
instance_profile: Optional[dict]
network_interfaces: Optional[list]
virtualization_type: Optional[str]
tags: Optional[list] = []

Expand Down
Loading