feat(test): Remaining IAM tests (#1451)

This commit is contained in:
Nacho Rivera
2022-11-04 13:38:22 +01:00
committed by GitHub
parent 8ae989cce8
commit 4762e1cc4c
18 changed files with 1417 additions and 35 deletions

View File

@@ -12,14 +12,18 @@ class iam_administrator_access_with_mfa(Check):
report.resource_id = group.name
report.resource_arn = group.arn
report.region = iam_client.region
report.status = "PASS"
report.status_extended = f"Group {group.name} has no policies."
if group.attached_policies:
admin_policy = False
report.status_extended = (
f"Group {group.name} provides non-administrative access."
)
for group_policy in group.attached_policies:
if (
group_policy["PolicyArn"]
== "arn:aws:iam::aws:policy/AdministratorAccess"
):
admin_policy = True
# users in group are Administrators
if group.users:
for group_user in group.users:
@@ -30,27 +34,9 @@ class iam_administrator_access_with_mfa(Check):
):
report.status = "FAIL"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA disabled."
findings.append(report)
elif (
user["user"] == group_user.name
and user["mfa_active"] == "true"
):
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrator access to User {group_user.name} with MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} provides administrative access but does not have users."
findings.append(report)
if not admin_policy:
report.status = "PASS"
report.status_extended = (
f"Group {group.name} provides non-administrative access."
)
findings.append(report)
else:
report.status = "PASS"
report.status_extended = f"Group {group.name} has no policies."
findings.append(report)
findings.append(report)
return findings

View File

@@ -0,0 +1,202 @@
from json import dumps
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_administrator_access_with_mfa_test:
@mock_iam
def test_group_with_no_policies(self):
iam = client("iam")
group_name = "test-group"
arn = iam.create_group(GroupName=group_name)["Group"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
):
from providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa import (
iam_administrator_access_with_mfa,
)
check = iam_administrator_access_with_mfa()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == group_name
assert result[0].resource_arn == arn
assert search(
f"Group {group_name} has no policies.", result[0].status_extended
)
@mock_iam
def test_group_non_administrative_policy(self):
iam = client("iam")
group_name = "test-group"
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
],
}
policy_arn = iam.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
arn = iam.create_group(GroupName=group_name)["Group"]["Arn"]
iam.attach_group_policy(GroupName=group_name, PolicyArn=policy_arn)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
):
from providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa import (
iam_administrator_access_with_mfa,
)
check = iam_administrator_access_with_mfa()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == group_name
assert result[0].resource_arn == arn
assert search(
f"Group {group_name} provides non-administrative access.",
result[0].status_extended,
)
@mock_iam
def test_admin_policy_no_users(self):
iam = client("iam")
group_name = "test-group"
arn = iam.create_group(GroupName=group_name)["Group"]["Arn"]
iam.attach_group_policy(
GroupName=group_name,
PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess",
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
):
from providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa import (
iam_administrator_access_with_mfa,
)
check = iam_administrator_access_with_mfa()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].resource_id == group_name
assert result[0].resource_arn == arn
assert search(
f"Group {group_name} provides administrative access but does not have users.",
result[0].status_extended,
)
@mock_iam
def test_admin_policy_with_user_without_mfa(self):
iam = client("iam")
group_name = "test-group"
user_name = "user-test"
iam.create_user(UserName=user_name)
arn = iam.create_group(GroupName=group_name)["Group"]["Arn"]
iam.attach_group_policy(
GroupName=group_name,
PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess",
)
iam.add_user_to_group(GroupName=group_name, UserName=user_name)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
):
from providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa import (
iam_administrator_access_with_mfa,
)
check = iam_administrator_access_with_mfa()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == group_name
assert result[0].resource_arn == arn
assert search(
f"Group {group_name} provides administrator access to User {user_name} with MFA disabled.",
result[0].status_extended,
)
@mock_iam
def test_various_policies_with_users_with_and_without_mfa(self):
iam = client("iam")
group_name = "test-group"
user_name_no_mfa = "user-no-mfa"
user_name_mfa = "user-mfa"
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
],
}
mfa_device_name = "mfa-test"
mfa_serial_number = iam.create_virtual_mfa_device(
VirtualMFADeviceName=mfa_device_name
)["VirtualMFADevice"]["SerialNumber"]
iam.create_user(UserName=user_name_no_mfa)
iam.create_user(UserName=user_name_mfa)
iam.enable_mfa_device(
UserName=user_name_mfa,
SerialNumber=mfa_serial_number,
AuthenticationCode1="123456",
AuthenticationCode2="123466",
)
policy_arn = iam.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
arn_group = iam.create_group(GroupName=group_name)["Group"]["Arn"]
iam.attach_group_policy(GroupName=group_name, PolicyArn=policy_arn)
iam.attach_group_policy(
GroupName=group_name,
PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess",
)
iam.add_user_to_group(GroupName=group_name, UserName=user_name_no_mfa)
iam.add_user_to_group(GroupName=group_name, UserName=user_name_mfa)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
):
from providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa import (
iam_administrator_access_with_mfa,
)
check = iam_administrator_access_with_mfa()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == group_name
assert result[0].resource_arn == arn_group
assert search(
f"Group {group_name} provides administrator access to User {user_name_no_mfa} with MFA disabled.",
result[0].status_extended,
)

