Compliance Checking in AWS
Published: 2022-03-13 23:24:48

Storing protected data such as HIPAA in the cloud requires extra attention to ensure the data is not compromised. Using the [NIST 800-171 framework](https://csrc.nist.gov/publications/detail/sp/800-171/rev-2/final) provides guidance for controls that can help facilitate the protection of HIPAA and other protected data sets. [AWS Audit Manager](https://docs.aws.amazon.com/audit-manager/latest/userguide/NIST-800-171-r2-1.1.html) is a good starting point for auditing your environment against NIST 800-171 but I wanted to learn more about NIST and building controls in AWS to protect data. To do this, I wanted to use AWS Config service to monitor for compliance for controls and to use AWS Simple Notification Service to send alerts to via email and Slack. To accomplish this, AWS provides [operational best practices for using AWS Config service](https://docs.aws.amazon.com/config/latest/developerguide/operational-best-practices-for-nist_800-171.html) to continuous monitor many NIST 800-171 controls. From this, I pulled controls that are applicable for services being utilized to host and analyze data in AWS.

## AWS Config Service

AWS Config provides services to assess and audit configurations for AWS resources. We all have read articles about S3 buckets being misconfigured and exposing data to the public. AWS Config can monitor for buckets that are configured for public access. If bucket should be misconfigured or changed to allow public access, AWS Config will report this compliance violation and you can set up remediation to automatically change the settings back.

Lets build out Cloudfomation template that checks for compliance for blocking public access and for EBS volumes that are not encrypted.

**Check for the S3 bucket public access**

```yaml
# S3 Compliance checks

  ConfigRuleBucketReadProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-public-read-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "A Config rule that checks that your Amazon S3 buckets do not allow public read access. If an Amazon S3 bucket policy or bucket ACL allows public read access, the bucket is noncompliant."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_PUBLIC_READ_PROHIBITED"
  ConfigRuleBucketWriteProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-public-write-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "A Config rule that checks that your Amazon S3 buckets do not allow public write access. If an Amazon S3 bucket policy or bucket ACL allows public write access, the bucket is noncompliant."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
  ConfigRuleS3PublicAccess:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-account-level-public-access-blocks"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::AccountPublicAccessBlock"
      Description: "A Config rule that checks whether the required public access block settings are configured from account level. The rule is only NON_COMPLIANT when the fields set below do not match the corresponding fields in the configuration item."
      InputParameters:
        IgnorePublicAcls: "True"
        BlockPublicPolicy: "True"
        BlockPublicAcls: "True"
        RestrictPublicBuckets: "True"
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS"
  ConfigRuleBucketPublicAccessProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-level-public-access-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "Checks if Amazon Simple Storage Service (Amazon S3) buckets are publicly accessible. This rule is NON_COMPLIANT if an Amazon S3 bucket is not listed in the excludedPublicBuckets parameter and bucket level settings are public."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED"
```

**Check that EBS Volumes are encrypted**

```yaml
#EC2 and EBS Compliance Checks
  ConfigRuleEncryptedVolumes:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "encrypted-volumes"
      Scope:
        ComplianceResourceTypes:
          - "AWS::EC2::Volume"
      Description: "A Config rule that checks whether the EBS volumes that are in an attached state are encrypted."
      Source:
        Owner: "AWS"
        SourceIdentifier: "ENCRYPTED_VOLUMES"
```

## Notification for compliance changes

Now that we have some compliance checking rules, we’ll want to notify us when resources are out of compliance. To do this, we will use a combination of AWS [EventBridge](https://aws.amazon.com/eventbridge/) and [Simple Notification Service](https://aws.amazon.com/sns).

EventBridge is a serverless event bus that we will use to detect changes to AWS Config. To get started, we will create an event rule to detect when a compliance changes. Portion of the cloud formation template is below and the particular section to note is the EventPattern.

```yaml
  EventRule:
    Type: "AWS::Events::Rule"
    Properties:
      Name: "detect-config-changes"
      Description: "A CloudWatch Event Rule that detects changes to AWS Config and publishes change events to an SNS topic for notification."
      State: "ENABLED"
      Targets:
        - Arn:
            Ref: "SnsTopic"
          Id: "target-id1"
      EventPattern:
        detail-type:
          - "Config Rules Compliance Change"
        source:
          - "aws.config"
```

Next, we will want to build out the SNS Topic to send us an email to alert us of compliance changes. The SNS Topic section creates the subscription, where as, the TopicPolicy section provides permissions to publish the notification.

```yaml
  SnsTopic:
    Type: "AWS::SNS::Topic"
    Properties:
      Subscription:
        - Endpoint: "aws-notifications-list@example.com"
          Protocol: "email"
      TopicName: "sns-topic"
  TopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Id: HDWTopicPolicy
        Statement:
          - Sid: AllowServices
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: 'sns:Publish'
            Resource:
              - !Ref SnsTopic
```

## Putting this all together

The final CloudFormation templates are below. The first stack creates the AWS Config rules and the other creates the EventBridge and SNS notifications. 

**AWS Config CloudFormation Template**

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "AWS Config Checks"
Resources:
#EC2 and EBS Compliance Checks
  ConfigRuleEncryptedVolumes:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "encrypted-volumes"
      Scope:
        ComplianceResourceTypes:
          - "AWS::EC2::Volume"
      Description: "A Config rule that checks whether the EBS volumes that are in an attached state are encrypted."
      Source:
        Owner: "AWS"
        SourceIdentifier: "ENCRYPTED_VOLUMES"
# S3 Compliance checks
  ConfigRuleBucketReadProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-public-read-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "A Config rule that checks that your Amazon S3 buckets do not allow public read access. If an Amazon S3 bucket policy or bucket ACL allows public read access, the bucket is noncompliant."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_PUBLIC_READ_PROHIBITED"
  ConfigRuleBucketWriteProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-public-write-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "A Config rule that checks that your Amazon S3 buckets do not allow public write access. If an Amazon S3 bucket policy or bucket ACL allows public write access, the bucket is noncompliant."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
  ConfigRuleS3PublicAccess:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-account-level-public-access-blocks"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::AccountPublicAccessBlock"
      Description: "A Config rule that checks whether the required public access block settings are configured from account level. The rule is only NON_COMPLIANT when the fields set below do not match the corresponding fields in the configuration item."
      InputParameters:
        IgnorePublicAcls: "True"
        BlockPublicPolicy: "True"
        BlockPublicAcls: "True"
        RestrictPublicBuckets: "True"
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS"
  ConfigRuleBucketPublicAccessProhibited:
    Type: "AWS::Config::ConfigRule"
    Properties:
      ConfigRuleName: "s3-bucket-level-public-access-prohibited"
      Scope:
        ComplianceResourceTypes:
          - "AWS::S3::Bucket"
      Description: "Checks if Amazon Simple Storage Service (Amazon S3) buckets are publicly accessible. This rule is NON_COMPLIANT if an Amazon S3 bucket is not listed in the excludedPublicBuckets parameter and bucket level settings are public."
      Source:
        Owner: "AWS"
        SourceIdentifier: "S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED"
Parameters: {}
Metadata: {}
Conditions: {}
```

**EventBridge and SNS Notifications**

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create SNS topic to send alerts and notifications to aws-notifications-list@example.com"
# Create SNSTopic to send email to aws-notifications-list@example.com
Resources:
  SnsTopic:
    Type: "AWS::SNS::Topic"
    Properties:
      Subscription:
        - Endpoint: "aws-notifications-list@example.com"
          Protocol: "email"
      TopicName: "sns-topic"
  TopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Id: HDWTopicPolicy
        Statement:
          - Sid: AllowServices
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: 'sns:Publish'
            Resource:
              - !Ref SnsTopic
      Topics:
        - !Ref SnsTopic
# Create CloudWatch Event Rule to notify of config changes
  EventRule:
    Type: "AWS::Events::Rule"
    Properties:
      Name: "detect-config-changes"
      Description: "A CloudWatch Event Rule that detects changes to AWS Config and publishes change events to an SNS topic for notification."
      State: "ENABLED"
      Targets:
        - Arn:
            Ref: "SnsTopic"
          Id: "target-id1"
      EventPattern:
        detail-type:
          - "Config Rules Compliance Change"
        source:
          - "aws.config"
Parameters: {}
Metadata: {}
Conditions: {}
```

## What’s next?

There are two additional items I’ll be looking to expand on. One is to add functionality for SNS to post the event in a Slack Channel when a resource comes out of compliance. Lastly, I’ll look to clean up JSON output to be more readable for the notifications.