mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
Co-authored-by: Daniel Barranquero <danielbo2001@gmail.com>
This commit is contained in:
co-authored by
Daniel Barranquero
parent
106026614d
commit
3369e48260
@@ -0,0 +1 @@
|
||||
`ec2_ami_account_block_public_access` check for AWS provider, verifying AMI block public access is enabled at the account level in each Region so AMIs cannot be shared publicly
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "ec2_ami_account_block_public_access",
|
||||
"CheckTitle": "AMI block public access is enabled at the account level",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Effects/Data Exposure"
|
||||
],
|
||||
"ServiceName": "ec2",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "compute",
|
||||
"Description": "AMI block public access configuration is assessed to see whether public sharing of AMIs is blocked in the account and Region. When enabled (`block-new-sharing`), no AMI in the Region can be made public regardless of individual image permissions.",
|
||||
"Risk": "Without blocking public access, AMIs could be accidentally or maliciously shared publicly, exposing baked-in secrets, source code, and infrastructure details to unauthorized actors.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharingamis-intro.html",
|
||||
"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/image-block-public-access.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws ec2 enable-image-block-public-access --image-block-public-access-state block-new-sharing",
|
||||
"NativeIaC": "",
|
||||
"Other": "1. In the AWS console, select the target Region in the top-right.\n2. Go to EC2 > AMIs.\n3. In the AMIs page, choose Block public access for AMIs (or EC2 Dashboard > Account attributes > Data protection and security).\n4. Choose Manage and enable Block public access.\n5. Save changes.",
|
||||
"Terraform": "```hcl\nresource \"aws_ec2_image_block_public_access\" \"<example_resource_name>\" {\n state = \"block-new-sharing\" # Blocks new public sharing of AMIs in the configured Region\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable AMI block public access (`block-new-sharing`) in every active Region. Apply guardrails (SCPs) to prevent it from being disabled, and review any AMIs that are currently shared publicly.",
|
||||
"Url": "https://hub.prowler.com/check/ec2_ami_account_block_public_access"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"internet-exposed"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
class ec2_ami_account_block_public_access(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for state in ec2_client.ami_block_public_access_states:
|
||||
report = Check_Report_AWS(
|
||||
metadata=self.metadata(),
|
||||
resource=state,
|
||||
)
|
||||
report.resource_id = ec2_client.audited_account
|
||||
report.resource_arn = ec2_client.account_arn_template
|
||||
|
||||
if state.status == "block-new-sharing":
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"AMI Block Public Access is enabled in {state.region}."
|
||||
)
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"AMI Block Public Access is disabled in {state.region}."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -52,6 +52,8 @@ class EC2(AWSService):
|
||||
self.__threading_call__(self._describe_ec2_addresses)
|
||||
self.ebs_block_public_access_snapshots_states = []
|
||||
self.__threading_call__(self._get_snapshot_block_public_access_state)
|
||||
self.ami_block_public_access_states = []
|
||||
self.__threading_call__(self._get_ami_block_public_access_state)
|
||||
self.instance_metadata_defaults = []
|
||||
self.__threading_call__(self._get_instance_metadata_defaults)
|
||||
self.launch_templates = []
|
||||
@@ -498,6 +500,21 @@ class EC2(AWSService):
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def _get_ami_block_public_access_state(self, regional_client):
|
||||
try:
|
||||
self.ami_block_public_access_states.append(
|
||||
AmiBlockPublicAccess(
|
||||
status=regional_client.get_image_block_public_access_state()[
|
||||
"ImageBlockPublicAccessState"
|
||||
],
|
||||
region=regional_client.region,
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def _get_instance_metadata_defaults(self, regional_client):
|
||||
try:
|
||||
instances_in_region = self.attributes_for_regions.get(
|
||||
@@ -798,6 +815,11 @@ class EbsSnapshotBlockPublicAccess(BaseModel):
|
||||
region: str
|
||||
|
||||
|
||||
class AmiBlockPublicAccess(BaseModel):
|
||||
status: str
|
||||
region: str
|
||||
|
||||
|
||||
class InstanceMetadataDefaults(BaseModel):
|
||||
http_tokens: Optional[str]
|
||||
instances: bool
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
from unittest import mock
|
||||
|
||||
from moto import mock_aws
|
||||
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_ec2_ami_account_block_public_access:
|
||||
@mock_aws
|
||||
def test_ec2_ami_block_public_access_state_unblocked(self):
|
||||
from prowler.providers.aws.services.ec2.ec2_service import AmiBlockPublicAccess
|
||||
|
||||
ec2_client = mock.MagicMock()
|
||||
ec2_client.ami_block_public_access_states = [
|
||||
AmiBlockPublicAccess(status="unblocked", region=AWS_REGION_US_EAST_1)
|
||||
]
|
||||
ec2_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
ec2_client.region = AWS_REGION_US_EAST_1
|
||||
ec2_client.account_arn_template = (
|
||||
f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access.ec2_client",
|
||||
new=ec2_client,
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access import (
|
||||
ec2_ami_account_block_public_access,
|
||||
)
|
||||
|
||||
check = ec2_ami_account_block_public_access()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"AMI Block Public Access is disabled in {AWS_REGION_US_EAST_1}."
|
||||
)
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_ami_block_public_access_state_block_new_sharing(self):
|
||||
from prowler.providers.aws.services.ec2.ec2_service import AmiBlockPublicAccess
|
||||
|
||||
ec2_client = mock.MagicMock()
|
||||
ec2_client.ami_block_public_access_states = [
|
||||
AmiBlockPublicAccess(
|
||||
status="block-new-sharing", region=AWS_REGION_US_EAST_1
|
||||
)
|
||||
]
|
||||
ec2_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
ec2_client.region = AWS_REGION_US_EAST_1
|
||||
ec2_client.account_arn_template = (
|
||||
f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access.ec2_client",
|
||||
new=ec2_client,
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access import (
|
||||
ec2_ami_account_block_public_access,
|
||||
)
|
||||
|
||||
check = ec2_ami_account_block_public_access()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"AMI Block Public Access is enabled in {AWS_REGION_US_EAST_1}."
|
||||
)
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_ami_block_public_access_no_resources(self):
|
||||
ec2_client = mock.MagicMock()
|
||||
ec2_client.ami_block_public_access_states = []
|
||||
ec2_client.audited_account = AWS_ACCOUNT_NUMBER
|
||||
ec2_client.region = AWS_REGION_US_EAST_1
|
||||
ec2_client.account_arn_template = (
|
||||
f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account"
|
||||
)
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_aws_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access.ec2_client",
|
||||
new=ec2_client,
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_account_block_public_access.ec2_ami_account_block_public_access import (
|
||||
ec2_ami_account_block_public_access,
|
||||
)
|
||||
|
||||
check = ec2_ami_account_block_public_access()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
Reference in New Issue
Block a user