A Step-by-Step Guide to Identifying Roles Without AWS IAM Permission Boundaries

A Step-by-Step Guide to Identifying Roles Without AWS IAM Permission Boundaries

Step 1: Understanding the Problem

AWS IAM (Identity & Access Management) Roles are a secure way to grant permissions to entities that you trust. However, without a permissions boundary, a role has the potential to escalate its privileges leading to more access than needed. To avoid this, our task is to identify such roles.

Step 2: Defining the Solution

To counter this problem, we are creating an AWS Lambda function that will:

  1. Scrutinize every IAM role for permission boundaries.

  2. Compile a JSON document listing roles missing these boundaries.

  3. Store the JSON in an AWS S3 bucket.

  4. Generate a pre-signed URL for secure access to the S3 object.

  5. Send the pre-signed URL to your email using AWS SNS.

So we will get

/In conclusion, you will find a Terraform Git repository that automates the entire process.

Step 3: Setting up the Infrastructure

  1. Creating an S3 bucket to store the JSON file.

  2. Setting up SNS Topic and Subscription to enable email notifications.

    Confirm the subscription that you will receive in your email.

  3. Create a lambda function with python as runtime and attatch to it's execution role this policy.

     {
         "Version": "2012-10-17",
         "Statement": [
             {
                 "Sid": "S3Actions",
                 "Effect": "Allow",
                 "Action": [
                     "s3:PutObject",
                     "s3:ListBucket",
                     "s3:GetObject"
                 ],
                 "Resource": [
                     "arn:aws:s3:::<bucket-name>/*",
                     "arn:aws:s3:::<bucket-name>"
                 ]
             },
             {
                 "Sid": "IAMRoleActions",
                 "Effect": "Allow",
                 "Action": [
                     "iam:ListRoles",
                     "iam:GetRole"
                 ],
                 "Resource": "*"
             },
             {
                 "Sid": "SNSTopicPublish",
                 "Effect": "Allow",
                 "Action": "sns:Publish",
                 "Resource": "arn:aws:sns:<region>:<account-id>:<sns-topic>"
             }
         ]
     }
    

    Don't forget to replace replace <bucket-name>, <region>, <account-id> and <sns-topic> with your actual data.

  4. Setting up the Lambda function with the Python code that carries out the main task.

     import boto3
     import json
     from botocore.exceptions import NoCredentialsError
    
     # Function to generate presigned URL to access S3 objects
     def create_presigned_url(bucket_name, object_name, expiration=3600):
         # Initialize S3 client
         s3_client = boto3.client('s3')
         try:
             # Generate and return pre-signed URL
             response = s3_client.generate_presigned_url('get_object',
                                                         Params={'Bucket': bucket_name,
                                                                 'Key': object_name},
                                                         ExpiresIn=expiration)
         except NoCredentialsError:
             print("[Error] No AWS credentials found.")
             return None
         return response
    
     # Main function
     def lambda_handler(event, context):
         bucket_name = "<bucket-name>"
         object_name = "object_name"
         topic_arn = "<sns-topic>"
    
         # Initialize S3 and IAM clients
         s3 = boto3.client('s3')
         iam = boto3.client('iam')
    
         response = iam.list_roles()
         roles_no_boundary = []
    
         # Loop through all IAM roles
         for role in response['Roles']:
             data = iam.get_role(RoleName=role['RoleName'])
    
             # Check if the role has a permission boundary
             try:
                 data['Role']['PermissionsBoundary']['PermissionsBoundaryArn']
             except KeyError:  
                 # If not, add it to the list
                 roles_no_boundary.append({"ARN": role['Arn']})  
    
         json_data = json.dumps(roles_no_boundary)  # Convert list to JSON
    
         # Upload JSON to S3
         s3.put_object(Body=json_data, Bucket=bucket_name, Key=object_name)
    
         # Create pre-signed URL for the uploaded JSON
         url=create_presigned_url(bucket_name, object_name)
    
         # Initialize SNS client
         sns = boto3.client('sns')
    
         # Publish SNS message containing the pre-signed URL
         response = sns.publish(
             TopicArn=topic_arn,
             Message='Here is your pre-signed URL: ' + url,
             Subject='Your Presigned S3 URL'
         )
    
         return {
             'statusCode': 200,
             'body': "JSON file has been successfully generated and uploaded to S3"
         }
    

    Don't forget to replace replace <bucket-name>, and <sns-topic> with your actual data.

  5. Run a default test in your Lambda, and you will receive the email shortly.

Step 4: Monitoring and Alerts

Monitoring and alerting are crucial for maintaining our Lambda function's health. A common issue is the 'Access Denied' error, indicating insufficient permissions for the AWS IAM Role.

To address this, we'll use Amazon CloudWatch. It monitors AWS resources, tracks metrics, and logs files. We'll set up CloudWatch to detect 'Access Denied' events and trigger an alert via SNS (Simple Notification Service).

  1. The CloudWatch Logs Group is created by default when the lambda get executed (if she has sufficient permissions) so you go to the log group

  2. Creating a Subscription Filter

    Create a Subscription Filter with our priviously created Lambda function

  3. Creating a Metric Filter

    A metric filter will be used in combination with the subscription filter to convert filtered text into a numeric value that CloudWatch will continuously monitor.

  4. Setting CloudWatch Alarm

    Now that our metrics are in place, we can set up a CloudWatch alarm. This alarm will trigger whenever it detects 'Access Denied' entries.

  5. Setting up SNS Topic and Subscription

    When the alarm is triggered, it will send a message to an SNS Topic, which in turn triggers the external lambda function.

Conclusion

By following this guide, you can effectively identify AWS IAM roles without permission boundaries, ensuring tighter security and controlled access within your AWS environment. Implementing the outlined steps will help you set up a comprehensive monitoring and alerting system, allowing you to address potential issues proactively and maintain a robust security posture.

If you missed something, don't worry! You can automate the process using this awesome Terraform code.