Skip to main content
The module distinguishes between two separate concepts:
  • Encryption at rest (server_side_encryption_configuration) — sets the default encryption applied to objects when no encryption header is supplied by the caller.
  • Encryption enforcement policies — IAM bucket policies that deny PutObject requests that do not meet your encryption requirements.
Default encryption alone does not prevent a caller from specifying a different encryption method or no encryption header at all. Enforcement policies close that gap by actively rejecting non-compliant uploads.

Enforcement variables

VariableDefaultDescription
attach_deny_incorrect_encryption_headersfalseDenies PutObject when the s3:x-amz-server-side-encryption header does not match the algorithm configured in server_side_encryption_configuration.
attach_deny_incorrect_kms_key_ssefalseDenies PutObject when the KMS key specified in the request does not match allowed_kms_key_arn.
allowed_kms_key_arnnullThe ARN of the KMS key that must be used in PutObject requests. Required when attach_deny_incorrect_kms_key_sse = true.
attach_deny_unencrypted_object_uploadsfalseDenies PutObject when no s3:x-amz-server-side-encryption header is present in the request.
attach_deny_ssec_encrypted_object_uploadsfalseDenies PutObject when a customer-provided encryption key (SSEC) header is detected.

Policy conditions from source

Each enforcement policy is implemented as an IAM Deny statement targeting s3:PutObject. The following shows the exact conditions used in main.tf.

Deny incorrect encryption header

Checks that s3:x-amz-server-side-encryption matches the configured algorithm. When sse_algorithm = "aws:kms", only aws:kms is allowed; otherwise only AES256 is allowed.
data "aws_iam_policy_document" "deny_incorrect_encryption_headers" {
  statement {
    sid    = "denyIncorrectEncryptionHeaders"
    effect = "Deny"

    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.this[0].arn}/*"]

    principals {
      identifiers = ["*"]
      type        = "*"
    }

    condition {
      test     = "StringNotEquals"
      variable = "s3:x-amz-server-side-encryption"
      values   = try(var.server_side_encryption_configuration.rule.apply_server_side_encryption_by_default.sse_algorithm, null) == "aws:kms" ? ["aws:kms"] : ["AES256"]
    }
  }
}

Deny incorrect KMS key

Checks that the KMS key ID in the request matches allowed_kms_key_arn:
data "aws_iam_policy_document" "deny_incorrect_kms_key_sse" {
  statement {
    sid    = "denyIncorrectKmsKeySse"
    effect = "Deny"

    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.this[0].arn}/*"]

    principals {
      identifiers = ["*"]
      type        = "*"
    }

    condition {
      test     = "StringNotEquals"
      variable = "s3:x-amz-server-side-encryption-aws-kms-key-id"
      values   = [try(var.allowed_kms_key_arn, null)]
    }
  }
}

Deny unencrypted object uploads

Checks that the s3:x-amz-server-side-encryption header is not null (i.e., it must be present):
data "aws_iam_policy_document" "deny_unencrypted_object_uploads" {
  statement {
    sid    = "denyUnencryptedObjectUploads"
    effect = "Deny"

    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.this[0].arn}/*"]

    principals {
      identifiers = ["*"]
      type        = "*"
    }

    condition {
      test     = "Null"
      variable = "s3:x-amz-server-side-encryption"
      values   = [true]
    }
  }
}

Deny SSEC-encrypted object uploads

Checks that the s3:x-amz-server-side-encryption-customer-algorithm header is null (i.e., SSEC must not be used). Note the condition value is false — the header must be absent:
data "aws_iam_policy_document" "deny_ssec_encrypted_object_uploads" {
  statement {
    sid    = "denySSECEncryptedObjectUploads"
    effect = "Deny"

    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.this[0].arn}/*"]

    principals {
      identifiers = ["*"]
      type        = "*"
    }

    condition {
      test     = "Null"
      variable = "s3:x-amz-server-side-encryption-customer-algorithm"
      values   = [false]
    }
  }
}
The Null condition with values = [false] means “the key is NOT null” — i.e., the header IS present. This denies any PutObject where the customer-provided encryption header is set, effectively banning SSEC.

Complete example

The following example configures a bucket with KMS encryption at rest and all relevant enforcement policies enabled:
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"

  bucket = "my-encrypted-bucket"

  server_side_encryption_configuration = {
    rule = {
      apply_server_side_encryption_by_default = {
        sse_algorithm     = "aws:kms"
        kms_master_key_id = aws_kms_key.s3.arn
      }
    }
  }

  attach_deny_incorrect_encryption_headers = true
  attach_deny_incorrect_kms_key_sse        = true
  allowed_kms_key_arn                      = aws_kms_key.s3.arn
  attach_deny_unencrypted_object_uploads   = true
}
When using attach_deny_incorrect_kms_key_sse, you must set allowed_kms_key_arn to the ARN of the KMS key that callers are permitted to use. If allowed_kms_key_arn is null, the policy will deny all KMS-encrypted uploads.

Choosing which policies to apply

Deny unencrypted uploads

Use attach_deny_unencrypted_object_uploads = true as a baseline to ensure every object is encrypted, regardless of algorithm.

Enforce a specific algorithm

Combine with attach_deny_incorrect_encryption_headers = true and configure server_side_encryption_configuration so the policy knows which algorithm to require.

Enforce a specific KMS key

Add attach_deny_incorrect_kms_key_sse = true and set allowed_kms_key_arn when you need to ensure objects are encrypted with a specific customer-managed key.

Prohibit SSEC

Use attach_deny_ssec_encrypted_object_uploads = true to prevent callers from supplying their own encryption keys, keeping key management centralised in AWS KMS.