Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iam): add new check iam_role_administratoraccess_policy #2822

Merged
merged 21 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "iam_role_administratoraccess_policy",
"CheckTitle": "Ensure IAM Roles do not have AdministratorAccess policy attached",
"CheckType": [],
"ServiceName": "iam",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "high",
"ResourceType": "AwsIamRole",
"Description": "Ensure IAM Roles do not have AdministratorAccess policy attached",
"Risk": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It should be granted very conservatively. For granting access to 3rd party vendors, consider using alternative managed policies, such as ViewOnlyAccess or SecurityAudit.",
"RelatedUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege.",
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege"
}
},
"Categories": [
"trustboundaries"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "CAF Security Epic: IAM"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.iam_client import iam_client


class iam_role_administratoraccess_policy(Check):
def execute(self) -> Check_Report_AWS:
findings = []
for role in iam_client.roles:
if (
not role.is_service_role
): # Avoid service roles since they cannot be modified by the user
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_arn = role.arn
report.resource_id = role.name
report.resource_tags = role.tags
report.status = "PASS"
report.status_extended = (
f"IAM Role {role.name} does not have AdministratorAccess policy."
)
for policy in role.attached_policies:
if policy["PolicyName"] == "AdministratorAccess":
report.status_extended = f"IAM Role {role.name} has AdministratorAccess policy attached."
report.status = "FAIL"

findings.append(report)

return findings
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
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.aws.services.iam.iam_service import Role
from prowler.providers.common.models import Audit_Metadata

AWS_REGION = "us-east-1"
AWS_ACCOUNT_ID = "123456789012"


class Test_iam_role_administratoraccess_policy:
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_ID,
audited_account_arn=f"arn:aws:iam::{AWS_ACCOUNT_ID}: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=["us-east-1", "eu-west-1"],
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

@mock_iam
def test_no_roles(self):
from prowler.providers.aws.services.iam.iam_service import IAM

current_audit_info = self.set_mocked_audit_info()
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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 0

@mock_iam
def test_role_without_administratoraccess_policy(self):
iam = client("iam")
role_name = "test"
assume_role_policy_document = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_ID}:root"},
"Action": "sts:AssumeRole",
},
}
response = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(assume_role_policy_document),
)

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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "IAM Role test does not have AdministratorAccess policy."
)
assert result[0].resource_id == "test"
assert result[0].resource_arn == response["Role"]["Arn"]
assert result[0].resource_tags == []

@mock_iam
def test_role_with_securityaudit_policy(self):
iam = client("iam")
role_name = "test"
assume_role_policy_document = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_ID}:root"},
"Action": "sts:AssumeRole",
},
}
response = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(assume_role_policy_document),
)
iam.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/SecurityAudit",
)

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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "IAM Role test does not have AdministratorAccess policy."
)
assert result[0].resource_id == "test"
assert result[0].resource_arn == response["Role"]["Arn"]
assert result[0].resource_tags == []

@mock_iam
def test_role_with_administratoraccess_policy(self):
iam = client("iam")
role_name = "test"
assume_role_policy_document = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::012345678910:root"},
"Action": "sts:AssumeRole",
},
}
response = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(assume_role_policy_document),
)
iam.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess",
)

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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "IAM Role test has AdministratorAccess policy attached."
)
assert result[0].resource_id == "test"
assert result[0].resource_arn == response["Role"]["Arn"]
assert result[0].resource_tags == []

@mock_iam
def test_asterisk_principal_role_with_administratoraccess_policy(self):
iam = client("iam")
role_name = "test"
assume_role_policy_document = {
"Version": "2012-10-17",
"Statement": {
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "sts:AssumeRole",
},
}
response = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=dumps(assume_role_policy_document),
)
iam.attach_role_policy(
RoleName=role_name,
PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess",
)

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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=IAM(current_audit_info),
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "IAM Role test has AdministratorAccess policy attached."
)
assert result[0].resource_id == "test"
assert result[0].resource_arn == response["Role"]["Arn"]
assert result[0].resource_tags == []

@mock_iam
def test_only_aws_service_linked_roles(self):
iam_client = mock.MagicMock
iam_client.roles = []
iam_client.roles.append(
Role(
name="AWSServiceRoleForAmazonGuardDuty",
arn="arn:aws:iam::106908755756:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty",
assume_role_policy={
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole",
}
],
},
is_service_role=True,
)
)

current_audit_info = self.set_mocked_audit_info()

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_role_administratoraccess_policy.iam_role_administratoraccess_policy.iam_client",
new=iam_client,
):
# Test Check
from prowler.providers.aws.services.iam.iam_role_administratoraccess_policy.iam_role_administratoraccess_policy import (
iam_role_administratoraccess_policy,
)

check = iam_role_administratoraccess_policy()
result = check.execute()
assert len(result) == 0