Using CloudWatch to Secure Your AWS Account

Using CloudWatch to Secure Your AWS Account

AWS IAM Defense in Depth

In my post "AWS Security Assessments - Getting Access", I described how to use a CloudFormation Quick Create-Link to easily setup Cross-Account Trusts to access customer accounts when performing an AWS Security Assessment. Following that post a colleague posed the question if cross-account trusts could be used for phishing to gain access to an account. The short answer to that question is YES!!

However keeping in mind a defense-in-depth strategy and approach to IAM security in AWS, you should follow least privilege access in IAM, periodically review the IAM Credential Report, and turn on IAM Access Analyzer. Another step in the defense in depth approach to securing your account is to use CloudFormation to create a CloudWatch Alarm to respond to CloudTrail API IAM Calls. What that means in simple terms is you'll get an email alert anytime changes are made to IAM users including add, delete, or modify operations.

The power of Infrastructure as Code (IaC) tools like CloudFormation or Terraform comes from its ability to allow you to quickly provision deployments to your infrastructure in an error free repeatable process. This of course is particularly useful when you're working to secure your AWS environment.


How Does it Work?

It's important to note that some of these steps are optional depending on what controls you already have enabled and you can modify your CloudFormation script as needed. Here's what we'll be doing in a nutshell.

  • Create a CloudTrail Trail if one doesn't already exist in your account. CloudTrail provides event history of your AWS account activity, including actions taken through the AWS Management Console, AWS SDKs, command line tools, and other AWS services, and coincidentally should be one of the first things you enable in your account
  • Create an S3 bucket that your Trail will write to.
  • Create a CloudWatch Log Group that your Trail will write to.
  • Create an SNS topic that will forward your alarm to the specified email address.
  • Create your CloudWatch Alarm and apply the Metrics to watch for.

Here's the CloudFormation template written in YAML that you'll want to deploy. You can also update it as needed to include any additional IAM Actions you want to monitor from the full AWS IAM API Reference List.

AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudWatch Alarm that detects changes to IAM users and
groups and publishes change events to an SNS topic for notification.'

Resources:

  CloudTrail:
    Type: 'AWS::CloudTrail::Trail'
    Properties:
      TrailName: ManagementEventsTrail
      IsLogging: true
      EnableLogFileValidation: false
      EventSelectors:
        - IncludeManagementEvents: true
          ReadWriteType: All
      IsMultiRegionTrail: true
      IncludeGlobalServiceEvents: true
      S3BucketName:
        Ref: S3BucketForCloudTrail
      CloudWatchLogsLogGroupArn:
        'Fn::GetAtt':
          - CloudWatchLogGroup
          - Arn
      CloudWatchLogsRoleArn:
        'Fn::GetAtt':
          - IamRoleForCwLogs
          - Arn
    DependsOn: S3BucketPolicy

  S3BucketForCloudTrail:
    Type: 'AWS::S3::Bucket'
    Properties:
      # <CHANGE BELOW AS NEEDED>
      BucketName: management-events-trail-bucket-name

  S3BucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket:
        Ref: S3BucketForCloudTrail
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: AWSCloudTrailBucketPermissionsCheck
            Effect: Allow
            Principal:
              Service:
                - cloudtrail.amazonaws.com
            Action: 's3:GetBucketAcl'
            Resource:
              'Fn::GetAtt':
                - S3BucketForCloudTrail
                - Arn
          - Sid: ' AWSConfigBucketDelivery'
            Effect: Allow
            Principal:
              Service:
                - cloudtrail.amazonaws.com
            Action: 's3:PutObject'
            Resource:
              'Fn::Join':
                - ''
                - - 'Fn::GetAtt':
                      - S3BucketForCloudTrail
                      - Arn
                  - /AWSLogs/*
            Condition:
              StringEquals:
                's3:x-amz-acl': bucket-owner-full-control

  CloudWatchLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      # <CHANGE BELOW AS NEEDED>
      LogGroupName: CloudTrailLogs

  IamRoleForCwLogs:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: allow-access-to-cw-logs
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: '*'
      RoleName: CloudTrailLogs-to-CloudWatch

  SnsTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      Subscription:
        # <CHANGE BELOW AS NEEDED>
        - Endpoint: example@example.com
          Protocol: email
      TopicName: alarm-action

  CloudWatchAlarm:
    Type: 'AWS::CloudWatch::Alarm'
    Properties:
      AlarmName: iam_user_changes
      AlarmDescription: >-
        A CloudWatch Alarm that triggers when changes are made to IAM users.
      MetricName: IAMPolicyEventCount
      Namespace: CloudTrailMetrics
      Statistic: Sum
      Period: '300'
      EvaluationPeriods: '1'
      Threshold: '1'
      ComparisonOperator: GreaterThanOrEqualToThreshold
      AlarmActions:
        - Ref: SnsTopic
      TreatMissingData: notBreaching


  MetricFilter:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      # <CHANGE BELOW AS NEEDED>
      LogGroupName: CloudTrailLogs
      # <CHANGE BELOW AS NEEDED>
      FilterPattern: >-
        {($.eventName=AddUserToGroup)||($.eventName=AttachGroupPolicy)||($.eventName=AttachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=ChangePassword)||($.eventName=CreateAccessKey)||($.eventName=CreatePolicy)||($.eventName=CreateRole)||($.eventName=CreateUser)||($.eventName=CreateVirtualMFADevice)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=UpdateAccessKey)||($.eventName=UpdateGroup)||($.eventName=UpdateRole)||($.eventName=UpdateUser)}
      MetricTransformations:
        - MetricValue: '1'
          MetricNamespace: CloudTrailMetrics
          MetricName: IAMPolicyEventCount
    DependsOn: CloudWatchLogGroup
Parameters: {}
Metadata: {}
Conditions: {}


Create the Stack

Now that our code is modified, its time to deploy it to CloudFormation. Start by creating a new Stack.

image.png


Browse to your template

image.png


Give it a name and create your stack

image.png


Confirm Your Subscription

You'll need check your email to confirm your SNS Subscription. image.png


Take it for a Test Drive

Now all you need to do is modify an IAM user to make sure it works. It's also important to note that CloudWatch Alarms aren't instantaneous and there is a delay associated with them. In fact one if the controls we specified in the script was Period: '300' which means our CloudWatch Alarm will check every 5 minutes. If everything worked as planned you should have received an alert like this.

image.png