Blog

Use custom rules to validate your compliance

03 Feb, 2024
Xebia Background Header Wave

AWS has a lot of controls built in, but what if you need more? AWS Config allows you to create your own rules. These rules can then inspect your resources and determine if they are compliant. This is useful when you want to enforce certain configuration settings. Giving you an overview of how compliant your workloads are.

Let’s use a specific example, but you can apply this concept to other scenarios.

Check S3 access logs configuration

When an engineer creates an Amazon S3 bucket, they need to enable access logging on the bucket. AWS has a built-in config rule for this called s3-bucket-logging-enabled. When you enable the AWS Foundational Security Best Practices v1.0.0 standard in Security Hub it becomes available.

When we create a new account, part of the bootstrapping is the deployment of a bucket. The engineers who use the account only have read rights on this bucket. When an engineer creates a bucket, we expect him to use this bucket for the access logs.

Creating rules: Lambda vs CloudFormation Guard

You can build a custom config rules in 2 ways, using AWS Lambda and CloudFormation Guard. Lambda gives you a lot of flexibility, but it also brings complexity of maintaining. CloudFormation Guard is a bit more lightweight in that regard. Yes, you still need to maintain the logic to determine when your resource is compliant or not. But you need to do this in both cases, thus my go to preference is CloudFormation Guard.

Config Rule

When you start to define your custom config rule you need to think about the scenarios that happen:

  • When the resource is not an S3 bucket, we should skip the rule.
  • When the resource is the actual logging bucket, we should skip the rule.
  • When the resource does not have logging configured, we should fail the rule.
  • When the resource has logging configured on a different bucket then the one we want. We should fail the rule.
  • When the resource has logging configured with the expected bucket, we should pass the rule.

With AWS Config you can define InputParameters. We will use this to pass in the name of the bucket that will store the access logs. We named the parameter loggingBucket. You can reference that value of this parameter using: CONFIG_RULE_PARAMETERS.loggingBucket.

Deployment

I use CloudFormation for the deployment of the rule here is a snippet of the template:

Resources:
  S3AccessLogging:
    Type: AWS::Config::ConfigRule
    Properties:
      ConfigRuleName: lz-s3-access-logging
      Description: Validate that access logging has been enabled and that the correct logging bucket is used.
      EvaluationModes:
        - Mode: DETECTIVE
      InputParameters:
        Fn::Sub: '{"loggingBucket": "s3-access-logs-${AWS::AccountId}-${AWS::Region}"}'
      Scope:
        ComplianceResourceTypes: ["AWS::S3::Bucket"]
      Source:
        Owner: CUSTOM_POLICY
        SourceDetails:
          - EventSource: aws.config
            MessageType: ConfigurationItemChangeNotification
          - EventSource: aws.config
            MessageType: OversizedConfigurationItemChangeNotification
        CustomPolicyDetails:
          EnableDebugLogDelivery: 'true'
          PolicyRuntime: guard-2.x.x
          PolicyText: |-
            rule s3_logging_configuration when resourceType == "AWS::S3::Bucket" resourceName != CONFIG_RULE_PARAMETERS.loggingBucket {
                supplementaryConfiguration.BucketLoggingConfiguration exists
                <<
                Violation: S3 Bucket needs to have access logging configured
                Fix: Configure the destinationBucketName on your S3 bucket
                >>
            }

            rule s3_logging_correct_bucket when s3_logging_configuration {
                supplementaryConfiguration.BucketLoggingConfiguration {
                    destinationBucketName == CONFIG_RULE_PARAMETERS.loggingBucket
                    <<
                    Violation: S3 Bucket needs to have access logging configured
                    Fix: Configure the destinationBucketName on your S3 bucket
                    >>
                }
            }

Let’s walk over this policy and explain what is happening. The first rule only applies when the resource is a S3 bucket and the name is not equal to the name of the logging bucket. The name of the loggingBucket is configurable via the InputParameters. Next, we will check if the bucket has used the logging bucket for the access logs configured. When the logging configuration exists the destinationBucketName needs to match the given name.

You can test the scenarios I mentioned earlier using unit tests. Read: This is how you can test your cfn-guard rules for more information on how you could do that.

After you deployed this rule into your accounts. Config will check all your S3 Buckets if they are compliant with this custom config rule.

Limitations

Currently, AWS Config does only support CloudFormation Guard 2.x.x. I am waiting on the support for 3.x. Since that brings even more powerful capabilities like the json_parse method. This allows you to also check JSON structures like for example the bucket policy.

Conclusion

You can create custom config rules using CloudFormation Guard. Using CloudFormation Guard you don’t have to maintain dependencies from your functions. And you don’t need to upgrade your lambda runtime when it gets deprecated.

Do you want to know how you can distribute your rules? Read my next blog to learn more.

Photo by Pixabay

Joris Conijn
Joris has been working with the AWS cloud since 2009 and focussing on building event driven architectures. While working with the cloud from (almost) the start he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learn new things and experiment with them, because that is the way to learn and grow. In his spare time he enjoys running and runs a small micro brewery from his home.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts