feat(iam): Check inline policies in IAM Users, Groups & Roles for admin priv's (#2750)

Co-authored-by: Gerard Ocampo <gerard.ocampo@zelis.com>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
gerardocampo
2023-08-23 02:29:13 -04:00
committed by GitHub
parent 590a5669d6
commit e5d2c0c700
7 changed files with 983 additions and 22 deletions

View File

@@ -15,7 +15,7 @@ repos:
## TOML
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.7.0
rev: v2.10.0
hooks:
- id: pretty-format-toml
args: [--autofix]
@@ -28,7 +28,7 @@ repos:
- id: shellcheck
## PYTHON
- repo: https://github.com/myint/autoflake
rev: v2.0.1
rev: v2.2.0
hooks:
- id: autoflake
args:
@@ -45,19 +45,19 @@ repos:
args: ["--profile", "black"]
- repo: https://github.com/psf/black
rev: 23.1.0
rev: 22.12.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8
exclude: contrib
args: ["--ignore=E266,W503,E203,E501,W605"]
- repo: https://github.com/python-poetry/poetry
rev: 1.5.1 # add version here
rev: 1.6.0 # add version here
hooks:
- id: poetry-check
- id: poetry-lock

View File

@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "iam_inline_policy_no_administrative_privileges",
"CheckTitle": "Ensure inline policies that allow full \"*:*\" administrative privileges are not associated to IAM identities",
"CheckType": [
"Software and Configuration Checks",
"Industry and Regulatory Standards",
"CIS AWS Foundations Benchmark"
],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "high",
"ResourceType": "AwsIamPolicy",
"Description": "Ensure inline policies that allow full \"*:*\" administrative privileges are not associated to IAM identities",
"Risk": "IAM policies are the means by which privileges are granted to users, groups or roles. It is recommended and considered a standard security advice to grant least privilege—that is; granting only the permissions required to perform a task. Determine what users need to do and then craft policies for them that let the users perform only those tasks instead of allowing full administrative privileges. Providing full administrative privileges instead of restricting to the minimum set of permissions that the user is required to do exposes the resources to potentially unwanted actions.",
"RelatedUrl": "",
"Remediation": {
"Code": {
"CLI": "https://docs.bridgecrew.io/docs/iam_47#cli-command",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/iam_47#aws-console",
"Terraform": "https://docs.bridgecrew.io/docs/iam_47#terraform"
},
"Recommendation": {
"Text": "It is more secure to start with a minimum set of permissions and grant additional permissions as necessary; rather than starting with permissions that are too lenient and then trying to tighten them later. List policies an analyze if permissions are the least possible to conduct business activities.",
"Url": "http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "CAF Security Epic: IAM"
}

View File

@@ -0,0 +1,41 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_inline_policy_no_administrative_privileges(Check):
def execute(self) -> Check_Report_AWS:
findings = []
for policy in iam_client.policies:
if policy.attached and policy.type == "Inline":
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_arn = policy.arn
report.resource_id = policy.name
report.resource_tags = policy.tags
report.status = "PASS"
report.status_extended = f"{policy.type} policy {policy.name} for IAM identity {policy.arn} does not allow '*:*' administrative privileges."
if policy.document:
# Check the statements, if one includes *:* stop iterating over the rest
if not isinstance(policy.document["Statement"], list):
policy_statements = [policy.document["Statement"]]
else:
policy_statements = policy.document["Statement"]
for statement in policy_statements:
# Check policies with "Effect": "Allow" with "Action": "*" over "Resource": "*".
if (
statement["Effect"] == "Allow"
and "Action" in statement
and (
statement["Action"] == "*"
or statement["Action"] == ["*"]
)
and (
statement["Resource"] == "*"
or statement["Resource"] == ["*"]
)
):
report.status = "FAIL"
report.status_extended = f"{policy.type} policy {policy.name} for IAM identity {policy.arn} allows '*:*' administrative privileges."
break
findings.append(report)
return findings

View File

@@ -61,7 +61,6 @@ class IAM(AWSService):
self.__list_attached_group_policies__()
self.__list_attached_user_policies__()
self.__list_attached_role_policies__()
self.__list_inline_user_policies__()
self.__list_mfa_devices__()
self.password_policy = self.__get_password_policy__()
support_policy_arn = (
@@ -79,6 +78,9 @@ class IAM(AWSService):
self.policies.extend(self.__list_policies__("AWS"))
self.policies.extend(self.__list_policies__("Local"))
self.__list_policies_version__(self.policies)
self.__list_inline_user_policies__()
self.__list_inline_group_policies__()
self.__list_inline_role_policies__()
self.saml_providers = self.__list_saml_providers__()
self.server_certificates = self.__list_server_certificates__()
self.__list_tags_for_resource__()
@@ -375,8 +377,8 @@ class IAM(AWSService):
def __list_inline_user_policies__(self):
logger.info("IAM - List Inline User Policies...")
try:
for user in self.users:
for user in self.users:
try:
inline_user_policies = []
get_user_inline_policies_paginator = self.client.get_paginator(
"list_user_policies"
@@ -385,14 +387,114 @@ class IAM(AWSService):
UserName=user.name
):
for policy in page["PolicyNames"]:
inline_user_policies.append(policy)
try:
inline_user_policies.append(policy)
# Get inline policies & their policy documents here
inline_policy = self.client.get_user_policy(
UserName=user.name, PolicyName=policy
)
inline_user_policy_doc = inline_policy["PolicyDocument"]
self.policies.append(
Policy(
name=policy,
arn=user.arn,
type="Inline",
attached=True,
version_id="v1",
document=inline_user_policy_doc,
)
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
user.inline_policies = inline_user_policies
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}"
)
def __list_inline_group_policies__(self):
logger.info("IAM - List Inline Group Policies...")
for group in self.groups:
try:
inline_group_policies = []
get_group_inline_policies_paginator = self.client.get_paginator(
"list_group_policies"
)
for page in get_group_inline_policies_paginator.paginate(
GroupName=group.name
):
for policy in page["PolicyNames"]:
try:
inline_group_policies.append(policy)
# Get inline policies & their policy documents here:
inline_policy = self.client.get_group_policy(
GroupName=group.name, PolicyName=policy
)
inline_group_policy_doc = inline_policy["PolicyDocument"]
self.policies.append(
Policy(
name=policy,
arn=group.arn,
type="Inline",
attached=True,
version_id="v1",
document=inline_group_policy_doc,
)
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
group.inline_policies = inline_group_policies
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __list_inline_role_policies__(self):
logger.info("IAM - List Inline Role Policies...")
for role in self.roles:
try:
inline_role_policies = []
get_role_inline_policies_paginator = self.client.get_paginator(
"list_role_policies"
)
for page in get_role_inline_policies_paginator.paginate(
RoleName=role.name
):
for policy in page["PolicyNames"]:
try:
inline_role_policies.append(policy)
# Get inline policies & their policy documents here:
inline_policy = self.client.get_role_policy(
RoleName=role.name, PolicyName=policy
)
inline_role_policy_doc = inline_policy["PolicyDocument"]
self.policies.append(
Policy(
name=policy,
arn=role.arn,
type="Inline",
attached=True,
version_id="v1",
document=inline_role_policy_doc,
)
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
role.inline_policies = inline_role_policies
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __list_entities_role_for_policy__(self, policy_arn):
logger.info("IAM - List Entities Role For Policy...")
@@ -567,6 +669,7 @@ class Role(BaseModel):
assume_role_policy: dict
is_service_role: bool
attached_policies: list[dict] = []
inline_policies: list[str] = []
tags: Optional[list] = []
@@ -574,6 +677,7 @@ class Group(BaseModel):
name: str
arn: str
attached_policies: list[dict] = []
inline_policies: list[str] = []
users: list[User] = []

View File

@@ -0,0 +1,628 @@
from json import dumps
from unittest import mock
from boto3 import client, session
from moto import mock_iam
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
from prowler.providers.common.models import Audit_Metadata
AWS_ACCOUNT_NUMBER = "123456789012"
AWS_REGION = "us-east-1"
INLINE_POLICY_ADMIN = {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": ["*"], "Resource": "*"}],
}
INLINE_POLICY_NOT_ADMIN = {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "*"}],
}
ASSUME_ROLE_POLICY_DOCUMENT = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
"Action": "sts:AssumeRole",
},
}
class Test_iam_inline_policy_no_administrative_privileges:
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
session_config=None,
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_account_arn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root",
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=[AWS_REGION],
organizations_metadata=None,
audit_resources=None,
mfa_enabled=False,
audit_metadata=Audit_Metadata(
services_scanned=0,
expected_checks=[],
completed_checks=0,
audit_progress=0,
),
)
return audit_info
# Groups
@mock_iam
def test_groups_no_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM Group
group_name = "test_group"
_ = iam_client.create_group(GroupName=group_name)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 0
@mock_iam
def test_groups_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM Group
group_name = "test_group"
group_arn = iam_client.create_group(GroupName=group_name)["Group"]["Arn"]
# Put Group Policy
policy_name = "test_admin_inline_policy"
_ = iam_client.put_group_policy(
GroupName=group_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == group_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "FAIL"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {group_arn} allows '*:*' administrative privileges."
)
@mock_iam
def test_groups_no_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM Group
group_name = "test_group"
group_arn = iam_client.create_group(GroupName=group_name)["Group"]["Arn"]
# Put Group Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_group_policy(
GroupName=group_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == group_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "PASS"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {group_arn} does not allow '*:*' administrative privileges."
)
@mock_iam
def test_groups_admin_and_not_admin_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM Group
group_name = "test_group"
group_arn = iam_client.create_group(GroupName=group_name)["Group"]["Arn"]
# Put Group Policy NOT ADMIN
policy_name_not_admin = "test_not_admin_inline_policy"
_ = iam_client.put_group_policy(
GroupName=group_name,
PolicyName=policy_name_not_admin,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Put Group Policy ADMIN
policy_name_admin = "test_admin_inline_policy"
_ = iam_client.put_group_policy(
GroupName=group_name,
PolicyName=policy_name_admin,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 2
for result in results:
if result.resource_id == policy_name_admin:
assert result.region == AWS_REGION
assert result.resource_arn == group_arn
assert result.resource_id == policy_name_admin
assert result.resource_tags == []
assert result.status == "FAIL"
assert (
result.status_extended
== f"Inline policy {policy_name_admin} for IAM identity {group_arn} allows '*:*' administrative privileges."
)
elif result.resource_id == policy_name_not_admin:
assert result.region == AWS_REGION
assert result.resource_arn == group_arn
assert result.resource_id == policy_name_not_admin
assert result.resource_tags == []
assert result.status == "PASS"
assert (
result.status_extended
== f"Inline policy {policy_name_not_admin} for IAM identity {group_arn} does not allow '*:*' administrative privileges."
)
# Roles
@mock_iam
def test_roles_no_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM Role
role_name = "test_role"
_ = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 0
@mock_iam
def test_roles_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM Role
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
)["Role"]["Arn"]
# Put Role Policy
policy_name = "test_admin_inline_policy"
_ = iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == role_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "FAIL"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {role_arn} allows '*:*' administrative privileges."
)
@mock_iam
def test_roles_no_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM Role
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
)["Role"]["Arn"]
# Put Role Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == role_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "PASS"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {role_arn} does not allow '*:*' administrative privileges."
)
@mock_iam
def test_roles_admin_and_not_admin_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM Role
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
)["Role"]["Arn"]
# Put Role Policy - NOT ADMIN
policy_name_not_admin = "test_not_admin_inline_policy"
_ = iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name_not_admin,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Put Role Policy - ADMIN
policy_name_admin = "test_admin_inline_policy"
_ = iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name_admin,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 2
for result in results:
if result.resource_id == policy_name_admin:
assert result.region == AWS_REGION
assert result.resource_arn == role_arn
assert result.resource_id == policy_name_admin
assert result.resource_tags == []
assert result.status == "FAIL"
assert (
result.status_extended
== f"Inline policy {policy_name_admin} for IAM identity {role_arn} allows '*:*' administrative privileges."
)
elif result.resource_id == policy_name_not_admin:
assert result.region == AWS_REGION
assert result.resource_arn == role_arn
assert result.resource_id == policy_name_not_admin
assert result.resource_tags == []
assert result.status == "PASS"
assert (
result.status_extended
== f"Inline policy {policy_name_not_admin} for IAM identity {role_arn} does not allow '*:*' administrative privileges."
)
# Users
@mock_iam
def test_users_no_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM User
user_name = "test_user"
_ = iam_client.create_user(
UserName=user_name,
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 0
@mock_iam
def test_users_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM User
user_name = "test_user"
user_arn = iam_client.create_user(UserName=user_name,)[
"User"
]["Arn"]
# Put User Policy
policy_name = "test_admin_inline_policy"
_ = iam_client.put_user_policy(
UserName=user_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == user_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "FAIL"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {user_arn} allows '*:*' administrative privileges."
)
@mock_iam
def test_users_no_admin_inline_policy(self):
# IAM Client
iam_client = client("iam")
# Create IAM User
user_name = "test_user"
user_arn = iam_client.create_user(UserName=user_name,)[
"User"
]["Arn"]
# Put User Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_user_policy(
UserName=user_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 1
assert results[0].region == AWS_REGION
assert results[0].resource_arn == user_arn
assert results[0].resource_id == policy_name
assert results[0].resource_tags == []
assert results[0].status == "PASS"
assert (
results[0].status_extended
== f"Inline policy {policy_name} for IAM identity {user_arn} does not allow '*:*' administrative privileges."
)
@mock_iam
def test_users_admin_and_not_admin_inline_policies(self):
# IAM Client
iam_client = client("iam")
# Create IAM User
user_name = "test_user"
user_arn = iam_client.create_user(UserName=user_name,)[
"User"
]["Arn"]
# Put Group Policy - NOT ADMIN
policy_name_not_admin = "test_not_admin_inline_policy"
_ = iam_client.put_user_policy(
UserName=user_name,
PolicyName=policy_name_not_admin,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# Put Group Policy - ADMIN
policy_name_admin = "test_admin_inline_policy"
_ = iam_client.put_user_policy(
UserName=user_name,
PolicyName=policy_name_admin,
PolicyDocument=dumps(INLINE_POLICY_ADMIN),
)
# Audit Info
current_audit_info = self.set_mocked_audit_info()
from prowler.providers.aws.services.iam.iam_service import IAM
with mock.patch(
"prowler.providers.aws.lib.audit_info.audit_info.current_audit_info",
new=current_audit_info,
), mock.patch(
"prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges.iam_client",
new=IAM(current_audit_info),
):
from prowler.providers.aws.services.iam.iam_inline_policy_no_administrative_privileges.iam_inline_policy_no_administrative_privileges import (
iam_inline_policy_no_administrative_privileges,
)
check = iam_inline_policy_no_administrative_privileges()
results = check.execute()
assert len(results) == 2
for result in results:
if result.resource_id == policy_name_admin:
assert result.region == AWS_REGION
assert result.resource_arn == user_arn
assert result.resource_id == policy_name_admin
assert result.resource_tags == []
assert result.status == "FAIL"
assert (
result.status_extended
== f"Inline policy {policy_name_admin} for IAM identity {user_arn} allows '*:*' administrative privileges."
)
elif result.resource_id == policy_name_not_admin:
assert result.region == AWS_REGION
assert result.resource_arn == user_arn
assert result.resource_id == policy_name_not_admin
assert result.resource_tags == []
assert result.status == "PASS"
assert (
result.status_extended
== f"Inline policy {policy_name_not_admin} for IAM identity {user_arn} does not allow '*:*' administrative privileges."
)

View File

@@ -5,12 +5,34 @@ from freezegun import freeze_time
from moto import mock_iam
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
from prowler.providers.aws.services.iam.iam_service import IAM, is_service_role
from prowler.providers.aws.services.iam.iam_service import IAM, Policy, is_service_role
from prowler.providers.common.models import Audit_Metadata
AWS_ACCOUNT_NUMBER = "123456789012"
TEST_DATETIME = "2023-01-01T12:01:01+00:00"
INLINE_POLICY_NOT_ADMIN = {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "*"}],
}
ASSUME_ROLE_POLICY_DOCUMENT = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
"Action": "sts:AssumeRole",
},
}
SECURITY_AUDIT_POLICY_ARN = "arn:aws:iam::aws:policy/SecurityAudit"
READ_ONLY_ACCESS_POLICY_ARN = "arn:aws:iam::aws:policy/ReadOnlyAccess"
SUPPORT_SERVICE_ROLE_POLICY_ARN = (
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
)
ADMINISTRATOR_ACCESS_POLICY_ARN = "arn:aws:iam::aws:policy/AdministratorAccess"
class Test_IAM_Service:
# Mocked Audit Info
@@ -547,7 +569,7 @@ class Test_IAM_Service:
)
iam.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/ReadOnlyAccess",
PolicyArn=READ_ONLY_ACCESS_POLICY_ARN,
)
# IAM client for this test class
@@ -561,14 +583,14 @@ class Test_IAM_Service:
assert iam.roles[0].attached_policies[0]["PolicyName"] == "ReadOnlyAccess"
assert (
iam.roles[0].attached_policies[0]["PolicyArn"]
== "arn:aws:iam::aws:policy/ReadOnlyAccess"
== READ_ONLY_ACCESS_POLICY_ARN
)
@mock_iam
def test__get_entities_attached_to_support_roles__no_roles(self):
iam_client = client("iam")
_ = iam_client.list_entities_for_policy(
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
EntityFilter="Role",
)["PolicyRoles"]
@@ -595,11 +617,11 @@ class Test_IAM_Service:
)
iam_client.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
)
iam_client.list_entities_for_policy(
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
EntityFilter="Role",
)["PolicyRoles"]
@@ -612,7 +634,7 @@ class Test_IAM_Service:
def test__get_entities_attached_to_securityaudit_roles__no_roles(self):
iam_client = client("iam")
_ = iam_client.list_entities_for_policy(
PolicyArn="arn:aws:iam::aws:policy/SecurityAudit",
PolicyArn=SECURITY_AUDIT_POLICY_ARN,
EntityFilter="Role",
)["PolicyRoles"]
@@ -639,11 +661,11 @@ class Test_IAM_Service:
)
iam_client.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/SecurityAudit",
PolicyArn=SECURITY_AUDIT_POLICY_ARN,
)
iam_client.list_entities_for_policy(
PolicyArn="arn:aws:iam::aws:policy/SecurityAudit",
PolicyArn=SECURITY_AUDIT_POLICY_ARN,
EntityFilter="Role",
)["PolicyRoles"]
@@ -753,3 +775,135 @@ nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
assert len(iam.saml_providers) == 1
assert iam.saml_providers[0]["Arn"].split("/")[1] == saml_provider_name
# Test IAM User Inline Policy
@mock_iam
def test__list_inline_user_policies__(self):
# IAM Client
iam_client = client("iam")
# Create IAM User
user_name = "test_user"
user_arn = iam_client.create_user(UserName=user_name,)[
"User"
]["Arn"]
# Put User Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_user_policy(
UserName=user_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# IAM client for this test class
audit_info = self.set_mocked_audit_info()
iam = IAM(audit_info)
assert len(iam.users) == 1
assert iam.users[0].name == user_name
assert iam.users[0].arn == user_arn
assert iam.users[0].mfa_devices == []
assert iam.users[0].password_last_used is None
assert iam.users[0].attached_policies == []
assert iam.users[0].inline_policies == [policy_name]
assert iam.users[0].tags == []
# TODO: Workaround until this gets fixed https://github.com/getmoto/moto/issues/6712
for policy in iam.policies:
if policy.name == policy_name:
assert policy == Policy(
name=policy_name,
arn=user_arn,
version_id="v1",
type="Inline",
attached=True,
document=INLINE_POLICY_NOT_ADMIN,
)
# Test IAM Group Inline Policy
@mock_iam
def test__list_inline_group_policies__(self):
# IAM Client
iam_client = client("iam")
# Create IAM Group
group_name = "test_group"
group_arn = iam_client.create_group(GroupName=group_name,)[
"Group"
]["Arn"]
# Put User Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_group_policy(
GroupName=group_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
iam_client.delete_policy
# IAM client for this test class
audit_info = self.set_mocked_audit_info()
iam = IAM(audit_info)
assert len(iam.groups) == 1
assert iam.groups[0].name == group_name
assert iam.groups[0].arn == group_arn
assert iam.groups[0].attached_policies == []
assert iam.groups[0].inline_policies == [policy_name]
assert iam.groups[0].users == []
# TODO: Workaround until this gets fixed https://github.com/getmoto/moto/issues/6712
for policy in iam.policies:
if policy.name == policy_name:
assert policy == Policy(
name=policy_name,
arn=group_arn,
version_id="v1",
type="Inline",
attached=True,
document=INLINE_POLICY_NOT_ADMIN,
)
# Test IAM Role Inline Policy
@mock_iam
def test__list_inline_role_policies__(self):
# IAM Client
iam_client = client("iam")
# Create IAM Role
role_name = "test_role"
role_arn = iam_client.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
)["Role"]["Arn"]
# Put User Policy
policy_name = "test_not_admin_inline_policy"
_ = iam_client.put_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
)
# IAM client for this test class
audit_info = self.set_mocked_audit_info()
iam = IAM(audit_info)
assert len(iam.roles) == 1
assert iam.roles[0].name == role_name
assert iam.roles[0].arn == role_arn
assert iam.roles[0].assume_role_policy == ASSUME_ROLE_POLICY_DOCUMENT
assert not iam.roles[0].is_service_role
assert iam.roles[0].attached_policies == []
assert iam.roles[0].inline_policies == [policy_name]
assert iam.roles[0].tags == []
# TODO: Workaround until this gets fixed https://github.com/getmoto/moto/issues/6712
for policy in iam.policies:
if policy.name == policy_name:
assert policy == Policy(
name=policy_name,
arn=role_arn,
version_id="v1",
type="Inline",
attached=True,
document=INLINE_POLICY_NOT_ADMIN,
)