mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 14:55:00 +00:00
feat(iam): Add IAM checks (#1407)
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy
|
||||
# of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
CHECK_ID_check116="1.16"
|
||||
CHECK_TITLE_check116="[check116] Ensure IAM policies are attached only to groups or roles"
|
||||
CHECK_SCORED_check116="SCORED"
|
||||
CHECK_CIS_LEVEL_check116="LEVEL1"
|
||||
CHECK_SEVERITY_check116="Low"
|
||||
CHECK_ASFF_TYPE_check116="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
|
||||
CHECK_ASFF_RESOURCE_TYPE_check116="AwsIamUser"
|
||||
CHECK_ALTERNATE_check116="check116"
|
||||
CHECK_ASFF_COMPLIANCE_TYPE_check116="ens-op.acc.3.aws.iam.1"
|
||||
CHECK_SERVICENAME_check116="iam"
|
||||
CHECK_RISK_check116='By default IAM users; groups; and roles have no access to AWS resources. IAM policies are the means by which privileges are granted to users; groups; or roles. It is recommended that IAM policies be applied directly to groups and roles but not users. Assigning privileges at the group or role level reduces the complexity of access management as the number of users grow. Reducing access management complexity may in-turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges.'
|
||||
CHECK_REMEDIATION_check116='Remove any policy attached directly to the user. Use groups or roles instead.'
|
||||
CHECK_DOC_check116='https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'
|
||||
CHECK_CAF_EPIC_check116='IAM'
|
||||
|
||||
check116(){
|
||||
# "Ensure IAM policies are attached only to groups or roles (Scored)"
|
||||
LIST_USERS=$($AWSCLI iam list-users --query 'Users[*].UserName' --output text $PROFILE_OPT --region $REGION)
|
||||
for user in $LIST_USERS;do
|
||||
USER_ATTACHED_POLICY=$($AWSCLI iam list-attached-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
|
||||
USER_INLINE_POLICY=$($AWSCLI iam list-user-policies --output text $PROFILE_OPT --region $REGION --user-name $user)
|
||||
if [[ $USER_ATTACHED_POLICY ]] || [[ $USER_INLINE_POLICY ]]
|
||||
then
|
||||
if [[ $USER_ATTACHED_POLICY ]]
|
||||
then
|
||||
textFail "$REGION: $user has managed policy directly attached" "$REGION" "$user"
|
||||
fi
|
||||
if [[ $USER_INLINE_POLICY ]]
|
||||
then
|
||||
textFail "$REGION: $user has inline policy directly attached" "$REGION" "$user"
|
||||
fi
|
||||
else
|
||||
textPass "$REGION: No policies attached to user $user" "$REGION" "$user"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "iam_policy_attached_only_to_group_or_roles",
|
||||
"CheckTitle": "Ensure IAM policies are attached only to groups or roles",
|
||||
"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": "low",
|
||||
"ResourceType": "AwsIamUser",
|
||||
"Description": "Ensure IAM policies are attached only to groups or roles",
|
||||
"Risk": "By default IAM users; groups; and roles have no access to AWS resources. IAM policies are the means by which privileges are granted to users; groups; or roles. It is recommended that IAM policies be applied directly to groups and roles but not users. Assigning privileges at the group or role level reduces the complexity of access management as the number of users grow. Reducing access management complexity may in-turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Remove any policy attached directly to the user. Use groups or roles instead.",
|
||||
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "CAF Security Epic: IAM",
|
||||
"Compliance": [
|
||||
{
|
||||
"Control": [
|
||||
"1.16"
|
||||
],
|
||||
"Framework": "CIS-AWS",
|
||||
"Group": [
|
||||
"level1"
|
||||
],
|
||||
"Version": "1.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class iam_policy_attached_only_to_group_or_roles(Check):
|
||||
def execute(self) -> Check_Report:
|
||||
findings = []
|
||||
if iam_client.users:
|
||||
for user in iam_client.users:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_id = user.name
|
||||
report.resource_arn = user.arn
|
||||
if user.attached_policies or user.inline_policies:
|
||||
if user.attached_policies:
|
||||
for policy in user.attached_policies:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"User {user.name} has attached the following policy {policy['PolicyName']}"
|
||||
report.resource_id = user.name
|
||||
findings.append(report)
|
||||
if user.inline_policies:
|
||||
for policy in user.inline_policies:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"User {user.name} has the following inline policy {policy}"
|
||||
)
|
||||
report.resource_id = user.name
|
||||
findings.append(report)
|
||||
|
||||
else:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"User {user.name} has no inline or attached policies"
|
||||
)
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,138 @@
|
||||
from json import dumps
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_iam
|
||||
|
||||
|
||||
class Test_iam_policy_attached_only_to_group_or_roles:
|
||||
@mock_iam
|
||||
def test_iam_user_attached_policy(self):
|
||||
result = []
|
||||
iam_client = client("iam")
|
||||
user = "test_attached_policy"
|
||||
policy_name = "policy1"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_user(UserName=user)
|
||||
policyArn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
iam_client.attach_user_policy(UserName=user, PolicyArn=policyArn)
|
||||
|
||||
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_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles import (
|
||||
iam_policy_attached_only_to_group_or_roles,
|
||||
)
|
||||
|
||||
check = iam_policy_attached_only_to_group_or_roles()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
|
||||
@mock_iam
|
||||
def test_iam_user_attached_and_inline_policy(self):
|
||||
result = []
|
||||
iam_client = client("iam")
|
||||
user = "test_inline_policy"
|
||||
policyName = "policy1"
|
||||
policyDocument = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_user(UserName=user)
|
||||
iam_client.put_user_policy(
|
||||
UserName=user, PolicyName=policyName, PolicyDocument=dumps(policyDocument)
|
||||
)
|
||||
policyArn = iam_client.create_policy(
|
||||
PolicyName=policyName, PolicyDocument=dumps(policyDocument)
|
||||
)["Policy"]["Arn"]
|
||||
iam_client.attach_user_policy(UserName=user, PolicyArn=policyArn)
|
||||
|
||||
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_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles import (
|
||||
iam_policy_attached_only_to_group_or_roles,
|
||||
)
|
||||
|
||||
check = iam_policy_attached_only_to_group_or_roles()
|
||||
result = check.execute()
|
||||
assert len(result) == 2
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[1].status == "FAIL"
|
||||
assert search(
|
||||
f"User {user} has attached the following policy",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert search(f"User {user} has the following inline policy", result[1].status_extended)
|
||||
|
||||
@mock_iam
|
||||
def test_iam_user_inline_policy(self):
|
||||
result = []
|
||||
iam_client = client("iam")
|
||||
user = "test_attached_inline_policy"
|
||||
policyName = "policy1"
|
||||
policyDocument = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_user(UserName=user)
|
||||
iam_client.put_user_policy(
|
||||
UserName=user, PolicyName=policyName, PolicyDocument=dumps(policyDocument)
|
||||
)
|
||||
|
||||
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_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles import (
|
||||
iam_policy_attached_only_to_group_or_roles,
|
||||
)
|
||||
|
||||
check = iam_policy_attached_only_to_group_or_roles()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
|
||||
@mock_iam
|
||||
def test_iam_user_no_policies(self):
|
||||
result = []
|
||||
iam_client = client("iam")
|
||||
user = "test_no_policies"
|
||||
iam_client.create_user(UserName=user)
|
||||
|
||||
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_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_attached_only_to_group_or_roles.iam_policy_attached_only_to_group_or_roles import (
|
||||
iam_policy_attached_only_to_group_or_roles,
|
||||
)
|
||||
|
||||
check = iam_policy_attached_only_to_group_or_roles()
|
||||
result = check.execute()
|
||||
assert result[0].status == "PASS"
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "iam_policy_no_administrative_privileges",
|
||||
"CheckTitle": "Ensure IAM policies that allow full \"*:*\" administrative privileges are not created",
|
||||
"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": "medium",
|
||||
"ResourceType": "AwsIamPolicy",
|
||||
"Description": "Ensure IAM policies that allow full \"*:*\" administrative privileges are not created",
|
||||
"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": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"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": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "CAF Security Epic: IAM",
|
||||
"Compliance": [
|
||||
{
|
||||
"Control": [
|
||||
"1.22"
|
||||
],
|
||||
"Framework": "CIS-AWS",
|
||||
"Group": [
|
||||
"level1"
|
||||
],
|
||||
"Version": "1.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class iam_policy_no_administrative_privileges(Check):
|
||||
def execute(self) -> Check_Report:
|
||||
findings = []
|
||||
for index, policy_document in enumerate(iam_client.list_policies_version):
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_arn = iam_client.policies[index]["Arn"]
|
||||
report.resource_id = iam_client.policies[index]["PolicyName"]
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Policy {iam_client.policies[index]['PolicyName']} does not allow \"*:*\" administrative privileges"
|
||||
# Check the statements, if one includes *:* stop iterating over the rest
|
||||
for statement in policy_document["Statement"]:
|
||||
if (
|
||||
statement["Action"] == "*"
|
||||
and statement["Effect"] == "Allow"
|
||||
and statement["Resource"] == "*"
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Policy {iam_client.policies[index]['PolicyName']} allows \"*:*\" administrative privileges"
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,111 @@
|
||||
from json import dumps
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_iam
|
||||
|
||||
|
||||
class Test_iam_policy_no_administrative_privileges_test:
|
||||
@mock_iam
|
||||
def test_policy_administrative(self):
|
||||
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy1"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "*", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
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_no_administrative_privileges.iam_policy_no_administrative_privileges.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_no_administrative_privileges.iam_policy_no_administrative_privileges import (
|
||||
iam_policy_no_administrative_privileges,
|
||||
)
|
||||
|
||||
check = iam_policy_no_administrative_privileges()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
|
||||
@mock_iam
|
||||
def test_policy_non_administrative(self):
|
||||
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy1"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
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_no_administrative_privileges.iam_policy_no_administrative_privileges.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_no_administrative_privileges.iam_policy_no_administrative_privileges import (
|
||||
iam_policy_no_administrative_privileges,
|
||||
)
|
||||
|
||||
check = iam_policy_no_administrative_privileges()
|
||||
result = check.execute()
|
||||
assert result[0].status == "PASS"
|
||||
|
||||
@mock_iam
|
||||
def test_policy_administrative_and_non_administrative(self):
|
||||
|
||||
iam_client = client("iam")
|
||||
policy_name_non_administrative = "policy1"
|
||||
policy_document_non_administrative = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
policy_name_administrative = "policy2"
|
||||
policy_document_administrative = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "*", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_policy(
|
||||
PolicyName=policy_name_non_administrative,
|
||||
PolicyDocument=dumps(policy_document_non_administrative),
|
||||
)["Policy"]["Arn"]
|
||||
iam_client.create_policy(
|
||||
PolicyName=policy_name_administrative,
|
||||
PolicyDocument=dumps(policy_document_administrative),
|
||||
)["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_no_administrative_privileges.iam_policy_no_administrative_privileges.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_policy_no_administrative_privileges.iam_policy_no_administrative_privileges import (
|
||||
iam_policy_no_administrative_privileges,
|
||||
)
|
||||
|
||||
check = iam_policy_no_administrative_privileges()
|
||||
result = check.execute()
|
||||
assert len(result) == 2
|
||||
assert result[0].status == "PASS"
|
||||
assert result[1].status == "FAIL"
|
||||
@@ -23,8 +23,15 @@ class IAM:
|
||||
self.groups = self.__get_groups__()
|
||||
self.__get_group_users__()
|
||||
self.__list_attached_group_policies__()
|
||||
self.__list_attached_user_policies__()
|
||||
self.__list_inline_user_policies__()
|
||||
self.__list_mfa_devices__()
|
||||
self.password_policy = self.__get_password_policy__()
|
||||
self.entities_attached_to_support_roles = (
|
||||
self.__get_entities_attached_to_support_roles__()
|
||||
)
|
||||
self.policies = self.__list_policies__()
|
||||
self.list_policies_version = self.__list_policies_version__(self.policies)
|
||||
self.saml_providers = self.__list_saml_providers__()
|
||||
|
||||
def __get_client__(self):
|
||||
@@ -238,10 +245,86 @@ class IAM:
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
|
||||
def __list_attached_user_policies__(self):
|
||||
try:
|
||||
for user in self.users:
|
||||
attached_user_policies = []
|
||||
get_user_attached_policies_paginator = self.client.get_paginator(
|
||||
"list_attached_user_policies"
|
||||
)
|
||||
for page in get_user_attached_policies_paginator.paginate(
|
||||
UserName=user.name
|
||||
):
|
||||
for policy in page["AttachedPolicies"]:
|
||||
attached_user_policies.append(policy)
|
||||
|
||||
user.attached_policies = attached_user_policies
|
||||
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
|
||||
def __list_inline_user_policies__(self):
|
||||
try:
|
||||
for user in self.users:
|
||||
inline_user_policies = []
|
||||
get_user_inline_policies_paginator = self.client.get_paginator(
|
||||
"list_user_policies"
|
||||
)
|
||||
for page in get_user_inline_policies_paginator.paginate(
|
||||
UserName=user.name
|
||||
):
|
||||
for policy in page["PolicyNames"]:
|
||||
inline_user_policies.append(policy)
|
||||
|
||||
user.inline_policies = inline_user_policies
|
||||
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
|
||||
def __get_entities_attached_to_support_roles__(self):
|
||||
try:
|
||||
support_roles = []
|
||||
support_entry_policy_arn = (
|
||||
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
support_roles = self.client.list_entities_for_policy(
|
||||
PolicyArn=support_entry_policy_arn, EntityFilter="Role"
|
||||
)["PolicyRoles"]
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
|
||||
finally:
|
||||
return support_roles
|
||||
|
||||
def __list_policies__(self):
|
||||
try:
|
||||
policies = []
|
||||
list_policies_paginator = self.client.get_paginator("list_policies")
|
||||
for page in list_policies_paginator.paginate(Scope="Local"):
|
||||
for policy in page["Policies"]:
|
||||
policies.append(policy)
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
finally:
|
||||
return policies
|
||||
|
||||
def __list_policies_version__(self, policies):
|
||||
try:
|
||||
policies_version = []
|
||||
|
||||
for policy in policies:
|
||||
policy_version = self.client.get_policy_version(
|
||||
PolicyArn=policy["Arn"], VersionId=policy["DefaultVersionId"]
|
||||
)
|
||||
policies_version.append(policy_version["PolicyVersion"]["Document"])
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
finally:
|
||||
return policies_version
|
||||
|
||||
def __list_saml_providers__(self):
|
||||
try:
|
||||
saml_providers = self.client.list_saml_providers()["SAMLProviderList"]
|
||||
|
||||
except Exception as error:
|
||||
logger.error(f"{self.region} -- {error.__class__.__name__}: {error}")
|
||||
|
||||
@@ -249,6 +332,7 @@ class IAM:
|
||||
return saml_providers
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class MFADevice:
|
||||
serial_number: str
|
||||
@@ -263,22 +347,26 @@ class MFADevice:
|
||||
class User:
|
||||
name: str
|
||||
arn: str
|
||||
mfa_devices: list[MFADevice]
|
||||
mfa_devices: "list[MFADevice]"
|
||||
password_last_used: str
|
||||
attached_policies: "list[dict]"
|
||||
inline_policies: "list[str]"
|
||||
|
||||
def __init__(self, name, arn, password_last_used):
|
||||
self.name = name
|
||||
self.arn = arn
|
||||
self.password_last_used = password_last_used
|
||||
self.mfa_devices = []
|
||||
self.attached_policies = []
|
||||
self.inline_policies = []
|
||||
|
||||
|
||||
@dataclass
|
||||
class Group:
|
||||
name: str
|
||||
arn: str
|
||||
attached_policies: list[dict]
|
||||
users: list[User]
|
||||
attached_policies: "list[dict]"
|
||||
users: " list[User]"
|
||||
|
||||
def __init__(self, name, arn):
|
||||
self.name = name
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from json import dumps
|
||||
|
||||
from boto3 import client, session
|
||||
from moto import mock_iam
|
||||
@@ -375,6 +376,89 @@ class Test_IAM_Service:
|
||||
iam.groups[0].attached_policies[0]["PolicyArn"] == policy["Policy"]["Arn"]
|
||||
)
|
||||
|
||||
@mock_iam
|
||||
def test__get_entities_attached_to_support_roles__no_roles(self):
|
||||
iam_client = client("iam")
|
||||
support_roles = iam_client.list_entities_for_policy(
|
||||
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
|
||||
EntityFilter="Role",
|
||||
)["PolicyRoles"]
|
||||
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
iam = IAM(audit_info)
|
||||
assert len(iam.entities_attached_to_support_roles) == 0
|
||||
|
||||
@mock_iam
|
||||
def test__get_entities_attached_to_support_roles__(self):
|
||||
iam_client = client("iam")
|
||||
role_name = "test_support"
|
||||
assume_role_policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": {
|
||||
"Sid": "test",
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": "*"},
|
||||
"Action": "sts:AssumeRole",
|
||||
},
|
||||
}
|
||||
iam_client.create_role(
|
||||
RoleName=role_name,
|
||||
AssumeRolePolicyDocument=dumps(assume_role_policy_document),
|
||||
)
|
||||
iam_client.attach_role_policy(
|
||||
RoleName=role_name,
|
||||
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
|
||||
)
|
||||
|
||||
iam_client.list_entities_for_policy(
|
||||
PolicyArn="arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy",
|
||||
EntityFilter="Role",
|
||||
)["PolicyRoles"]
|
||||
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
iam = IAM(audit_info)
|
||||
assert len(iam.entities_attached_to_support_roles) == 1
|
||||
assert iam.entities_attached_to_support_roles[0]["RoleName"] == role_name
|
||||
|
||||
@mock_iam
|
||||
def test___list_policies__(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy1"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
iam = IAM(audit_info)
|
||||
assert len(iam.policies) == 1
|
||||
assert iam.policies[0]["PolicyName"] == "policy1"
|
||||
|
||||
@mock_iam
|
||||
def test__list_policies_version__(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy2"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{"Effect": "Allow", "Action": "*", "Resource": "*"},
|
||||
],
|
||||
}
|
||||
iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)
|
||||
audit_info = self.set_mocked_audit_info()
|
||||
iam = IAM(audit_info)
|
||||
|
||||
assert len(iam.list_policies_version) == 1
|
||||
assert iam.list_policies_version[0]["Statement"][0]["Effect"] == "Allow"
|
||||
assert iam.list_policies_version[0]["Statement"][0]["Action"] == "*"
|
||||
assert iam.list_policies_version[0]["Statement"][0]["Resource"] == "*"
|
||||
|
||||
# Test IAM List SAML Providers
|
||||
@mock_iam
|
||||
def test__list_saml_providers__(self):
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "iam_support_role_created",
|
||||
"CheckTitle": "Ensure a support role has been created to manage incidents with AWS Support",
|
||||
"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": "medium",
|
||||
"ResourceType": "AwsIamRole",
|
||||
"Description": "Ensure a support role has been created to manage incidents with AWS Support",
|
||||
"Risk": "AWS provides a support center that can be used for incident notification and response; as well as technical support and customer services. Create an IAM Role to allow authorized users to manage incidents with AWS Support.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Create an IAM role for managing incidents with AWS.",
|
||||
"Url": "https://docs.aws.amazon.com/awssupport/latest/user/using-service-linked-roles-sup.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "CAF Security Epic: IAM",
|
||||
"Compliance": [
|
||||
{
|
||||
"Control": [
|
||||
"1.20"
|
||||
],
|
||||
"Framework": "CIS-AWS",
|
||||
"Group": [
|
||||
"level1"
|
||||
],
|
||||
"Version": "1.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class iam_support_role_created(Check):
|
||||
def execute(self) -> Check_Report:
|
||||
findings = []
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_id = "AWSSupportServiceRolePolicy"
|
||||
report.resource_arn = (
|
||||
"arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
if iam_client.entities_attached_to_support_roles:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Support policy attached to role {iam_client.entities_attached_to_support_roles[0]['RoleName']}"
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Support policy is not attached to any role"
|
||||
findings.append(report)
|
||||
return findings
|
||||
@@ -0,0 +1,81 @@
|
||||
from json import dumps
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_iam
|
||||
|
||||
|
||||
class Test_iam_support_role_created:
|
||||
@mock_iam
|
||||
def test_support_role_created(self):
|
||||
iam = client("iam")
|
||||
role_name = "test_support"
|
||||
assume_role_policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": {
|
||||
"Sid": "test",
|
||||
"Effect": "Allow",
|
||||
"Principal": {"AWS": "*"},
|
||||
"Action": "sts:AssumeRole",
|
||||
},
|
||||
}
|
||||
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/aws-service-role/AWSSupportServiceRolePolicy",
|
||||
)
|
||||
|
||||
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_support_role_created.iam_support_role_created.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_support_role_created.iam_support_role_created import (
|
||||
iam_support_role_created,
|
||||
)
|
||||
|
||||
check = iam_support_role_created()
|
||||
result = check.execute()
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
f"Support policy attached to role {role_name}",
|
||||
result[0].status_extended,
|
||||
)
|
||||
assert result[0].resource_id == "AWSSupportServiceRolePolicy"
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
|
||||
@mock_iam
|
||||
def test_no_support_role_created(self):
|
||||
|
||||
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_support_role_created.iam_support_role_created.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
):
|
||||
from providers.aws.services.iam.iam_support_role_created.iam_support_role_created import (
|
||||
iam_support_role_created,
|
||||
)
|
||||
|
||||
check = iam_support_role_created()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== "Support policy is not attached to any role"
|
||||
)
|
||||
assert result[0].resource_id == "AWSSupportServiceRolePolicy"
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== "arn:aws:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "iam_user_no_setup_initial_access_key",
|
||||
"CheckTitle": "Do not setup access keys during initial user setup for all IAM users that have a console password",
|
||||
"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": "medium",
|
||||
"ResourceType": "AwsIamUser",
|
||||
"Description": "Do not setup access keys during initial user setup for all IAM users that have a console password",
|
||||
"Risk": "AWS console defaults the checkbox for creating access keys to enabled. This results in many access keys being generated unnecessarily. In addition to unnecessary credentials; it also generates unnecessary management work in auditing and rotating these keys. Requiring that additional steps be taken by the user after their profile has been created will give a stronger indication of intent that access keys are (a) necessary for their work and (b) once the access key is established on an account that the keys may be in use somewhere in the organization.",
|
||||
"RelatedUrl": "",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "From the IAM console: generate credential report and disable not required keys.",
|
||||
"Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"Tags": {
|
||||
"Tag1Key": "value",
|
||||
"Tag2Key": "value"
|
||||
},
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "CAF Security Epic: IAM",
|
||||
"Compliance": [
|
||||
{
|
||||
"Control": [
|
||||
"1.21"
|
||||
],
|
||||
"Framework": "CIS-AWS",
|
||||
"Group": [
|
||||
"level1"
|
||||
],
|
||||
"Version": "1.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
from lib.check.models import Check, Check_Report
|
||||
from providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
class iam_user_no_setup_initial_access_key(Check):
|
||||
def execute(self) -> Check_Report:
|
||||
findings = []
|
||||
for user_record in iam_client.credential_report:
|
||||
if (
|
||||
user_record["access_key_1_active"] == "true"
|
||||
and user_record["access_key_1_last_used_date"] == "N/A"
|
||||
and user_record["password_enabled"] == "true"
|
||||
) or (
|
||||
user_record["access_key_2_active"] == "true"
|
||||
and user_record["access_key_2_last_used_date"] == "N/A"
|
||||
and user_record["password_enabled"] == "true"
|
||||
):
|
||||
if (
|
||||
user_record["access_key_1_active"] == "true"
|
||||
and user_record["access_key_1_last_used_date"] == "N/A"
|
||||
and user_record["password_enabled"] == "true"
|
||||
):
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_id = user_record["user"]
|
||||
report.resource_arn = user_record["arn"]
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"User {user_record['user']} has never used access key 1"
|
||||
)
|
||||
findings.append(report)
|
||||
if (
|
||||
user_record["access_key_2_active"] == "true"
|
||||
and user_record["access_key_2_last_used_date"] == "N/A"
|
||||
and user_record["password_enabled"] == "true"
|
||||
):
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_id = user_record["user"]
|
||||
report.resource_arn = user_record["arn"]
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"User {user_record['user']} has never used access key 2"
|
||||
)
|
||||
findings.append(report)
|
||||
else:
|
||||
report = Check_Report(self.metadata)
|
||||
report.region = iam_client.region
|
||||
report.resource_id = user_record["user"]
|
||||
report.resource_arn = user_record["arn"]
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"User {user_record['user']} does not have access keys or uses the access keys configured"
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -0,0 +1,88 @@
|
||||
from csv import DictReader
|
||||
from re import search
|
||||
from unittest import mock
|
||||
|
||||
from moto import mock_iam
|
||||
|
||||
|
||||
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"""
|
||||
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_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.iam.iam_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key import (
|
||||
iam_user_no_setup_initial_access_key,
|
||||
)
|
||||
|
||||
service_client.credential_report = credential_list
|
||||
|
||||
check = iam_user_no_setup_initial_access_key()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert search("has never used access key 1", result[0].status_extended)
|
||||
|
||||
@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"""
|
||||
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_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.iam.iam_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key import (
|
||||
iam_user_no_setup_initial_access_key,
|
||||
)
|
||||
|
||||
service_client.credential_report = credential_list
|
||||
|
||||
check = iam_user_no_setup_initial_access_key()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert search("has never used access key 2", result[0].status_extended)
|
||||
|
||||
@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"""
|
||||
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_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key.iam_client",
|
||||
new=IAM(current_audit_info),
|
||||
) as service_client:
|
||||
from providers.aws.services.iam.iam_user_no_setup_initial_access_key.iam_user_no_setup_initial_access_key import (
|
||||
iam_user_no_setup_initial_access_key,
|
||||
)
|
||||
|
||||
service_client.credential_report = credential_list
|
||||
|
||||
check = iam_user_no_setup_initial_access_key()
|
||||
result = check.execute()
|
||||
assert result[0].status == "PASS"
|
||||
assert search(
|
||||
"does not have access keys or uses the access keys configured",
|
||||
result[0].status_extended,
|
||||
)
|
||||
Reference in New Issue
Block a user