fix(iam): check always if root credentials are present (#8013)

Co-authored-by: Andoni Alonso <14891798+andoniaf@users.noreply.github.com>
This commit is contained in:
Prowler Bot
2025-06-13 11:20:12 +02:00
committed by GitHub
parent ddd3a292f8
commit fc4536dca8
10 changed files with 756 additions and 113 deletions
@@ -23,7 +23,7 @@
"Terraform": ""
},
"Recommendation": {
"Text": "Use the credential report to check the user and ensure the access_key_1_active and access_key_2_active fields are set to FALSE.",
"Text": "Use the credential report to check the user and ensure the access_key_1_active and access_key_2_active fields are set to FALSE. If using AWS Organizations, consider enabling Centralized Root Management and removing individual root credentials.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html"
}
},
@@ -5,39 +5,65 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_no_root_access_key(Check):
def execute(self) -> Check_Report_AWS:
findings = []
# Check if the root credentials are managed by AWS Organizations
if (
iam_client.organization_features is not None
and "RootCredentialsManagement" not in iam_client.organization_features
):
if iam_client.credential_report:
for user in iam_client.credential_report:
if user["user"] == "<root_account>":
report = Check_Report_AWS(metadata=self.metadata(), resource=user)
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
if (
user["access_key_1_active"] == "false"
and user["access_key_2_active"] == "false"
):
report.status = "PASS"
report.status_extended = (
"Root account does not have access keys."
password_enabled = user["password_enabled"] == "true"
access_key_1_active = user["access_key_1_active"] == "true"
access_key_2_active = user["access_key_2_active"] == "true"
# Only report if root actually has credentials
if password_enabled or access_key_1_active or access_key_2_active:
report = Check_Report_AWS(
metadata=self.metadata(), resource=user
)
elif (
user["access_key_1_active"] == "true"
and user["access_key_2_active"] == "true"
):
report.status = "FAIL"
report.status_extended = (
"Root account has two active access keys."
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
# Check if organization manages root credentials
org_managed = (
iam_client.organization_features is not None
and "RootCredentialsManagement"
in iam_client.organization_features
)
else:
report.status = "FAIL"
report.status_extended = (
"Root account has one active access key."
)
findings.append(report)
if not access_key_1_active and not access_key_2_active:
report.status = "PASS"
if org_managed:
report.status_extended = (
"Root account has password configured but no access keys. "
"Consider removing individual root credentials since organizational "
"root management is active."
)
else:
report.status_extended = (
"Root account does not have access keys."
)
elif access_key_1_active and access_key_2_active:
report.status = "FAIL"
if org_managed:
report.status_extended = (
"Root account has two active access keys "
"despite organizational root management being enabled."
)
else:
report.status_extended = (
"Root account has two active access keys."
)
else:
report.status = "FAIL"
if org_managed:
report.status_extended = (
"Root account has one active access key "
"despite organizational root management being enabled."
)
else:
report.status_extended = (
"Root account has one active access key."
)
findings.append(report)
break
return findings
@@ -23,7 +23,7 @@
"Terraform": ""
},
"Recommendation": {
"Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account.",
"Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account. If using AWS Organizations, consider enabling Centralized Root Management and removing individual root credentials.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa"
}
},
@@ -7,40 +7,77 @@ class iam_root_hardware_mfa_enabled(Check):
findings = []
# This check is only available in Commercial Partition
if iam_client.audited_partition == "aws":
# Check if the root credentials are managed by AWS Organizations
if (
iam_client.organization_features is not None
and "RootCredentialsManagement" not in iam_client.organization_features
):
if iam_client.account_summary:
virtual_mfa = False
report = Check_Report_AWS(
metadata=self.metadata(),
resource=iam_client.account_summary,
)
report.region = iam_client.region
report.resource_id = "<root_account>"
report.resource_arn = iam_client.mfa_arn_template
if iam_client.credential_report:
for user in iam_client.credential_report:
if user["user"] == "<root_account>":
password_enabled = user["password_enabled"] == "true"
access_key_1_active = user["access_key_1_active"] == "true"
access_key_2_active = user["access_key_2_active"] == "true"
if (
iam_client.account_summary["SummaryMap"]["AccountMFAEnabled"]
> 0
):
for mfa in iam_client.virtual_mfa_devices:
# If the ARN of the associated IAM user of the Virtual MFA device is "arn:aws:iam::[aws-account-id]:root", your AWS root account is not using a hardware-based MFA device for MFA protection.
if "root" in mfa.get("User", {}).get("Arn", ""):
virtual_mfa = True
report.status = "FAIL"
report.status_extended = "Root account has a virtual MFA instead of a hardware MFA device enabled."
if not virtual_mfa:
report.status = "PASS"
report.status_extended = (
"Root account has a hardware MFA device enabled."
# Only report if root actually has credentials
if (
password_enabled
or access_key_1_active
or access_key_2_active
) and iam_client.account_summary:
virtual_mfa = False
report = Check_Report_AWS(
metadata=self.metadata(),
resource=user,
)
else:
report.status = "FAIL"
report.status_extended = "MFA is not enabled for root account."
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = iam_client.mfa_arn_template
findings.append(report)
# Check if organization manages root credentials
org_managed = (
iam_client.organization_features is not None
and "RootCredentialsManagement"
in iam_client.organization_features
)
if (
iam_client.account_summary["SummaryMap"][
"AccountMFAEnabled"
]
> 0
):
for mfa in iam_client.virtual_mfa_devices:
# If the ARN of the associated IAM user of the Virtual MFA device is "arn:aws:iam::[aws-account-id]:root", your AWS root account is not using a hardware-based MFA device for MFA protection.
if "root" in mfa.get("User", {}).get("Arn", ""):
virtual_mfa = True
report.status = "FAIL"
if org_managed:
report.status_extended = (
"Root account has credentials with virtual MFA "
"instead of hardware MFA despite organizational root management being enabled."
)
else:
report.status_extended = "Root account has a virtual MFA instead of a hardware MFA device enabled."
break
if not virtual_mfa:
report.status = "PASS"
if org_managed:
report.status_extended = (
"Root account has credentials with hardware MFA enabled. "
"Consider removing individual root credentials since organizational "
"root management is active."
)
else:
report.status_extended = "Root account has a hardware MFA device enabled."
else:
report.status = "FAIL"
if org_managed:
report.status_extended = (
"Root account has credentials without MFA "
"despite organizational root management being enabled."
)
else:
report.status_extended = (
"MFA is not enabled for root account."
)
findings.append(report)
return findings
@@ -23,7 +23,7 @@
"Terraform": ""
},
"Recommendation": {
"Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account.",
"Text": "Using IAM console navigate to Dashboard and expand Activate MFA on your root account. If using AWS Organizations, consider enabling Centralized Root Management and removing individual root credentials.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#id_root-user_manage_mfa"
}
},
@@ -5,28 +5,53 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_root_mfa_enabled(Check):
def execute(self) -> Check_Report_AWS:
findings = []
# Check if the root credentials are managed by AWS Organizations
if (
iam_client.organization_features is not None
and "RootCredentialsManagement" not in iam_client.organization_features
):
if iam_client.credential_report:
for user in iam_client.credential_report:
if user["user"] == "<root_account>":
if iam_client.credential_report:
for user in iam_client.credential_report:
if user["user"] == "<root_account>":
password_enabled = user["password_enabled"] == "true"
access_key_1_active = user["access_key_1_active"] == "true"
access_key_2_active = user["access_key_2_active"] == "true"
# Only report if root actually has credentials
if password_enabled or access_key_1_active or access_key_2_active:
report = Check_Report_AWS(
metadata=self.metadata(), resource=user
)
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
# Check if organization manages root credentials
org_managed = (
iam_client.organization_features is not None
and "RootCredentialsManagement"
in iam_client.organization_features
)
if user["mfa_active"] == "false":
report.status = "FAIL"
report.status_extended = (
"MFA is not enabled for root account."
)
if org_managed:
report.status_extended = (
"Root account has credentials without MFA "
"despite organizational root management being enabled."
)
else:
report.status_extended = (
"MFA is not enabled for root account."
)
else:
report.status = "PASS"
report.status_extended = "MFA is enabled for root account."
if org_managed:
report.status_extended = (
"Root account has credentials with MFA enabled. "
"Consider removing individual root credentials since organizational "
"root management is active."
)
else:
report.status_extended = (
"MFA is enabled for root account."
)
findings.append(report)
return findings
@@ -54,7 +54,7 @@ class IAM(AWSService):
self.role_arn_template = f"arn:{self.audited_partition}:iam:{self.region}:{self.audited_account}:role"
self.password_policy_arn_template = f"arn:{self.audited_partition}:iam:{self.region}:{self.audited_account}:password-policy"
self.mfa_arn_template = (
f"arn:{self.audited_partition}:iam:{self.region}:{self.audited_account}:mfa"
f"arn:{self.audited_partition}:iam::{self.audited_account}:mfa"
)
self.users = self._get_users()
self.roles = self._get_roles()
@@ -33,11 +33,13 @@ class Test_iam_no_root_access_key_test:
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
@@ -75,11 +77,13 @@ class Test_iam_no_root_access_key_test:
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
@@ -117,11 +121,13 @@ class Test_iam_no_root_access_key_test:
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "true"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
@@ -159,11 +165,13 @@ class Test_iam_no_root_access_key_test:
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "true"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
@@ -174,3 +182,179 @@ class Test_iam_no_root_access_key_test:
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_aws
def test_root_no_credentials(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(aws_provider),
) as service_client:
from prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key import (
iam_no_root_access_key,
)
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
# Should return no findings since root has no credentials
assert len(result) == 0
@mock_aws
def test_root_no_access_keys_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(aws_provider),
) as service_client:
from prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key import (
iam_no_root_access_key,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Root account has password configured but no access keys. Consider removing individual root credentials since organizational root management is active."
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_aws
def test_root_access_keys_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(aws_provider),
) as service_client:
from prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key import (
iam_no_root_access_key,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Root account has one active access key despite organizational root management being enabled."
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_aws
def test_root_both_access_keys_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(aws_provider),
) as service_client:
from prowler.providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key import (
iam_no_root_access_key,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:user/<root_account>"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "true"
check = iam_no_root_access_key()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Root account has two active access keys despite organizational root management being enabled."
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@@ -1,5 +1,9 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
@@ -15,23 +19,14 @@ class Test_iam_root_hardware_mfa_enabled_test:
set_mocked_aws_provider,
)
@mock_aws
def test_root_virtual_mfa_enabled(self):
iam_client = mock.MagicMock
iam_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
iam_client.virtual_mfa_devices = [
{
"SerialNumber": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/mfa",
"User": {"Arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
}
]
iam_client.audited_partition = "aws"
iam_client.region = AWS_REGION_US_EAST_1
iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
iam_client.organization_features = []
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
@@ -40,13 +35,32 @@ class Test_iam_root_hardware_mfa_enabled_test:
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=iam_client,
),
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# Set up virtual MFA device for root
service_client.virtual_mfa_devices = [
{
"SerialNumber": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/mfa",
"User": {"Arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
}
]
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -58,18 +72,14 @@ class Test_iam_root_hardware_mfa_enabled_test:
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
@mock_aws
def test_root_hardware_mfa_enabled(self):
iam_client = mock.MagicMock
iam_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
iam_client.virtual_mfa_devices = []
iam_client.audited_partition = "aws"
iam_client.region = AWS_REGION_US_EAST_1
iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
iam_client.organization_features = []
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
@@ -78,13 +88,27 @@ class Test_iam_root_hardware_mfa_enabled_test:
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=iam_client,
),
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# No virtual MFA devices (indicating hardware MFA)
service_client.virtual_mfa_devices = []
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -96,16 +120,14 @@ class Test_iam_root_hardware_mfa_enabled_test:
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
@mock_aws
def test_root_hardware_mfa_enabled_none_summary(self):
iam_client = mock.MagicMock
iam_client.account_summary = None
iam_client.virtual_mfa_devices = []
iam_client.audited_partition = "aws"
iam_client.region = AWS_REGION_US_EAST_1
iam_client.mfa_arn_template = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
iam_client.organization_features = []
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
@@ -114,13 +136,222 @@ class Test_iam_root_hardware_mfa_enabled_test:
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=iam_client,
),
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# No account summary
service_client.account_summary = None
service_client.virtual_mfa_devices = []
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_root_no_credentials(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
service_client.virtual_mfa_devices = []
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
# Should return no findings since root has no credentials
assert len(result) == 0
@mock_aws
def test_root_hardware_mfa_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
service_client.virtual_mfa_devices = []
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"Root account has credentials with hardware MFA enabled. "
"Consider removing individual root credentials since organizational "
"root management is active.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
@mock_aws
def test_root_virtual_mfa_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 1},
}
service_client.virtual_mfa_devices = [
{
"SerialNumber": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/mfa",
"User": {"Arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
}
]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "true"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"Root account has credentials with virtual MFA instead of hardware MFA "
"despite organizational root management being enabled.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
@mock_aws
def test_root_no_mfa_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled import (
iam_root_hardware_mfa_enabled,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.account_summary = {
"SummaryMap": {"AccountMFAEnabled": 0},
}
service_client.virtual_mfa_devices = []
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"Root account has credentials without MFA "
"despite organizational root management being enabled.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa"
@@ -42,12 +42,16 @@ class Test_iam_root_mfa_enabled_test:
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["mfa_active"] = "false"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"MFA is not enabled for root account.", result[0].status_extended
@@ -80,13 +84,149 @@ class Test_iam_root_mfa_enabled_test:
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["mfa_active"] = "true"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search("MFA is enabled for root account.", result[0].status_extended)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == service_client.credential_report[0]["arn"]
@mock_aws
def test_root_no_credentials(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled import (
iam_root_mfa_enabled,
)
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["mfa_active"] = "false"
service_client.credential_report[0]["password_enabled"] = "false"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
# Should return no findings since root has no credentials
assert len(result) == 0
@mock_aws
def test_root_mfa_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled import (
iam_root_mfa_enabled,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["mfa_active"] = "true"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "false"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
"Root account has credentials with MFA enabled. "
"Consider removing individual root credentials since organizational "
"root management is active.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == service_client.credential_report[0]["arn"]
@mock_aws
def test_root_mfa_disabled_with_organizational_management_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled.iam_client",
new=IAM(aws_provider),
) as service_client,
):
from prowler.providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled import (
iam_root_mfa_enabled,
)
# Set up organizational root management
service_client.organization_features = ["RootCredentialsManagement"]
service_client.credential_report[0]["user"] = "<root_account>"
service_client.credential_report[0]["mfa_active"] = "false"
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "true"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
"Root account has credentials without MFA "
"despite organizational root management being enabled.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == service_client.credential_report[0]["arn"]