S3 TLS enforcement using Terraform

As a Cloud Engineer, when you start working for big Enterprise companies with strict security policies, one of the most common questions you might face is enforcing S3 TLS (HTTPS) connections to the AWS S3 bucket. This is a common requirement that might get from security policies enforcement engines like OPA, for example.

This article demonstrates how to enforce S3 TLS (HTTPS) and TLS version security policies for Amazon S3 buckets using Terraform.

What are AWS S3 bucket policies?

To specify requirements, conditions, or restrictions for access to AWS S3 Bucket, you must use AWS S3 Bucket Policies (JSON-based rules that help grant or restrict permissions to your Amazon S3 resources).

Here are some useful examples of MFA policies, IP-address pool restrictions, and restricting access to a specific HTTP referer: AWS S3 Bucket policy examples.

enforcing S3 TLS (HTTPS) Policy

First of all, to enforce TLS (HTTPS) connections to the AWS S3 Bucket, you have to create an AWS S3 Bucket Policy statement that contains a condition that checks every API call to the S3 bucket is coming through an encrypted TLS (HTTPS) connection. AWS provides us with the aws:SecureTransport boolean condition, which is set to true if the API call is coming through an encrypted connection (HTTPS) and set to false if the API call came from an unencrypted connection (HTTP).

Terraform - How to enforce TLS (HTTPS) for AWS S3 Bucket - Diagram

Now, to enforce TLS (HTTPS) connections to the AWS S3 bucket, you have to add a Deny statement in yourAWS S3 Bucket Policy for all (s3:*) API calls if the aws:SecureTransport the condition has not been met (false):

"Condition": {
    "Bool": {
        "aws:SecureTransport": "false"
    }
}

If you’d like to go further and enforce a version of the TLS protocol, you can do it by adding a condition to theAWS S3 Bucket Policy:

"Condition": {
    "NumericLessThan": {
        "s3:TlsVersion": 1.2
    }
}

Here, we’re usingNumericLessThan condition to make sure that the TLS (HTTPS) version (s3:TlsVersion) used to make an API call to the S3 bucket is greater than or equals 1.2 (pay attention that we’re checking for an opposite condition).

Possible condition values for numeric types are:

  • NumericEquals (==)
  • NumericGreaterThan (>)
  • NumericGreaterThanEquals (>=)
  • NumericLessThan (<)
  • NumericLessThanEquals (<=)

The final security policy (thanks Rafał Pawłaszek for pointing me to the “Creating a condition with multiple keys or values“) for yourAWS S3 Bucket that enforces TLS (HTTPS) protocol to version 1.2 and higher should have two statements in place:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR_S3_BUCKET_NAME/*",
                "arn:aws:s3:::YOUR_S3_BUCKET_NAME"
            ],
            "Effect": "Deny",
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            }
        },
        {
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR_S3_BUCKET_NAME/*",
                "arn:aws:s3:::YOUR_S3_BUCKET_NAME"
            ],
            "Effect": "Deny",
            "Condition": {
                "NumericLessThan": {
                    "s3:TlsVersion": 1.2
                }
            }
        }
    ]
}

Setting up the S3 Bucket Policy manually

To manually set up the AWS S3 Bucket Policy for your S3 bucket, you have to open the S3 service in the Web console:

AWS Web Console - Open S3 Service

Select your S3 Bucket from the list:

AWS Web Console - Select S3 Bucket

Go to the Permissions tab:

AWS Web Console - Select S3 Bucket Permission

Scroll the page down to Bucket Policy and hit the Edit button:

AWS Web Console - Edit S3 Bucket Policy

Paste the S3 Bucket Policy to the Policy input field:

AWS Web Console - Edit S3 Bucket Policy - Enforce TLS (HTTPS) for API calls

Do not forget to change the S3 Bucket ARNs in the provided template and hit the “Save changes” button.

Setting up the S3 Bucket Policy using Terraform

To enforce TLS (HTTPS) protocol for AWS S3 Bucket, you have to use s3_bucket_policy resource:

provider "aws" {
  region = "us-east-1"
}
resource "aws_s3_bucket" "bucket" {
    bucket = "my-tls-enforced-bucket"
    server_side_encryption_configuration {
      rule {
        apply_server_side_encryption_by_default {
          sse_algorithm = "AES256"
        }
      }
    }
}
resource "aws_s3_bucket_policy" "bucket" {
    bucket = aws_s3_bucket.bucket.id
    policy = jsonencode({
        Version = "2012-10-17"
        Id      = "BUCKET-POLICY"
        Statement = [
            {
                Sid       = "EnforceTls"
                Effect    = "Deny"
                Principal = "*"
                Action    = "s3:*"
                Resource = [
                    "${aws_s3_bucket.bucket.arn}/*",
                    "${aws_s3_bucket.bucket.arn}",
                ]
                Condition = {
                    Bool = {
                        "aws:SecureTransport" = "false"
                    }
                }
            },
            {
                Sid       = "EnforceProtoVer"
                Effect    = "Deny"
                Principal = "*"
                Action    = "s3:*"
                Resource = [
                    "${aws_s3_bucket.bucket.arn}/*",
                    "${aws_s3_bucket.bucket.arn}",
                ]
                Condition = {
                    NumericLessThan = {
                        "s3:TlsVersion": 1.2
                    }
                }
            }
        ]
    })
}

The Terraform code above will create an SSE-encrypted S3 bucket with enforced TLS protocol requirements.

Summary

This article demonstrated how to enforce TLS (HTTPS) and enforce requirements for the TLS version for the AWS S3 bucket using Terraform.