Skip to main content

CloudTrail Bucket Requires MFA Delete

Overview

This check verifies that S3 buckets storing CloudTrail logs have MFA Delete enabled. MFA Delete adds an extra layer of protection by requiring multi-factor authentication to permanently delete object versions or disable versioning on the bucket. This helps ensure that your critical audit logs cannot be tampered with, even if credentials are compromised.

Note: This check only evaluates buckets in the same AWS account as the trail. If your CloudTrail logs are stored in a different account (a common best practice), you should verify MFA Delete separately in that account.

Risk

Without MFA Delete enabled, attackers who gain access to privileged credentials can:

  • Permanently delete CloudTrail log versions, erasing evidence of their activities
  • Disable versioning on the bucket, allowing them to overwrite logs without keeping history
  • Compromise forensic investigations by destroying audit trails
  • Make it impossible to recover deleted log data
  • Cover their tracks after a breach, making incident response much harder

This is particularly dangerous because CloudTrail logs are your primary source of truth for what happened in your AWS account.

Remediation Steps

Prerequisites

You need:

  • Root account access with MFA enabled (MFA Delete can only be enabled by the root user)
  • The name of the S3 bucket storing your CloudTrail logs
  • Your MFA device serial number and a current code
Why root access is required

AWS requires the root account to enable MFA Delete as a security measure. This prevents even highly-privileged IAM users from disabling this protection. You cannot use IAM users, roles, or assume-role to enable MFA Delete - it must be the root account.

To find your MFA device serial number:

  1. Sign in to AWS as the root user
  2. Go to Security Credentials (top right, click your account name)
  3. Under Multi-factor authentication (MFA), find your MFA device ARN (e.g., arn:aws:iam::123456789012:mfa/root-account-mfa-device)

AWS Console Method

MFA Delete cannot be enabled through the AWS Console. You must use the AWS CLI. However, you can use AWS CloudShell (a browser-based CLI) if you don't have the CLI installed locally.

  1. Sign in to AWS as the root user

    • Go to AWS Console
    • Sign in using your root account email and password
    • Complete MFA verification
  2. Open CloudShell

    • In the AWS Console, click the CloudShell icon (terminal icon) in the top navigation bar
    • Wait for the shell to initialize
  3. Find your CloudTrail bucket name

    • Run this command to list your trails and their S3 buckets:
    aws cloudtrail describe-trails --region us-east-1 --query 'trailList[*].{Name:Name,S3BucketName:S3BucketName}'
    • Note the bucket name for the trail you want to protect
  4. Check current versioning status

    aws s3api get-bucket-versioning --bucket <your-bucket-name>
    • If versioning is not already enabled, you'll need to enable it along with MFA Delete
  5. Enable MFA Delete

    • Replace the placeholders and run:
    aws s3api put-bucket-versioning \
    --bucket <your-bucket-name> \
    --versioning-configuration Status=Enabled,MFADelete=Enabled \
    --mfa "arn:aws:iam::<account-id>:mfa/root-account-mfa-device <mfa-code>"
    • <your-bucket-name>: Your CloudTrail S3 bucket name
    • <account-id>: Your 12-digit AWS account ID
    • <mfa-code>: The current 6-digit code from your MFA device

Important: The MFA code expires quickly. Have your MFA device ready and enter the code immediately before running the command.

AWS CLI (optional)

If you prefer to use the CLI from your local machine, follow these steps:

Step 1: Configure root credentials

Temporarily configure the AWS CLI with root credentials. Important: Remove these credentials immediately after use.

aws configure --profile root-temp
# Enter root access key ID
# Enter root secret access key
# Region: us-east-1
# Output format: json

Step 2: Find your CloudTrail bucket

aws cloudtrail describe-trails \
--region us-east-1 \
--profile root-temp \
--query 'trailList[*].{Name:Name,S3BucketName:S3BucketName}'

Step 3: Check current versioning status

aws s3api get-bucket-versioning \
--bucket <your-bucket-name> \
--profile root-temp

Step 4: Enable MFA Delete

aws s3api put-bucket-versioning \
--bucket <your-bucket-name> \
--versioning-configuration Status=Enabled,MFADelete=Enabled \
--mfa "arn:aws:iam::<account-id>:mfa/root-account-mfa-device <mfa-code>" \
--profile root-temp

Step 5: Clean up root credentials

Immediately remove the temporary root credentials:

rm ~/.aws/credentials
# Or remove just the root-temp profile from the credentials file

Security Warning: Never leave root credentials configured on any machine. Create temporary access keys, use them, then delete them from the AWS Console.

CloudFormation (optional)

Important: CloudFormation cannot enable MFA Delete because it requires root credentials with an MFA token. You must enable MFA Delete manually using the CLI as shown above.

