Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · CloudFront

CloudFront.5: Distributions should have logging enabled

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub CloudFront.5 check?

CloudFront.5 checks whether standard (legacy) access logging is enabled on a distribution. It reports FAILED when the `Logging.Enabled` field is false. It only evaluates standard logging to S3 — a distribution streaming real-time logs to Kinesis but with standard logging off still fails.

Why does CloudFront.5 matter?

CloudFront sits at the very edge, so its access logs carry the cleanest record of client behaviour — true viewer IP, exact requested URI, and HTTP status before caching or load balancing obscures the trail. With logging off, a breach investigation can't determine which objects were accessed at all, and the gap maps to NIST AU-2/AU-3/AU-12 and PCI DSS 10.4.2, so an auditor can verify it in seconds.

How do I fix CloudFront.5?

  1. Prepare an S3 log bucket with ACLs enabled (Object Ownership set to Bucket owner preferred) so CloudFront's `awslogsdelivery` group can write, and add a lifecycle rule to bound retention.
  2. Fetch the full config and ETag, set the `Logging` block (Enabled, Bucket, Prefix, IncludeCookies), and push it back with `update-distribution --if-match <ETag>`.
  3. Confirm gzipped log files appear under the prefix and stand up an Athena table so the logs are queryable.
  4. Deploy the `cloudfront-accesslogs-enabled` Config rule so new distributions are caught at creation.

Remediation script · bash

# 1. Lock an S3 origin: only THIS distribution may read the bucket.
cat > bucket-policy.json <<'JSON'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowCloudFrontServicePrincipalReadOnly",
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-downloads-bucket/*",
    "Condition": { "StringEquals": {
      "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/E2QWRUHAPOMQZL"
    } }
  }]
}
JSON
aws s3api put-bucket-policy --bucket my-downloads-bucket --policy file://bucket-policy.json

# 2. Raise the minimum TLS version (the field is nested in ViewerCertificate, so send the whole config back).
aws cloudfront get-distribution-config --id E2QWRUHAPOMQZL > dist.json
ETAG=$(python3 -c "import json;print(json.load(open('dist.json'))['ETag'])")
# ... edit dist.json: ViewerCertificate.MinimumProtocolVersion = TLSv1.2_2021, ViewerProtocolPolicy = redirect-to-https ...
aws cloudfront update-distribution --id E2QWRUHAPOMQZL \
  --distribution-config file://distribution-config.json --if-match "$ETAG"

# 3. Confirm the second door is shut: a raw S3 GET should now return 403.
curl -s -o /dev/null -w '%{http_code}\n' https://my-downloads-bucket.s3.amazonaws.com/file.pdf

Full walkthrough (console steps, edge cases and verification) in the lesson Protect CloudFront distributions and origins.

Is CloudFront.5 a false positive?

Real-time logging to Kinesis does not satisfy CloudFront.5 — the control checks the standard `Logging.Enabled` field only, so distributions with real-time logs still need standard logging enabled to pass.

Part of the learning path Lock down access