chore(iam): add tags to missing checks report (#5280)

Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
Rubén De la Torre Vico
2024-10-03 19:47:10 +02:00
committed by GitHub
parent 4d4bf3fa11
commit 8bb3bd0dcb
20 changed files with 387 additions and 106 deletions
@@ -5,17 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_check_saml_providers_sts(Check):
def execute(self) -> Check_Report_AWS:
findings = []
if iam_client.saml_providers:
for provider in iam_client.saml_providers:
report = Check_Report_AWS(self.metadata())
provider_name = provider["Arn"].split("/")[1]
report.resource_id = provider_name
report.resource_arn = provider["Arn"]
report.region = iam_client.region
report.status = "PASS"
report.status_extended = (
f"SAML Provider {provider_name} has been found."
)
findings.append(report)
if not iam_client.saml_providers and iam_client.saml_providers is not None:
report = Check_Report_AWS(self.metadata())
report.resource_id = iam_client.audited_account
report.resource_arn = iam_client.audited_account_arn
report.region = iam_client.region
report.status = "FAIL"
report.status_extended = "No SAML Providers found."
findings.append(report)
for provider_arn, provider in iam_client.saml_providers.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = provider.name
report.resource_arn = provider_arn
report.resource_tags = provider.tags
report.region = iam_client.region
report.status = "PASS"
report.status_extended = f"SAML Provider {provider.name} has been found."
findings.append(report)
return findings
@@ -13,6 +13,7 @@ class iam_no_expired_server_certificates_stored(Check):
report.region = iam_client.region
report.resource_id = certificate.id
report.resource_arn = certificate.arn
report.resource_tags = certificate.tags
expiration_days = (datetime.now(timezone.utc) - certificate.expiration).days
if expiration_days >= 0:
report.status = "FAIL"
@@ -16,6 +16,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.status_extended = f"User {user.name} has the policy {policy['PolicyName']} attached."
report.resource_id = f"{user.name}/{policy['PolicyName']}"
report.resource_arn = user.arn
report.resource_tags = user.tags
findings.append(report)
if user.inline_policies:
for policy in user.inline_policies:
@@ -25,6 +26,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.status_extended = f"User {user.name} has the inline policy {policy} attached."
report.resource_id = f"{user.name}/{policy}"
report.resource_arn = user.arn
report.resource_tags = user.tags
findings.append(report)
else:
@@ -32,6 +34,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.region = iam_client.region
report.resource_id = user.name
report.resource_arn = user.arn
report.resource_tags = user.tags
report.status = "PASS"
report.status_extended = (
f"User {user.name} has no inline or attached policies."
@@ -12,9 +12,15 @@ maximum_expiration_days = 90
class iam_rotate_access_key_90_days(Check):
def execute(self) -> Check_Report_AWS:
findings = []
response = iam_client.credential_report
for user in response:
for user in iam_client.credential_report:
# Search user in iam_client.users to get tags
user_tags = []
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
user_tags = iam_user.tags
break
if (
user["access_key_1_last_rotated"] == "N/A"
and user["access_key_2_last_rotated"] == "N/A"
@@ -23,6 +29,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = (
f"User {user['user']} does not have access keys."
@@ -44,6 +51,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 1 in over 90 days ({access_key_1_last_rotated.days} days)."
findings.append(report)
@@ -60,6 +68,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 2 in over 90 days ({access_key_2_last_rotated.days} days)."
findings.append(report)
@@ -69,6 +78,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = f"User {user['user']} does not have access keys older than 90 days."
findings.append(report)
@@ -47,7 +47,6 @@ def is_service_role(role):
return False
################## IAM
class IAM(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
@@ -89,13 +88,21 @@ class IAM(AWSService):
self._list_inline_role_policies()
self.saml_providers = self._list_saml_providers()
self.server_certificates = self._list_server_certificates()
self._list_tags_for_resource()
self.access_keys_metadata = {}
self._get_access_keys_metadata()
self.last_accessed_services = {}
self._get_last_accessed_services()
self.user_temporary_credentials_usage = {}
self._get_user_temporary_credentials_usage()
# List missing tags
self.__threading_call__(self._list_tags, self.users)
self.__threading_call__(self._list_tags, self.roles)
self.__threading_call__(
self._list_tags,
[policy for policy in self.policies if policy.type == "Custom"],
)
self.__threading_call__(self._list_tags, self.server_certificates)
self.__threading_call__(self._list_tags, self.saml_providers.values())
def _get_client(self):
return self.client
@@ -733,17 +740,31 @@ class IAM(AWSService):
def _list_saml_providers(self):
logger.info("IAM - List SAML Providers...")
saml_providers = {}
try:
saml_providers = self.client.list_saml_providers()["SAMLProviderList"]
saml_providers_list = self.client.list_saml_providers()["SAMLProviderList"]
for provider in saml_providers_list:
if not self.audit_resources or (
is_resource_filtered(provider["Arn"], self.audit_resources)
):
saml_providers[provider["Arn"]] = SAMLProvider(
name=provider["Arn"].split("/")[-1], arn=provider["Arn"]
)
except ClientError as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
if error.response["Error"]["Code"] == "AccessDenied":
saml_providers = None
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
saml_providers = None
finally:
return saml_providers
def _list_server_certificates(self):
return saml_providers
def _list_server_certificates(self) -> list:
logger.info("IAM - List Server Certificates...")
try:
server_certificates = []
@@ -768,71 +789,30 @@ class IAM(AWSService):
finally:
return server_certificates
def _list_tags_for_resource(self):
def _list_tags(self, resource: any):
logger.info("IAM - List Tags...")
try:
if self.roles:
for role in self.roles:
try:
response = self.client.list_role_tags(RoleName=role.name)[
"Tags"
]
role.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
role.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
for user in self.users:
try:
response = self.client.list_user_tags(UserName=user.name)["Tags"]
user.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
user.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
for policy in self.policies:
try:
if policy.type != "Inline":
response = self.client.list_policy_tags(PolicyArn=policy.arn)[
"Tags"
]
policy.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
policy.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
if isinstance(resource, Role):
resource.tags = self.client.list_role_tags(RoleName=resource.name).get(
"Tags", []
)
elif isinstance(resource, User):
resource.tags = self.client.list_user_tags(UserName=resource.name).get(
"Tags", []
)
elif isinstance(resource, Policy):
if resource.type == "Custom":
resource.tags = self.client.list_policy_tags(
PolicyArn=resource.arn
).get("Tags", [])
elif isinstance(resource, Certificate):
resource.tags = self.client.list_server_certificate_tags(
ServerCertificateName=resource.name
).get("Tags", [])
elif isinstance(resource, SAMLProvider):
resource.tags = self.client.list_saml_provider_tags(
SAMLProviderArn=resource.arn
).get("Tags", [])
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -952,7 +932,7 @@ class User(BaseModel):
console_access: Optional[bool]
attached_policies: list[dict] = []
inline_policies: list[str] = []
tags: Optional[list] = []
tags: Optional[list]
class Role(BaseModel):
@@ -962,7 +942,7 @@ class Role(BaseModel):
is_service_role: bool
attached_policies: list[dict] = []
inline_policies: list[str] = []
tags: Optional[list] = []
tags: Optional[list]
class Group(BaseModel):
@@ -991,6 +971,7 @@ class Certificate(BaseModel):
id: str
arn: str
expiration: datetime
tags: Optional[list]
class Policy(BaseModel):
@@ -1002,3 +983,9 @@ class Policy(BaseModel):
attached: bool
document: Optional[dict]
tags: Optional[list] = []
class SAMLProvider(BaseModel):
name: str
arn: str
tags: Optional[list]
@@ -14,6 +14,13 @@ class iam_user_accesskey_unused(Check):
)
findings = []
for user in iam_client.credential_report:
# Search user in iam_client.users to get tags
user_tags = []
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
user_tags = iam_user.tags
break
if (
user["access_key_1_active"] != "true"
and user["access_key_2_active"] != "true"
@@ -22,6 +29,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = (
f"User {user['user']} does not have access keys."
@@ -41,6 +49,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"] + "/AccessKey1"
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not used access key 1 in the last {maximum_expiration_days} days ({access_key_1_last_used_date.days} days)."
findings.append(report)
@@ -56,6 +65,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"] + "/AccessKey2"
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not used access key 2 in the last {maximum_expiration_days} days ({access_key_2_last_used_date.days} days)."
findings.append(report)
@@ -65,6 +75,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = f"User {user['user']} does not have unused access keys for {maximum_expiration_days} days."
findings.append(report)
@@ -13,6 +13,11 @@ class iam_user_mfa_enabled_console_access(Check):
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
# check if the user has password enabled
if user["password_enabled"] == "true":
if user["mfa_active"] == "false":
@@ -54,4 +54,9 @@ class iam_user_no_setup_initial_access_key(Check):
report.resource_arn = user["arn"]
report.status = status
report.status_extended = status_extended
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
findings.append(report)
@@ -13,6 +13,11 @@ class iam_user_two_active_access_key(Check):
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
if (
user["access_key_1_active"] == "true"
and user["access_key_2_active"] == "true"
@@ -17,6 +17,11 @@ class iam_user_with_temporary_credentials(Check):
report.resource_id = user_name
report.resource_arn = user_arn
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user_arn:
report.resource_tags = iam_user.tags
break
report.status = "PASS"
report.status_extended = f"User {user_name} doesn't have long lived credentials with access to other services than IAM or STS."
@@ -1,12 +1,31 @@
from unittest import mock
import botocore
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
orig = botocore.client.BaseClient._make_api_call
def mock_make_api_call(self, operation_name, kwarg):
if operation_name == "ListSAMLProviderTags":
return {
"Tags": [
{"Key": "Name", "Value": "test"},
{"Key": "Owner", "Value": "test"},
]
}
return orig(self, operation_name, kwarg)
class Test_iam_check_saml_providers_sts:
@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@mock_aws
def test_iam_check_saml_providers_sts(self):
iam_client = client("iam")
@@ -63,3 +82,46 @@ nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
check = iam_check_saml_providers_sts()
result = check.execute()
assert result[0].status == "PASS"
assert result[0].resource_id == saml_provider_name
assert (
result[0].resource_arn
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:saml-provider/{saml_provider_name}"
)
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test"},
{"Key": "Owner", "Value": "test"},
]
assert result[0].region == AWS_REGION_US_EAST_1
assert (
result[0].status_extended
== f"SAML Provider {saml_provider_name} has been found."
)
@mock_aws
def test_iam_check_saml_providers_sts_no_saml_providers(self):
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_check_saml_providers_sts.iam_check_saml_providers_sts.iam_client",
new=IAM(aws_provider),
):
# Test Check
from prowler.providers.aws.services.iam.iam_check_saml_providers_sts.iam_check_saml_providers_sts import (
iam_check_saml_providers_sts,
)
check = iam_check_saml_providers_sts()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_id == AWS_ACCOUNT_NUMBER
assert (
result[0].resource_arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].status_extended == "No SAML Providers found."
@@ -1,11 +1,23 @@
from re import search
from unittest import mock
import botocore
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
# Original botocore _make_api_call function
orig = botocore.client.BaseClient._make_api_call
# Mocked botocore _make_api_call function
def mock_make_api_call(self, operation_name, kwarg):
if operation_name == "ListServerCertificateTags":
return {"Tags": [{"Key": "Name", "Value": "certname"}]}
# If we don't want to patch the API call
return orig(self, operation_name, kwarg)
class Test_iam_no_expired_server_certificates_stored_test:
@mock_aws
@@ -31,6 +43,7 @@ class Test_iam_no_expired_server_certificates_stored_test:
assert len(result) == 0
@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@mock_aws
def test_expired_certificate(self):
iam_client = client("iam")
@@ -39,6 +52,7 @@ class Test_iam_no_expired_server_certificates_stored_test:
ServerCertificateName="certname",
CertificateBody="certbody",
PrivateKey="privatekey",
Tags=[{"Key": "Name", "Value": "certname"}],
)["ServerCertificateMetadata"]
from prowler.providers.aws.services.iam.iam_service import IAM
@@ -68,3 +82,5 @@ class Test_iam_no_expired_server_certificates_stored_test:
)
assert result[0].resource_id == cert["ServerCertificateId"]
assert result[0].resource_arn == cert["Arn"]
assert result[0].resource_tags == [{"Key": "Name", "Value": "certname"}]
assert result[0].region == AWS_REGION_US_EAST_1
@@ -133,6 +133,9 @@ class Test_iam_policy_attached_only_to_group_or_roles:
UserName=user, PolicyName=policyName, PolicyDocument=dumps(policyDocument)
)
# Tag the user
iam_client.tag_user(UserName=user, Tags=[{"Key": "tag1", "Value": "value1"}])
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
@@ -160,6 +163,7 @@ class Test_iam_policy_attached_only_to_group_or_roles:
result[0].resource_arn
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/{user}"
)
assert result[0].resource_tags == [{"Key": "tag1", "Value": "value1"}]
@mock_aws
def test_iam_user_no_policies(self):
@@ -168,6 +172,9 @@ class Test_iam_policy_attached_only_to_group_or_roles:
user = "test_no_policies"
iam_client.create_user(UserName=user)
# Tag the user
iam_client.tag_user(UserName=user, Tags=[{"Key": "tag1", "Value": "value1"}])
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
from prowler.providers.aws.services.iam.iam_service import IAM
@@ -195,3 +202,4 @@ class Test_iam_policy_attached_only_to_group_or_roles:
result[0].resource_arn
== f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/{user}"
)
assert result[0].resource_tags == [{"Key": "tag1", "Value": "value1"}]
@@ -15,6 +15,8 @@ class Test_iam_rotate_access_key_90_days_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "test-tag", "Value": "test"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -43,6 +45,7 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "test-tag", "Value": "test"}]
@mock_aws
def test_user_access_key_1_not_rotated(self):
@@ -53,6 +56,8 @@ class Test_iam_rotate_access_key_90_days_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "test-tag", "Value": "test"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -84,6 +89,7 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "test-tag", "Value": "test"}]
@mock_aws
def test_user_access_key_2_not_rotated(self):
@@ -94,6 +100,8 @@ class Test_iam_rotate_access_key_90_days_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "test-tag", "Value": "test"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -125,6 +133,7 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "test-tag", "Value": "test"}]
@mock_aws
def test_user_both_access_keys_not_rotated(self):
@@ -135,6 +144,8 @@ class Test_iam_rotate_access_key_90_days_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "test-tag", "Value": "test"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -171,6 +182,7 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "test-tag", "Value": "test"}]
assert result[1].status == "FAIL"
assert (
result[1].status_extended
@@ -179,6 +191,7 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[1].resource_id == user
assert result[1].resource_arn == arn
assert result[1].region == AWS_REGION_US_EAST_1
assert result[1].resource_tags == [{"Key": "test-tag", "Value": "test"}]
@mock_aws
def test_user_both_access_keys_rotated(self):
@@ -189,6 +202,8 @@ class Test_iam_rotate_access_key_90_days_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "test-tag", "Value": "test"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -226,3 +241,4 @@ class Test_iam_rotate_access_key_90_days_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "test-tag", "Value": "test"}]
@@ -79,7 +79,6 @@ def mock_make_api_call(self, operation_name, kwargs):
# Patch every AWS call using Boto3
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
class Test_IAM_Service:
# Test IAM Client
@mock_aws
def test_get_client(self):
@@ -806,16 +805,18 @@ nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
</KeyDescriptor>
</EntityDescriptor>"""
saml_provider_name = "test"
iam_client.create_saml_provider(
saml_arn = iam_client.create_saml_provider(
SAMLMetadataDocument=xml_template, Name=saml_provider_name
)
)["SAMLProviderArn"]
# IAM client for this test class
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam = IAM(aws_provider)
assert len(iam.saml_providers) == 1
assert iam.saml_providers[0]["Arn"].split("/")[1] == saml_provider_name
assert saml_arn in iam.saml_providers
assert iam.saml_providers[saml_arn].name == saml_provider_name
assert iam.saml_providers[saml_arn].arn == saml_arn
# Test IAM User Inline Policy
@mock_aws
@@ -18,6 +18,8 @@ class Test_iam_user_accesskey_unused_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider(
@@ -54,6 +56,9 @@ class Test_iam_user_accesskey_unused_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test-user"}
]
@mock_aws
def test_user_access_key_1_not_used(self):
@@ -64,6 +69,8 @@ class Test_iam_user_accesskey_unused_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider(
@@ -98,6 +105,9 @@ class Test_iam_user_accesskey_unused_test:
assert result[0].resource_id == user + "/AccessKey1"
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test-user"}
]
@mock_aws
def test_user_access_key_2_not_used(self):
@@ -108,6 +118,8 @@ class Test_iam_user_accesskey_unused_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider(
@@ -142,6 +154,9 @@ class Test_iam_user_accesskey_unused_test:
assert result[0].resource_id == user + "/AccessKey2"
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test-user"}
]
@mock_aws
def test_user_both_access_keys_not_used(self):
@@ -152,6 +167,8 @@ class Test_iam_user_accesskey_unused_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider(
@@ -191,6 +208,9 @@ class Test_iam_user_accesskey_unused_test:
assert result[0].resource_id == user + "/AccessKey1"
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test-user"}
]
assert result[1].status == "FAIL"
assert (
@@ -210,6 +230,8 @@ class Test_iam_user_accesskey_unused_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider(
@@ -249,3 +271,6 @@ class Test_iam_user_accesskey_unused_test:
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test-user"}
]
@@ -70,6 +70,8 @@ class Test_iam_user_mfa_enabled_console_access_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -96,6 +98,7 @@ class Test_iam_user_mfa_enabled_console_access_test:
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].resource_tags == [{"Key": "Name", "Value": "test-user"}]
@mock_aws
def test_user_password_console_and_mfa_enabled(self):
@@ -103,6 +106,8 @@ class Test_iam_user_mfa_enabled_console_access_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -130,6 +135,7 @@ class Test_iam_user_mfa_enabled_console_access_test:
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].resource_tags == [{"Key": "Name", "Value": "test-user"}]
@mock_aws
def test_user_password_console_enabled_and_mfa_not_enabled(self):
@@ -137,6 +143,8 @@ class Test_iam_user_mfa_enabled_console_access_test:
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": "test-user"}])
from prowler.providers.aws.services.iam.iam_service import IAM
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -164,3 +172,4 @@ class Test_iam_user_mfa_enabled_console_access_test:
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
assert result[0].resource_tags == [{"Key": "Name", "Value": "test-user"}]
@@ -2,22 +2,10 @@ from csv import DictReader
from re import search
from unittest import mock
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
AWS_ACCOUNT_NUMBER = "123456789012"
class Test_iam_user_no_setup_initial_access_key_test:
from tests.providers.aws.utils import (
AWS_ACCOUNT_ARN,
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
@mock_aws
def test_setup_access_key_1_fail(self):
raw_credential_report = r"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated
test_false_access_key_1,arn:aws:iam::123456789012:test_false_access_key_1,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A"""
@@ -40,13 +28,28 @@ test_false_access_key_1,arn:aws:iam::123456789012:test_false_access_key_1,2022-0
)
service_client.credential_report = credential_list
service_client.users = [
mock.MagicMock(
name="test_false_access_key_1",
arn="arn:aws:iam::123456789012:test_false_access_key_1",
tags=[{"Key": "Name", "Value": "test_false_access_key_1"}],
)
]
check = iam_user_no_setup_initial_access_key()
result = check.execute()
assert result[0].status == "FAIL"
assert search("has never used access key 1", result[0].status_extended)
assert result[0].resource_id == "test_false_access_key_1"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:test_false_access_key_1"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test_false_access_key_1"}
]
@mock_aws
def test_setup_access_key_2_fail(self):
raw_credential_report = r"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated
test_false_access_key_2,arn:aws:iam::123456789012:test_false_access_key_2,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,false,N/A,N/A,N/A,N/A,true,N/A,N/A,N/A,N/A,false,N/A,false,N/A"""
@@ -69,13 +72,28 @@ test_false_access_key_2,arn:aws:iam::123456789012:test_false_access_key_2,2022-0
)
service_client.credential_report = credential_list
service_client.users = [
mock.MagicMock(
name="test_false_access_key_2",
arn="arn:aws:iam::123456789012:test_false_access_key_2",
tags=[{"Key": "Name", "Value": "test_false_access_key_2"}],
)
]
check = iam_user_no_setup_initial_access_key()
result = check.execute()
assert result[0].status == "FAIL"
assert search("has never used access key 2", result[0].status_extended)
assert result[0].resource_id == "test_false_access_key_2"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:test_false_access_key_2"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test_false_access_key_2"}
]
@mock_aws
def test_setup_both_access_keys_fail(self):
raw_credential_report = r"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated
test_false_both_access_keys,arn:aws:iam::123456789012:test_false_both_access_keys,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,N/A,N/A,N/A,true,N/A,N/A,N/A,N/A,false,N/A,false,N/A"""
@@ -98,15 +116,39 @@ test_false_both_access_keys,arn:aws:iam::123456789012:test_false_both_access_key
)
service_client.credential_report = credential_list
service_client.users = [
mock.MagicMock(
name="test_false_both_access_keys",
arn="arn:aws:iam::123456789012:test_false_both_access_keys",
tags=[{"Key": "Name", "Value": "test_false_both_access_keys"}],
)
]
check = iam_user_no_setup_initial_access_key()
result = check.execute()
assert result[0].status == "FAIL"
assert search("has never used access key 1", result[0].status_extended)
assert result[0].resource_id == "test_false_both_access_keys"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:test_false_both_access_keys"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [
{"Key": "Name", "Value": "test_false_both_access_keys"}
]
assert result[1].status == "FAIL"
assert search("has never used access key 2", result[1].status_extended)
assert result[1].resource_id == "test_false_both_access_keys"
assert (
result[1].resource_arn
== "arn:aws:iam::123456789012:test_false_both_access_keys"
)
assert result[1].region == AWS_REGION_US_EAST_1
assert result[1].resource_tags == [
{"Key": "Name", "Value": "test_false_both_access_keys"}
]
@mock_aws
def test_setup_access_key_pass(self):
raw_credential_report = r"""user,arn,user_creation_time,password_enabled,password_last_used,password_last_changed,password_next_rotation,mfa_active,access_key_1_active,access_key_1_last_rotated,access_key_1_last_used_date,access_key_1_last_used_region,access_key_1_last_used_service,access_key_2_active,access_key_2_last_rotated,access_key_2_last_used_date,access_key_2_last_used_region,access_key_2_last_used_service,cert_1_active,cert_1_last_rotated,cert_2_active,cert_2_last_rotated
test_pass,arn:aws:iam::123456789012:test_pass,2022-02-17T14:59:38+00:00,not_supported,no_information,not_supported,not_supported,false,false,N/A,N/A,N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A"""
@@ -129,6 +171,13 @@ test_pass,arn:aws:iam::123456789012:test_pass,2022-02-17T14:59:38+00:00,not_supp
)
service_client.credential_report = credential_list
service_client.users = [
mock.MagicMock(
name="test_pass",
arn="arn:aws:iam::123456789012:test_pass",
tags=[{"Key": "Name", "Value": "test_pass"}],
)
]
check = iam_user_no_setup_initial_access_key()
result = check.execute()
@@ -137,3 +186,7 @@ test_pass,arn:aws:iam::123456789012:test_pass,2022-02-17T14:59:38+00:00,not_supp
"does not have access keys or uses the access keys configured",
result[0].status_extended,
)
assert result[0].resource_id == "test_pass"
assert result[0].resource_arn == "arn:aws:iam::123456789012:test_pass"
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": "test_pass"}]
@@ -23,6 +23,7 @@ class Test_iam_user_two_active_access_key:
iam_client = client("iam")
user = "test1"
user_arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": user}])
# Create Access Key 1
iam_client.create_access_key(UserName=user)
# Create Access Key 2
@@ -57,6 +58,7 @@ class Test_iam_user_two_active_access_key:
assert search(
f"User {user} has 2 active access keys.", result[0].status_extended
)
assert result[0].resource_tags == [{"Key": "Name", "Value": user}]
@mock_aws
def test_iam_user_one_active_access_key(self):
@@ -64,6 +66,7 @@ class Test_iam_user_two_active_access_key:
iam_client = client("iam")
user = "test1"
user_arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": user}])
# Create Access Key 1
iam_client.create_access_key(UserName=user)
@@ -94,6 +97,7 @@ class Test_iam_user_two_active_access_key:
f"User {user} does not have 2 active access keys.",
result[0].status_extended,
)
assert result[0].resource_tags == [{"Key": "Name", "Value": user}]
@mock_aws
def test_iam_user_without_active_access_key(self):
@@ -101,6 +105,7 @@ class Test_iam_user_two_active_access_key:
iam_client = client("iam")
user = "test1"
user_arn = iam_client.create_user(UserName=user)["User"]["Arn"]
iam_client.tag_user(UserName=user, Tags=[{"Key": "Name", "Value": user}])
from prowler.providers.aws.services.iam.iam_service import IAM
@@ -129,6 +134,7 @@ class Test_iam_user_two_active_access_key:
f"User {user} does not have 2 active access keys.",
result[0].status_extended,
)
assert result[0].resource_tags == [{"Key": "Name", "Value": user}]
@mock_aws
def test_iam_no_users(self):
@@ -16,6 +16,13 @@ class Test_iam_user_with_temporary_credentials:
iam_client.access_keys_metadata = {}
iam_client.last_accessed_services = {}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -45,6 +52,13 @@ class Test_iam_user_with_temporary_credentials:
iam_client.access_keys_metadata = {USER_DATA: []}
iam_client.last_accessed_services = {USER_DATA: []}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -74,6 +88,7 @@ class Test_iam_user_with_temporary_credentials:
assert result[0].resource_id == IAM_USER_NAME
assert result[0].resource_arn == IAM_USER_ARN
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": IAM_USER_NAME}]
def test_user_access_keys_no_accesed_services(self):
iam_client = mock.MagicMock
@@ -82,6 +97,13 @@ class Test_iam_user_with_temporary_credentials:
iam_client.access_keys_metadata = {USER_DATA: [{"AccessKeyId": 1}]}
iam_client.last_accessed_services = {USER_DATA: []}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -111,6 +133,7 @@ class Test_iam_user_with_temporary_credentials:
assert result[0].resource_id == IAM_USER_NAME
assert result[0].resource_arn == IAM_USER_ARN
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": IAM_USER_NAME}]
def test_user_access_keys_accesed_services_sts(self):
iam_client = mock.MagicMock
@@ -119,6 +142,13 @@ class Test_iam_user_with_temporary_credentials:
iam_client.access_keys_metadata = {USER_DATA: [{"AccessKeyId": 1}]}
iam_client.last_accessed_services = {USER_DATA: [{"ServiceNamespace": "sts"}]}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -148,6 +178,7 @@ class Test_iam_user_with_temporary_credentials:
assert result[0].resource_id == IAM_USER_NAME
assert result[0].resource_arn == IAM_USER_ARN
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": IAM_USER_NAME}]
def test_access_keys_with_iam_and_sts(self):
iam_client = mock.MagicMock
@@ -158,6 +189,13 @@ class Test_iam_user_with_temporary_credentials:
USER_DATA: [{"ServiceNamespace": "sts"}, {"ServiceNamespace": "iam"}]
}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -187,6 +225,7 @@ class Test_iam_user_with_temporary_credentials:
assert result[0].resource_id == IAM_USER_NAME
assert result[0].resource_arn == IAM_USER_ARN
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": IAM_USER_NAME}]
def test_access_keys_with_iam_and_ec2(self):
iam_client = mock.MagicMock
@@ -197,6 +236,13 @@ class Test_iam_user_with_temporary_credentials:
USER_DATA: [{"ServiceNamespace": "iam"}, {"ServiceNamespace": "ec2"}]
}
iam_client.users = [
mock.MagicMock(
arn=IAM_USER_ARN,
tags=[{"Key": "Name", "Value": IAM_USER_NAME}],
)
]
# Generate temporary credentials usage
iam_client.user_temporary_credentials_usage = {}
iam_client._get_user_temporary_credentials_usage = (
@@ -226,3 +272,4 @@ class Test_iam_user_with_temporary_credentials:
assert result[0].resource_id == IAM_USER_NAME
assert result[0].resource_arn == IAM_USER_ARN
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == [{"Key": "Name", "Value": IAM_USER_NAME}]