However, you can use CloudFormation to ensure versioning is enabled on your CloudTrail bucket:

AWSTemplateFormatVersion: '2010-09-09'
Description: S3 bucket for CloudTrail logs with versioning enabled

Parameters:
BucketName:
Type: String
Description: Name for the CloudTrail S3 bucket

Resources:
CloudTrailBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Ref BucketName
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256

CloudTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref CloudTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSCloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: !GetAtt CloudTrailBucket.Arn
- Sid: AWSCloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: !Sub '${CloudTrailBucket.Arn}/*'
Condition:
StringEquals:
's3:x-amz-acl': bucket-owner-full-control

Outputs:
BucketArn:
Description: CloudTrail bucket ARN
Value: !GetAtt CloudTrailBucket.Arn
BucketName:
Description: CloudTrail bucket name
Value: !Ref CloudTrailBucket

After deploying this template, you must still enable MFA Delete manually using the root account and CLI.

Deploy with:

aws cloudformation deploy \
--template-file cloudtrail-bucket.yaml \
--stack-name cloudtrail-bucket \
--parameter-overrides BucketName=my-cloudtrail-logs-bucket \
--region us-east-1
Terraform (optional)

Important: Terraform cannot enable MFA Delete because it requires root credentials with an MFA token. You must enable MFA Delete manually using the CLI as shown above.

However, you can use Terraform to ensure versioning is enabled on your CloudTrail bucket:

variable "bucket_name" {
description = "Name for the CloudTrail S3 bucket"
type = string
}

data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "cloudtrail" {
bucket = var.bucket_name

# Prevent accidental deletion
lifecycle {
prevent_destroy = true
}
}

resource "aws_s3_bucket_versioning" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
versioning_configuration {
status = "Enabled"
# Note: mfa_delete cannot be set via Terraform - requires root + MFA
}
}

resource "aws_s3_bucket_public_access_block" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_policy" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSCloudTrailAclCheck"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:GetBucketAcl"
Resource = aws_s3_bucket.cloudtrail.arn
},
{
Sid = "AWSCloudTrailWrite"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.cloudtrail.arn}/*"
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}

output "bucket_arn" {
description = "CloudTrail bucket ARN"
value = aws_s3_bucket.cloudtrail.arn
}

output "bucket_name" {
description = "CloudTrail bucket name"
value = aws_s3_bucket.cloudtrail.id
}

output "mfa_delete_reminder" {
description = "Reminder to enable MFA Delete"
value = "MFA Delete must be enabled manually by the root user using: aws s3api put-bucket-versioning --bucket ${var.bucket_name} --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa 'MFA_SERIAL MFA_CODE'"
}

Deploy with:

terraform init
terraform plan -var="bucket_name=my-cloudtrail-logs-bucket"
terraform apply -var="bucket_name=my-cloudtrail-logs-bucket"

After Terraform creates the bucket, enable MFA Delete manually using the root account and CLI.

Verification

After enabling MFA Delete, verify it's working:

  1. Check versioning status in the Console:

    • Go to S3 Console in us-east-1
    • Click on your CloudTrail bucket
    • Go to the Properties tab
    • Under Bucket Versioning, you should see:
      • Bucket Versioning: Enabled
      • MFA delete: Enabled
  2. Re-run the Prowler check:

    prowler aws --checks cloudtrail_bucket_requires_mfa_delete
    • The check should now pass
CLI verification commands

Check the bucket versioning configuration:

aws s3api get-bucket-versioning --bucket <your-bucket-name> --region us-east-1

Expected output when MFA Delete is enabled:

{
"Status": "Enabled",
"MFADelete": "Enabled"
}

List all CloudTrail trails and their buckets to verify all are protected:

aws cloudtrail describe-trails \
--region us-east-1 \
--query 'trailList[*].{TrailName:Name,S3Bucket:S3BucketName}' \
--output table

Then check each bucket's versioning status.

Additional Resources

Notes

  • Root account required: Only the root account can enable MFA Delete. This is by design and cannot be worked around.
  • MFA device needed: You must have an MFA device configured on the root account before you can enable MFA Delete.
  • Cannot disable easily: Once enabled, disabling MFA Delete also requires root credentials and an MFA code. This is intentional protection.
  • Cross-account buckets: If your CloudTrail logs are stored in a separate logging account (recommended best practice), you must enable MFA Delete in that account.
  • No Console support: The AWS Console does not support enabling MFA Delete - you must use the CLI or CloudShell.
  • Versioning required: MFA Delete requires bucket versioning to be enabled. The command enables both together.
  • Organization trails: For AWS Organizations trails, ensure the logging bucket in the management or logging account has MFA Delete enabled.