View File

@@ -0,0 +1,134 @@
import datetime
from csv import DictReader
from re import search
from unittest import mock
from moto import mock_iam
class Test_iam_avoid_root_usage:
@mock_iam
def test_root_not_used(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
<root_account>,arn:aws:iam::123456789012:<root_account>,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"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage import (
iam_avoid_root_usage,
)
service_client.credential_report = credential_list
check = iam_avoid_root_usage()
result = check.execute()
assert result[0].status == "PASS"
assert search(
"Root user in the account wasn't accessed in the last",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == "arn:aws:iam::123456789012:<root_account>"
@mock_iam
def test_root_password_used(self):
password_last_used = (
datetime.datetime.now() - datetime.timedelta(days=2)
).strftime("%Y-%m-%dT%H:%M:%S+00:00")
raw_credential_report = rf"""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
<root_account>,arn:aws:iam::123456789012:<root_account>,2022-04-17T14:59:38+00:00,true,{password_last_used},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"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage import (
iam_avoid_root_usage,
)
service_client.credential_report = credential_list
check = iam_avoid_root_usage()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
"Root user in the account was last accessed", result[0].status_extended
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == "arn:aws:iam::123456789012:<root_account>"
@mock_iam
def test_root_access_key_1_used(self):
access_key_1_last_used = (
datetime.datetime.now() - datetime.timedelta(days=2)
).strftime("%Y-%m-%dT%H:%M:%S+00:00")
raw_credential_report = rf"""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
<root_account>,arn:aws:iam::123456789012:<root_account>,2022-04-17T14:59:38+00:00,true,no_information,not_supported,not_supported,false,true,N/A,{access_key_1_last_used},N/A,N/A,false,N/A,N/A,N/A,N/A,false,N/A,false,N/A"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage import (
iam_avoid_root_usage,
)
service_client.credential_report = credential_list
check = iam_avoid_root_usage()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
"Root user in the account was last accessed", result[0].status_extended
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == "arn:aws:iam::123456789012:<root_account>"
@mock_iam
def test_root_access_key_2_used(self):
access_key_2_last_used = (
datetime.datetime.now() - datetime.timedelta(days=2)
).strftime("%Y-%m-%dT%H:%M:%S+00:00")
raw_credential_report = rf"""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
<root_account>,arn:aws:iam::123456789012:<root_account>,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,{access_key_2_last_used},N/A,N/A,false,N/A,false,N/A"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage import (
iam_avoid_root_usage,
)
service_client.credential_report = credential_list
check = iam_avoid_root_usage()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
"Root user in the account was last accessed", result[0].status_extended
)
assert result[0].resource_id == "<root_account>"
assert result[0].resource_arn == "arn:aws:iam::123456789012:<root_account>"

View File

@@ -16,7 +16,7 @@ class iam_disable_30_days_credentials(Check):
report.resource_id = user.name
report.resource_arn = user.arn
report.region = iam_client.region
if user.password_last_used and user.password_last_used != "":
if user.password_last_used:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(

View File

@@ -0,0 +1,97 @@
import datetime
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_disable_30_days_credentials_test:
@mock_iam
def test_iam_user_logged_30_days(self):
password_last_used = (
datetime.datetime.now() - datetime.timedelta(days=2)
).strftime("%Y-%m-%d %H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials import (
iam_disable_30_days_credentials,
)
service_client.users[0].password_last_used = password_last_used
check = iam_disable_30_days_credentials()
result = check.execute()
assert result[0].status == "PASS"
assert search(
f"User {user} has logged into the console in the past 30 days.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_iam_user_not_logged_30_days(self):
password_last_used = (
datetime.datetime.now() - datetime.timedelta(days=40)
).strftime("%Y-%m-%d %H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials import (
iam_disable_30_days_credentials,
)
service_client.users[0].password_last_used = password_last_used
check = iam_disable_30_days_credentials()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
f"User {user} has not logged into the console in the past 30 days.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_iam_user_not_logged(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials import (
iam_disable_30_days_credentials,
)
service_client.users[0].password_last_used = ""
print(service_client.users)
# raise Exception
check = iam_disable_30_days_credentials()
result = check.execute()
assert result[0].status == "PASS"
assert search(
f"User {user} has not a console password or is unused.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn

View File

@@ -16,7 +16,7 @@ class iam_disable_90_days_credentials(Check):
report.region = iam_client.region
report.resource_id = user.name
report.resource_arn = user.arn
if user.password_last_used and user.password_last_used != "":
if user.password_last_used:
time_since_insertion = (
datetime.datetime.now()
- datetime.datetime.strptime(

View File

@@ -0,0 +1,97 @@
import datetime
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_disable_90_days_credentials_test:
@mock_iam
def test_iam_user_logged_90_days(self):
password_last_used = (
datetime.datetime.now() - datetime.timedelta(days=2)
).strftime("%Y-%m-%d %H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials import (
iam_disable_90_days_credentials,
)
service_client.users[0].password_last_used = password_last_used
check = iam_disable_90_days_credentials()
result = check.execute()
assert result[0].status == "PASS"
assert search(
f"User {user} has logged into the console in the past 90 days.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_iam_user_not_logged_90_days(self):
password_last_used = (
datetime.datetime.now() - datetime.timedelta(days=100)
).strftime("%Y-%m-%d %H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials import (
iam_disable_90_days_credentials,
)
service_client.users[0].password_last_used = password_last_used
check = iam_disable_90_days_credentials()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
f"User {user} has not logged into the console in the past 90 days.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_iam_user_not_logged(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials import (
iam_disable_90_days_credentials,
)
service_client.users[0].password_last_used = ""
print(service_client.users)
# raise Exception
check = iam_disable_90_days_credentials()
result = check.execute()
assert result[0].status == "PASS"
assert search(
f"User {user} has not a console password or is unused.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn

View File

@@ -0,0 +1,159 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_no_root_access_key_test:
@mock_iam
def test_iam_root_no_access_keys(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
print(service_client.credential_report)
# raise Exception
assert result[0].status == "PASS"
assert search(
f"User <root_account> has not access keys.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_iam
def test_iam_root_access_key_1(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "false"
check = iam_no_root_access_key()
result = check.execute()
print(service_client.credential_report)
# raise Exception
assert result[0].status == "FAIL"
assert search(
f"User <root_account> has one active access key.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_iam
def test_iam_root_access_key_2(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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]["access_key_1_active"] = "false"
service_client.credential_report[0]["access_key_2_active"] = "true"
check = iam_no_root_access_key()
result = check.execute()
print(service_client.credential_report)
# raise Exception
assert result[0].status == "FAIL"
assert search(
f"User <root_account> has one active access key.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)
@mock_iam
def test_iam_root_both_access_keys(self):
iam_client = client("iam")
user = "test"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_no_root_access_key.iam_no_root_access_key.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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]["access_key_1_active"] = "true"
service_client.credential_report[0]["access_key_2_active"] = "true"
check = iam_no_root_access_key()
result = check.execute()
print(service_client.credential_report)
# raise Exception
assert result[0].status == "FAIL"
assert search(
f"User <root_account> has two active access key.",
result[0].status_extended,
)
assert result[0].resource_id == "<root_account>"
assert (
result[0].resource_arn
== "arn:aws:iam::123456789012:user/<root_account>"
)

View File

@@ -0,0 +1,74 @@
from re import search
from unittest import mock
from moto import mock_iam
class Test_iam_password_policy_expires_passwords_within_90_days_or_less:
@mock_iam
def test_password_expiration_lower_90(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM, PasswordPolicy
with mock.patch(
"providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less import (
iam_password_policy_expires_passwords_within_90_days_or_less,
)
service_client.password_policy = PasswordPolicy(
length=10,
symbols=True,
numbers=True,
uppercase=True,
lowercase=True,
allow_change=True,
expiration=True,
max_age=40,
reuse_prevention=2,
hard_expiry=True,
)
check = iam_password_policy_expires_passwords_within_90_days_or_less()
result = check.execute()
assert result[0].status == "PASS"
assert result[0].resource_id == "password_policy"
assert search(
"Password expiration is set lower than 90 days",
result[0].status_extended,
)
@mock_iam
def test_password_expiration_greater_90(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM, PasswordPolicy
with mock.patch(
"providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_password_policy_expires_passwords_within_90_days_or_less.iam_password_policy_expires_passwords_within_90_days_or_less import (
iam_password_policy_expires_passwords_within_90_days_or_less,
)
service_client.password_policy = PasswordPolicy(
length=10,
symbols=True,
numbers=True,
uppercase=True,
lowercase=True,
allow_change=True,
expiration=True,
max_age=100,
reuse_prevention=2,
hard_expiry=True,
)
check = iam_password_policy_expires_passwords_within_90_days_or_less()
result = check.execute()
assert result[0].status == "FAIL"
assert result[0].resource_id == "password_policy"
assert search(
"Password expiration is set greater than 90 days",
result[0].status_extended,
)

View File

@@ -14,6 +14,9 @@ from providers.aws.services.iam.iam_client import iam_client
class iam_policy_allows_privilege_escalation(Check):
def execute(self) -> Check_Report:
# Is necessary to include the "Action:*" for
# each service that has a policy that could
# allow for privilege escalation
privilege_escalation_iam_actions = {
"iam:AttachGroupPolicy",
"iam:SetDefaultPolicyVersion2",
@@ -30,23 +33,31 @@ class iam_policy_allows_privilege_escalation(Check):
"iam:SetDefaultPolicyVersion",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateLoginProfile",
"iam:*",
"sts:AssumeRole",
"sts:*",
"ec2:RunInstances",
"ec2:*",
"lambda:CreateEventSourceMapping",
"lambda:CreateFunction",
"lambda:InvokeFunction",
"lambda:UpdateFunctionCode",
"lambda:*",
"dynamodb:CreateTable",
"dynamodb:PutItem",
"dynamodb:*",
"glue:CreateDevEndpoint",
"glue:GetDevEndpoint",
"glue:GetDevEndpoints",
"glue:UpdateDevEndpoint",
"glue:*",
"cloudformation:CreateStack",
"cloudformation:DescribeStacks",
"cloudformation:*",
"datapipeline:CreatePipeline",
"datapipeline:PutPipelineDefinition",
"datapipeline:ActivatePipeline",
"datapipeline:*",
}
findings = []
for policy in iam_client.customer_managed_policies:
@@ -86,7 +97,11 @@ class iam_policy_allows_privilege_escalation(Check):
# First, we need to perform a left join with ALLOWED_ACTIONS and DENIED_ACTIONS
left_actions = allowed_actions.difference(denied_actions)
# Then, we need to find the DENIED_NOT_ACTIONS in LEFT_ACTIONS
privileged_actions = left_actions.intersection(denied_not_actions)
if denied_not_actions:
privileged_actions = left_actions.intersection(denied_not_actions)
# If there is no Denied Not Actions
else:
privileged_actions = left_actions
# Finally, check if there is a privilege escalation action within this policy
policy_privilege_escalation_actions = privileged_actions.intersection(
privilege_escalation_iam_actions

View File

@@ -0,0 +1,185 @@
from json import dumps
from unittest import mock
from boto3 import client
from moto import mock_iam
AWS_REGION = "us-east-1"
class Test_iam_policy_allows_privilege_escalation:
@mock_iam
def test_iam_policy_allows_privilege_escalation_sts(self):
iam_client = client("iam", region_name=AWS_REGION)
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "sts:*", "Resource": "*"},
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Customer Managed IAM Policy {policy_arn} allows for privilege escalation using the following actions: {{'sts:*'}}"
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
@mock_iam
def test_iam_policy_not_allows_privilege_escalation(self):
iam_client = client("iam", region_name=AWS_REGION)
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "sts:*", "Resource": "*"},
{"Effect": "Deny", "Action": "sts:*", "Resource": "*"},
{"Effect": "Deny", "NotAction": "sts:*", "Resource": "*"},
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Customer Managed IAM Policy {policy_arn} not allows for privilege escalation"
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
@mock_iam
def test_iam_policy_not_allows_privilege_escalation_glue_GetDevEndpoints(self):
iam_client = client("iam", region_name=AWS_REGION)
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{"Effect": "Allow", "Action": "lambda:*", "Resource": "*"},
{"Effect": "Deny", "Action": "lambda:InvokeFunction", "Resource": "*"},
{
"Effect": "Deny",
"NotAction": "glue:GetDevEndpoints",
"Resource": "*",
},
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Customer Managed IAM Policy {policy_arn} not allows for privilege escalation"
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
@mock_iam
def test_iam_policy_not_allows_privilege_escalation_dynamodb_PutItem(self):
iam_client = client("iam", region_name=AWS_REGION)
policy_name = "policy1"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:*",
"iam:PassRole",
"dynamodb:PutItem",
"cloudformation:CreateStack",
"cloudformation:DescribeStacks",
"ec2:RunInstances",
],
"Resource": "*",
},
{
"Effect": "Deny",
"Action": ["lambda:InvokeFunction", "cloudformation:CreateStack"],
"Resource": "*",
},
{"Effect": "Deny", "NotAction": "dynamodb:PutItem", "Resource": "*"},
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Customer Managed IAM Policy {policy_arn} allows for privilege escalation using the following actions: {{'dynamodb:PutItem'}}"
)
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn

View File

@@ -0,0 +1,65 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_root_hardware_mfa_enabled_test:
@mock_iam
def test_root_hardware_virtual_mfa_enabled(self):
iam = client("iam")
mfa_device_name = "mfa-test"
iam.create_virtual_mfa_device(VirtualMFADeviceName=mfa_device_name)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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[0]["SerialNumber"] = "sddfaf-root-sfsfds"
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
"Root account has a virtual MFA instead of a hardware MFA enabled.",
result[0].status_extended,
)
assert result[0].resource_id == "root"
@mock_iam
def test_root_hardware_virtual_hardware_mfa_enabled(self):
iam = client("iam")
mfa_device_name = "mfa-test"
iam.create_virtual_mfa_device(VirtualMFADeviceName=mfa_device_name)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_root_hardware_mfa_enabled.iam_root_hardware_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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[0]["SerialNumber"] = ""
check = iam_root_hardware_mfa_enabled()
result = check.execute()
assert result[0].status == "PASS"
assert search(
"Root account has hardware MFA enabled.", result[0].status_extended
)
assert result[0].resource_id == "root"
assert (
result[0].resource_arn == f"arn:aws:iam::{service_client.account}:root"
)

View File

@@ -0,0 +1,67 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_root_mfa_enabled_test:
@mock_iam
def test_root_mfa_not_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
"MFA is not 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_iam
def test_root_mfa_enabled(self):
iam_client = client("iam")
user = "test-user"
iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_root_mfa_enabled.iam_root_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from 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"] = "true"
service_client.credential_report[0][
"arn"
] = "arn:aws:iam::123456789012:<root_account>:root"
check = iam_root_mfa_enabled()
result = check.execute()
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"]

View File

@@ -0,0 +1,102 @@
import datetime
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_rotate_access_key_90_days_test:
@mock_iam
def test_user_no_access_keys(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days import (
iam_rotate_access_key_90_days,
)
service_client.credential_report[0]["access_key_1_last_rotated"] == "N/A"
service_client.credential_report[0]["access_key_2_last_rotated"] == "N/A"
check = iam_rotate_access_key_90_days()
result = check.execute()
assert result[0].status == "PASS"
assert result[0].status_extended == f"User {user} has not access keys."
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_access_key_1_not_rotated(self):
credentials_last_rotated = (
datetime.datetime.now() - datetime.timedelta(days=100)
).strftime("%Y-%m-%dT%H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days import (
iam_rotate_access_key_90_days,
)
service_client.credential_report[0][
"access_key_1_last_rotated"
] = credentials_last_rotated
check = iam_rotate_access_key_90_days()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"User {user} has not rotated access key 1 in over 90 days (100 days)."
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_access_key_2_not_rotated(self):
credentials_last_rotated = (
datetime.datetime.now() - datetime.timedelta(days=100)
).strftime("%Y-%m-%dT%H:%M:%S+00:00")
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_rotate_access_key_90_days.iam_rotate_access_key_90_days import (
iam_rotate_access_key_90_days,
)
service_client.credential_report[0][
"access_key_2_last_rotated"
] = credentials_last_rotated
check = iam_rotate_access_key_90_days()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"User {user} has not rotated access key 2 in over 90 days (100 days)."
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn

View File

@@ -13,17 +13,15 @@ class iam_user_hardware_mfa_enabled(Check):
report.resource_arn = user.arn
report.region = iam_client.region
if user.mfa_devices:
report.status = "PASS"
report.status_extended = f"User {user.name} has hardware MFA enabled."
for mfa_device in user.mfa_devices:
if mfa_device.type == "mfa" or mfa_device.type == "sms-mfa":
report.status = "FAIL"
report.status_extended = f"User {user.name} has a virtual MFA instead of a hardware MFA enabled."
findings.append(report)
else:
report.status = "PASS"
report.status_extended = (
f"User {user.name} has hardware MFA enabled."
)
findings.append(report)
break
findings.append(report)
else:
report.status = "FAIL"
report.status_extended = (

View File

@@ -0,0 +1,103 @@
from re import search
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_user_hardware_mfa_enabled_test:
@mock_iam
def test_user_no_mfa_devices(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled import (
iam_user_hardware_mfa_enabled,
)
service_client.users[0].mfa_devices = []
check = iam_user_hardware_mfa_enabled()
result = check.execute()
assert result[0].status == "FAIL"
assert search(
f"User {user} has not any type of MFA enabled.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_virtual_mfa_devices(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM, MFADevice
with mock.patch(
"providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled import (
iam_user_hardware_mfa_enabled,
)
mfa_devices = [
MFADevice(serial_number="123454", type="mfa"),
MFADevice(serial_number="1234547", type="sms-mfa"),
]
service_client.users[0].mfa_devices = mfa_devices
check = iam_user_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
f"User {user} has a virtual MFA instead of a hardware MFA enabled.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_virtual_sms_mfa_devices(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM, MFADevice
with mock.patch(
"providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_hardware_mfa_enabled.iam_user_hardware_mfa_enabled import (
iam_user_hardware_mfa_enabled,
)
mfa_devices = [
MFADevice(serial_number="123454", type="test-mfa"),
MFADevice(serial_number="1234547", type="sms-mfa"),
]
service_client.users[0].mfa_devices = mfa_devices
check = iam_user_hardware_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert search(
f"User {user} has a virtual MFA instead of a hardware MFA enabled.",
result[0].status_extended,
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn

View File

@@ -0,0 +1,98 @@
from unittest import mock
from boto3 import client
from moto import mock_iam
class Test_iam_user_mfa_enabled_console_access_test:
@mock_iam
def test_user_not_password_console_enabled(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access import (
iam_user_mfa_enabled_console_access,
)
service_client.credential_report[0]["password_enabled"] = "not_supported"
check = iam_user_mfa_enabled_console_access()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"User {user} has not Console Password enabled."
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_password_console_and_mfa_enabled(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access import (
iam_user_mfa_enabled_console_access,
)
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["mfa_active"] = "true"
check = iam_user_mfa_enabled_console_access()
result = check.execute()
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"User {user} has Console Password enabled and MFA enabled."
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn
@mock_iam
def test_user_password_console_enabled_and_mfa_not_enabled(self):
iam_client = client("iam")
user = "test-user"
arn = iam_client.create_user(UserName=user)["User"]["Arn"]
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.iam.iam_service import IAM
with mock.patch(
"providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access.iam_client",
new=IAM(current_audit_info),
) as service_client:
from providers.aws.services.iam.iam_user_mfa_enabled_console_access.iam_user_mfa_enabled_console_access import (
iam_user_mfa_enabled_console_access,
)
service_client.credential_report[0]["password_enabled"] = "true"
service_client.credential_report[0]["mfa_active"] = "false"
check = iam_user_mfa_enabled_console_access()
result = check.execute()
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"User {user} has Console Password enabled but MFA disabled."
)
assert result[0].resource_id == user
assert result[0].resource_arn == arn

View File

@@ -9,7 +9,7 @@ class Test_iam_user_no_setup_initial_access_key_test:
@mock_iam
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::106908755756: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"""
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"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
@@ -35,7 +35,7 @@ test_false_access_key_1,arn:aws:iam::106908755756:test_false_access_key_1,2022-0
@mock_iam
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::106908755756: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"""
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"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)
@@ -61,7 +61,7 @@ test_false_access_key_2,arn:aws:iam::106908755756:test_false_access_key_2,2022-0
@mock_iam
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::106908755756: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"""
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"""
credential_lines = raw_credential_report.split("\n")
csv_reader = DictReader(credential_lines, delimiter=",")
credential_list = list(csv_reader)