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:
- Sign in to AWS as the root user
- Go to Security Credentials (top right, click your account name)
- 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.
-
Sign in to AWS as the root user
- Go to AWS Console
- Sign in using your root account email and password
- Complete MFA verification
-
Open CloudShell
- In the AWS Console, click the CloudShell icon (terminal icon) in the top navigation bar
- Wait for the shell to initialize
-
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
-
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
-
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:
-
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
-
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
- AWS Documentation: Using MFA Delete
- AWS Documentation: Enabling Versioning on Buckets
- AWS Documentation: CloudTrail Log File Integrity Validation
- AWS Security Blog: Protecting CloudTrail Logs
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.