AWS IAM Policy No Statements with Admin Access Audit: SOC2 Compliance

This runbook reviews and ensures AWS IAM policies don't contain overly permissive statements granting full admin access, adhering to the principle of least privilege for enhanced security.

  1. 1

    This lists all IAM users in an AWS account, providing key details like usernames, user IDs, and creation dates. Essential for managing permissions and auditing access, this function supports security and compliance protocols by offering a clear view of user entities and their access levels. It's instrumental in enforcing security policies and the principle of least privilege in AWS resource access management.

    import boto3 import botocore.exceptions creds = _get_creds(cred_label)['creds'] access_key = creds['username'] secret_key = creds['password'] # Initialize the IAM client iam_client = boto3.client('iam',aws_access_key_id=access_key,aws_secret_access_key=secret_key) try: # Create a paginator for the list_users operation paginator = iam_client.get_paginator('list_users') # Use the paginator to paginate through the users table = context.newtable() table.title = "User list" table.num_cols = 3 table.num_rows = 1 table.has_header_row = True rownum = 0 table.setval(rownum, 0, "User name") table.setval(rownum, 1, "User ID") table.setval(rownum, 2, "Created on") for page in paginator.paginate(): users = page['Users'] table.num_rows += len(page['Users']) # Output user details if users: # print("List of IAM Users:") for user in users: rownum += 1 # print(f"Username: {user['UserName']}, User ID: {user['UserId']}, Created On: {user['CreateDate']}") table.setval(rownum, 0, user['UserName']) table.setval(rownum, 1, user['UserId']) table.setval(rownum, 2, user['CreateDate']) else: print("No IAM users found in this page.") # Handle specific exceptions except botocore.exceptions.NoCredentialsError: print("Credentials not available") except botocore.exceptions.PartialCredentialsError: print("Incomplete credentials provided") except botocore.exceptions.SSLError: print("SSL connection could not be established. Ensure your network allows SSL connections to AWS services") except botocore.exceptions.EndpointConnectionError: print("Unable to connect to the endpoint. Check your AWS configuration and network settings") except botocore.exceptions.ClientError as e: print(f"Unexpected error occurred accessing AWS: {e}") # Handle general exceptions except Exception as e: print(f"An unhandled error occurred: {str(e)}")
    copied
    1
  2. 2

    This task audits AWS IAM users to identify those with administrative access. It ensures adherence to security standards by limiting broad access rights, crucial for mitigating risks associated with unauthorized permissions in a cloud environment.

    import boto3 from botocore.exceptions import ClientError, BotoCoreError creds = _get_creds(cred_label)['creds'] access_key = creds['username'] secret_key = creds['password'] def is_admin_policy(policy_document, exclude_permission_boundary): """ Check if the policy document contains admin access statements. """ for statement in policy_document.get('Statement', []): if statement.get('Effect') == 'Allow' and \ statement.get('Action') == '*' and \ statement.get('Resource') == '*': if not exclude_permission_boundary or \ (exclude_permission_boundary and 'Condition' not in statement): return True return False def evaluate_group_policies(user_name, iam, exclude_permission_boundary): """ Evaluate policies attached to groups for a given user. """ reasons = [] try: groups = iam.list_groups_for_user(UserName=user_name)['Groups'] for group in groups: attached_policies = iam.list_attached_group_policies(GroupName=group['GroupName'])['AttachedPolicies'] #print(attached_policies) # for debugging for policy in attached_policies: policy_details = iam.get_policy(PolicyArn=policy['PolicyArn']) if 'DefaultVersionId' in policy_details['Policy']: policy_version = iam.get_policy_version( PolicyArn=policy['PolicyArn'], VersionId=policy_details['Policy']['DefaultVersionId'] ) if is_admin_policy(policy_version['PolicyVersion']['Document'], exclude_permission_boundary): reasons.append(f"Group Attached Policy: {policy['PolicyArn']} ({group['GroupName']})") except ClientError as e: print(f"Error retrieving group policies for user {user_name}: {e}") return reasons def evaluate_attached_policies(user_name, iam, exclude_permission_boundary): """ Evaluate attached managed policies for a given user. """ reasons = [] try: attached_policies = iam.list_attached_user_policies(UserName=user_name)['AttachedPolicies'] #print(attached_policies) # for debugging for policy in attached_policies: policy_details = iam.get_policy(PolicyArn=policy['PolicyArn']) if 'DefaultVersionId' in policy_details['Policy']: policy_version = iam.get_policy_version( PolicyArn=policy['PolicyArn'], VersionId=policy_details['Policy']['DefaultVersionId'] ) if is_admin_policy(policy_version['PolicyVersion']['Document'], exclude_permission_boundary): reasons.append(f"Attached Policy: {policy['PolicyArn']}") except ClientError as e: print(f"Error retrieving attached policies for user {user_name}: {e}") return reasons def evaluate_inline_policies(user_name, iam, exclude_permission_boundary): """ Evaluate inline policies for a given user. """ reasons = [] try: inline_policies = iam.list_user_policies(UserName=user_name)['PolicyNames'] #print(inline_policies) # for debugging for policy_name in inline_policies: policy_document = iam.get_user_policy( UserName=user_name, PolicyName=policy_name )['PolicyDocument'] if is_admin_policy(policy_document, exclude_permission_boundary): reasons.append(f"Inline Policy: {policy_name}") except ClientError as e: print(f"Error retrieving inline policies for user {user_name}: {e}") return reasons def evaluate_iam_users_and_policies(exclude_permission_boundary=False): """ Evaluates IAM users for admin access in attached, inline, and group policies. """ iam = boto3.client('iam',aws_access_key_id=access_key,aws_secret_access_key=secret_key) compliance_report = { 'compliant': [], 'non_compliant': {} } try: users = iam.list_users()['Users'] for user in users: user_name = user['UserName'] print(f"Evaluating user: {user_name}") reasons = evaluate_attached_policies(user_name, iam, exclude_permission_boundary) + \ evaluate_inline_policies(user_name, iam, exclude_permission_boundary) + \ evaluate_group_policies(user_name, iam, exclude_permission_boundary) if reasons: compliance_report['non_compliant'][user_name] = reasons else: compliance_report['compliant'].append(user_name) except ClientError as e: print(f"ClientError while listing IAM users: {e}") except BotoCoreError as e: print(f"BotoCoreError encountered: {e}") except Exception as e: print(f"An unexpected error occurred: {e}") # Print Compliance Report if compliance_report['non_compliant']: print("\nNon-Compliant IAM Users (Admin Access Found):") for user, reasons in compliance_report['non_compliant'].items(): print(f"{user} - Reasons: {', '.join(reasons)}") else: print("\nNo Non-Compliant IAM Users Found.") if compliance_report['compliant']: print("\nCompliant IAM Users (No Admin Access):") for user in compliance_report['compliant']: print(user) else: print("\nAll IAM Users are Non-Compliant.") exclude_permission_boundary = False evaluate_iam_users_and_policies(exclude_permission_boundary) context.skip_sub_tasks=True
    copied
    2
    1. 2.1

      This task is used to detach managed IAM policies or delete inline policies from specific IAM users. This action is crucial for maintaining secure and appropriate access levels within AWS environments, ensuring compliance with best security practices.

      import boto3 from botocore.exceptions import ClientError, NoCredentialsError, BotoCoreError creds = _get_creds(cred_label)['creds'] access_key = creds['username'] secret_key = creds['password'] def remove_or_modify_policy(iam_client, user_name, policy_arn=None, inline_policy_name=None): """ Detach a managed IAM policy or delete an inline IAM policy from a specified AWS IAM user. Args: iam_client: An initialized Boto3 IAM client. user_name: The name of the IAM user. policy_arn: The ARN of the managed IAM policy to be detached. inline_policy_name: The name of the inline IAM policy to be deleted. The function checks if the user exists and whether the specified policies are attached or exist, then proceeds with the appropriate action. """ try: # Check if the user exists iam_client.get_user(UserName=user_name) if policy_arn: # Detach managed policy if it is attached attached_policies = iam_client.list_attached_user_policies(UserName=user_name)['AttachedPolicies'] if any(policy['PolicyArn'] == policy_arn for policy in attached_policies): iam_client.detach_user_policy(UserName=user_name, PolicyArn=policy_arn) print(f"Detached policy {policy_arn} from {user_name}") else: print(f"Policy {policy_arn} is not attached to {user_name}") elif inline_policy_name: # Delete inline policy if it exists inline_policies = iam_client.list_user_policies(UserName=user_name)['PolicyNames'] if inline_policy_name in inline_policies: iam_client.delete_user_policy(UserName=user_name, PolicyName=inline_policy_name) print(f"Deleted inline policy {inline_policy_name} from {user_name}") else: print(f"Inline policy {inline_policy_name} does not exist for {user_name}") except ClientError as e: print(f"An AWS ClientError occurred: {e}") except NoCredentialsError: print("No AWS credentials available. Please configure them.") except BotoCoreError as e: print(f"A BotoCoreError occurred: {e}") except Exception as e: print(f"An unexpected error occurred: {e}") iam_client = boto3.client('iam',aws_access_key_id=access_key,aws_secret_access_key=secret_key) # user_name = 'test_user' # policy_arn_to_remove = 'arn:aws:iam::aws:policy/AdministratorAccess' # Example ARN # inline_policy_name = 'your-inline-policy-name' remove_or_modify_policy(iam_client, user_name, policy_arn=policy_arn_to_remove)
      copied
      2.1