Ravindra BagaleCourses & study guides मराठी Track your progress

S3 Bucket Policy Project 7: Cross-Account ALB and CloudTrail Logs Bucket

Central logging account owned by Sneha Rao in Hyderabad must accept ALB access logs and CloudTrail from workload accounts. That needs a bucket policy with ELB and CloudTrail service principals (and sometimes ACL bucket-owner-full-control on Put).

What you'll learn in this chapter

  • Why log buckets are cross-account by design
  • What ELB and CloudTrail principals look like in JSON
  • How to apply policy, enable logging, see objects appear
  • ACL vs policy for log delivery
  • Interview answers

1. Everyday project story

Mumbai ecommerce platform (CTO: Harshad Joshi) and London retail (SRE: Claire Thompson) both ship ALB logs into a security account bucket owned by Sneha Rao.

2. Why cross-account log bucket?

Why do we need this?

Workloads must not delete their own audit trail. A locked logging account owns the bucket; services in other accounts need explicit resource-policy Allow.

  • Separate security/logging account
  • Service principals for log delivery
  • Optional SourceAccount conditions
  • Block human deletes with extra Deny (project 9)

3. What is the policy?

What is it?

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowALBLogDelivery",
      "Effect": "Allow",
      "Principal": { "Service": "logdelivery.elasticloadbalancing.amazonaws.com" },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::rb-central-logs-ap-south-1/alb/AWSLogs/444455556666/*",
      "Condition": {
        "StringEquals": { "aws:SourceAccount": "444455556666" }
      }
    },
    {
      "Sid": "AllowCloudTrailPut",
      "Effect": "Allow",
      "Principal": { "Service": "cloudtrail.amazonaws.com" },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::rb-central-logs-ap-south-1/cloudtrail/AWSLogs/444455556666/*",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "444455556666",
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    },
    {
      "Sid": "AllowCloudTrailAclCheck",
      "Effect": "Allow",
      "Principal": { "Service": "cloudtrail.amazonaws.com" },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::rb-central-logs-ap-south-1"
    }
  ]
}

Line-by-line

  1. ALB principal logdelivery.elasticloadbalancing.amazonaws.com.
  2. Resource prefix includes AWSLogs/SOURCE_ACCOUNT/.
  3. aws:SourceAccount condition — only that workload account.
  4. CloudTrail PutObject + bucket-owner-full-control ACL condition (classic).
  5. GetBucketAcl for CloudTrail validation.
  6. Replace account ids and prefixes before apply.

4. CLI with output

How to do it (step by step)

aws s3api put-bucket-policy --bucket rb-central-logs-ap-south-1 --policy file://logs.json
# (empty success)

# On workload account: enable ALB access logs to s3://rb-central-logs-ap-south-1/alb
# Enable CloudTrail to s3://rb-central-logs-ap-south-1/cloudtrail

aws s3 ls s3://rb-central-logs-ap-south-1/alb/AWSLogs/444455556666/ --recursive | head
# AWSLogs/444455556666/elasticloadbalancing/ap-south-1/...

5. Test allow and deny

# ALLOW — after enabling ALB logs, objects appear under alb/AWSLogs/...
aws s3 ls s3://rb-central-logs-ap-south-1/alb/AWSLogs/444455556666/ | head
# PRE elasticloadbalancing/

# DENY — random IAM user from another account without grant
aws s3 cp s3://rb-central-logs-ap-south-1/alb/secret.log - --profile other
# AccessDenied

6. ACL vs policy

Modern ALB delivery often works with policy alone; CloudTrail historically expects bucket-owner-full-control on Put. Prefer Bucket owner enforced when the service supports it — read current AWS docs per service.

7. Multi-industry

Ecommerce ALB (Mumbai — Harshad Joshi), retail (London — Claire Thompson), bank org trail (Hyderabad — Sneha Rao), hospital API logs (Sydney — Liam O’Connor).

8. AccessDenied debugging and Policy Simulator

When S3 returns AccessDenied, check in order: (1) Block Public Access, (2) matching bucket policy Deny, (3) missing IAM Allow, (4) KMS key policy, (5) VPC endpoint policy, (6) legacy ACL. IAM Policy Simulator can miss BPA/VPC/KMS — always re-test with real CLI from the real network path.

# Decode
aws s3api get-bucket-policy --bucket BUCKET --query Policy --output text | python3 -m json.tool
aws s3api get-public-access-block --bucket BUCKET
aws iam simulate-principal-policy --policy-source-arn ARN \
  --action-names s3:GetObject --resource-arns arn:aws:s3:::BUCKET/key.txt
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject --max-items 5

9. Lab: cross-account log bucket

Do this in ap-south-1 (Mumbai) unless your org forbids it. Set a budget alert first.

How to do it (step by step)

  1. In logging account create bucket + policy with your workload account id.
  2. From workload account enable ALB access logs to the prefix.
  3. Wait and ls for AWSLogs objects (ALLOW proof).
  4. Try GetObject as unrelated user → AccessDenied.

Cleanup (stop billing)

  1. Disable logging on ALB/Trail; empty prefixes; delete policy/bucket.
  2. Remove lab trail if created.

10. Common errors and fixes

Each row is a real mistake students and juniors hit. Read the fix before you panic.

Error / symptomFix
AccessDenied but IAM Allow looks fineFind explicit Deny in bucket policy or BPA blocking public.
Policy JSON rejected on putValidate with python3 -m json.tool; no smart quotes; use file://.
Policy Simulator Allow, CLI DenySimulator skips some layers; retest with real CLI/role/network.
Wrong Resource ARN shapeGetObject needs arn:...:bucket/* ; ListBucket needs bare bucket ARN.
Condition key typo or wrong caseKeys like aws:SourceIp are case-sensitive.
Missing Principal on bucket policyResource-based policies require Principal; identity policies must omit it.
Account-level BPA overrides bucketAccount Block Public Access can still block public grants.
Encrypted object / KMS forgottenSSE-KMS needs key policy + kms:Decrypt/GenerateDataKey.
Logs never appearWrong prefix/account id; missing GetBucketAcl; BPA issues.
AccessDenied on enablePolicy Principal/Service spelling or Resource path.

11. Troubleshooting checklist

  1. Confirm region and endpoint hostname for the bucket.
  2. Print get-bucket-policy and get-public-access-block.
  3. Reproduce with a least-privilege lab IAM user.
  4. Binary-search Denies by commenting one Sid at a time in lab.
  5. Read CloudTrail errorCode and additionalEventData.
  6. If KMS involved, test key permissions separately.
  7. From VPC path: route to gateway prefix list + endpoint policy.
  8. Cleanup versioning/Object Lock labs carefully.

12. FAQ

Q. Is a bucket policy the same as an IAM policy?

No. Bucket policy is resource-based on the bucket; IAM is identity-based. For same-account, either can Allow; explicit Deny wins; cross-account needs both.

Q. Do I still need ACLs in 2026?

Prefer Bucket owner enforced (ACLs off) + bucket policy. Some log deliveries still need canned ACL on Put.

Q. What is Block Public Access?

Switches that block public ACLs/policies at account or bucket. Disable only the minimum for a deliberate public design.

Q. How do you debug AccessDenied fast?

BPA → bucket policy → IAM → KMS → VPCe → CloudTrail. Never “fix” with Action *.

Q. Why SourceAccount?

Stops other accounts from writing into your log prefixes.

Q. Same for Config/ELB?

Each service has its own principal — check docs.

Q. SSE-KMS on log bucket?

Key policy must allow the service — extra care.

Q. Org trail vs account trail?

Org trail centralizes; bucket still needs policy.

13. Interview Q&A

Q. Who writes ALB logs?

ELB log delivery service principal into your bucket.

Q. CloudTrail ACL condition why?

Classic delivery uses bucket-owner-full-control so owner can read.

Q. How prove allow?

Objects land under AWSLogs/account/.

Q. How prove deny?

Unrelated principal AccessDenied on Get/Put.

Q. Why separate logging account?

Attackers in workload account cannot easily wipe logs.

Q. Prefix per account?

Yes — AWSLogs/ACCOUNT_ID/ convention.

14. Tips in English, Marathi and Hindi

Ravindra Bagale's Tip (EN)

Never give workload admins s3:DeleteObject on the central log bucket.

Ravindra Bagale's Tip (MR)

Workload admin ला central log DeleteObject देऊ नका.

Tip (HI)

Workload admin को central log पर DeleteObject न दें।

Try at home

  1. Enable one ALB log path; screenshot ls output.
  2. Remove SourceAccount; explain risk; put it back.
  3. Sketch org logging landing zone on paper.

In short

  1. Log buckets use service principals + SourceAccount.
  2. Prefixes follow AWSLogs/account.
  3. CloudTrail may need ACL condition.
  4. Prove with objects appearing + stranger denied.
  5. Keep humans least-privilege on log buckets.

15. Policy design checklist before you save

  1. Write the business rule in one sentence (who can do what from where).
  2. Choose Allow vs Deny-unless consciously; remember explicit Deny wins.
  3. List every Resource ARN shape you need (bucket vs bucket/*).
  4. Name every Condition key exactly (case-sensitive).
  5. Decide BPA settings before put-bucket-policy.
  6. Plan the allow test and the deny test with expected HTTP/CLI codes.
  7. Plan cleanup (versioning, Object Lock, KMS schedules).
  8. Paste JSON through python3 -m json.tool before apply.

16. Policy Simulator walkthrough

IAM Policy Simulator is great for identity policies and a first pass on resource policies, but it can miss Block Public Access, VPC endpoint policies, and some KMS denials. Use it to catch typos, then always re-test with AWS CLI from the real network path (office IP, VPC, or CloudFront).

  1. Open IAM → Policy simulator → pick the user/role.
  2. Add action s3:GetObject / PutObject and the object ARN.
  3. If you use resource policies, include the bucket policy text where the tool allows.
  4. Note Allow/Deny; then run the matching CLI from the real path.
  5. If Simulator Allow but CLI Deny, check BPA, VPCe, KMS, and explicit Denies next.

17. Copy-paste runbook (print this)

# 0) Region + names
export AWS_REGION=ap-south-1
export BUCKET=rb-lab-$(date +%s)-ap-south-1

# 1) Create + show BPA
aws s3api create-bucket --bucket $BUCKET --region $AWS_REGION \
  --create-bucket-configuration LocationConstraint=$AWS_REGION
aws s3api get-public-access-block --bucket $BUCKET || true

# 2) Validate + apply policy
python3 -m json.tool policy.json >/dev/null
aws s3api put-bucket-policy --bucket $BUCKET --policy file://policy.json
aws s3api get-bucket-policy --bucket $BUCKET --query Policy --output text | python3 -m json.tool | head

# 3) Allow test + Deny test (fill per project)
# ... project-specific curls / aws s3 cp ...

# 4) Cleanup
aws s3 rm s3://$BUCKET --recursive || true
aws s3api delete-bucket-policy --bucket $BUCKET || true
aws s3api delete-bucket --bucket $BUCKET

18. Industry story bank (reuse the pattern)

Each project above uses one industry story. Use the same policy pattern in these other industries without rewriting the JSON from scratch — only change the bucket name, principal, and Condition values.

  • Healthcare (Priya Nair, Apollo-style clinic): patient PDFs — prefer SSE-KMS + OrgID + MFA delete.
  • Fintech (Arjun Mehta, UPI wallet): statements — office IP + SecureTransport + CloudFront OAC for the customer portal.
  • EdTech (Sofia Alvarez, online course): course videos — CloudFront OAC only; never public ACLs.
  • Logistics (Kenji Watanabe, fleet app): GPS dumps — VPC endpoint only from the private subnet runners.
  • Media (Fatima Khan, newsroom): raw footage — Object Lock COMPLIANCE + deny delete.
  • Retail (Lucas Meyer, D2C brand): product images — public read for /images/* only; deny everything else.
  • SaaS (Ananya Iyer, B2B CRM): tenant exports — ${aws:username} or ${aws:PrincipalTag/tenant} prefixes.
  • Government (Rahul Deshmukh, state portal): RTI archives — OrgID + MFA + Object Lock.

19. AccessDenied debugging flowchart

  1. Read the error code and the request ID. Save both.
  2. Confirm you are calling the right bucket and region.
  3. Check Block Public Access if the principal is * or a service that looks public.
  4. Decode the policy: is there an explicit Deny that matches? Deny wins.
  5. Check Condition keys: SecureTransport, SourceVpce, SourceIp, PrincipalOrgID, encryption headers.
  6. For KMS: does the key policy allow the caller? Bucket policy alone is not enough.
  7. For CloudFront: is OAC present and the distribution id matching?
  8. For VPC: are you really going through the endpoint (no public IP leak)?
  9. Simulate in IAM Policy Simulator, then re-test with CLI from the real path.
  10. Only then open a support case with the request id and a redacted policy.

20. What good looks like in a PR review

  • Policy JSON validates and is indented.
  • Every Statement has Sid, Effect, Action, Resource.
  • No Principal:* with write actions unless BPA and a Deny fence exist.
  • Allow test and Deny test are documented with expected output.
  • Cleanup steps will not strand Object Lock or KMS keys.

Practice Tasks

About this scenario

Nairobi Ride is a transport business in Nairobi. Wanjiru Kamau looks after its IT and cloud work. They count money in KES and run their AWS resources in the af-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 16

Easy Get logs bucket policy

Why: Delivery allow.

What to do: Get bucket policy nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy List log prefixes

Why: AWSLogs.

What to do: List objects nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Service principal needed — 1

Why: Delivery.

What to do: Enter 1 if CloudTrail needs an allow in the destination bucket policy.

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Easy Cross-account is different owners — 1

Why: Accounts.

What to do: Enter 1 if cross-account means writer and bucket can be different accounts.

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Easy Get logs versioning

Why: State.

What to do: Get bucket versioning nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Head logs bucket

Why: Exists.

What to do: Head bucket nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Get BPA logs

Why: BPA.

What to do: Get public access block nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Get bucket ACL

Why: Owner check.

What to do: Get bucket acl nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Get logs location

Why: Region.

What to do: Get bucket location nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Allow ELB log delivery GetBucketAcl

Why: ALB logs.

What to do: Allow s3:GetBucketAcl for elb service on arn:aws:s3:::nairobiride-logs.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Medium bucket-owner-full-control JSON

Why: Ownership.

What to do: JSON s3:x-amz-acl bucket-owner-full-control.

How:
  1. Start from the top-level keys the task names.
  2. Indent child keys with 2 spaces (no tabs).
  3. Fill in each property with the value from the task.
  4. Click Check answer. Key order does not matter.

Medium Unrelated Put should deny — 1

Why: Least.

What to do: Enter 1 if unrelated IAM users should not PutObject to the log bucket.

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Medium Protect deletes on log bucket — 1

Why: Integrity.

What to do: Enter 1 if denying delete helps protect audit logs.

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Hard Allow CloudTrail PutObject

Why: Trail delivery.

What to do: Allow s3:PutObject for cloudtrail.amazonaws.com on arn:aws:s3:::nairobiride-logs/AWSLogs/104591109902/*.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Hard AWSLogs prefix JSON

Why: Organize.

What to do: JSON prefix AWSLogs/123456789012/.

How:
  1. Start from the top-level keys the task names.
  2. Indent child keys with 2 spaces (no tabs).
  3. Fill in each property with the value from the task.
  4. Click Check answer. Key order does not matter.

Hard Delete logs policy

Why: Cleanup.

What to do: Delete bucket policy nairobiride-logs.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Revision, interview and comparison cards

Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.

Revision card: Cross-account log bucket. ALB and CloudTrail must write into a locked archive account. Bucket policy allows service principals (cloudtrail.amazonaws.com / ELB account) to PutObject under AWSLogs/ prefixes. Create archive bucket → delivery statements → enable Trail/ALB logs → verify objects → deny random Put. Lucas Meyer Org trail lands in log-archive account 999… only.
Revision card: Cross-account log bucket · Download PNG · Open card page
Revision card: Ownership and ACL condition. Delivery often requires bucket-owner-full-control so the archive account owns objects. Condition StringEquals s3:x-amz-acl bucket-owner-full-control on PutObject. Unrelated IAM user PutObject → AccessDenied. Ananya Iyer verifies ACL condition with a failing unsigned put.
Revision card: Ownership and ACL condition · Download PNG · Open card page
Comparison: CloudTrail delivery vs ALB access logs delivery. CloudTrail delivery: cloudtrail.amazonaws.com puts JSON digests/logs. ALB access logs delivery: Regional ELB account puts access log lines.
Comparison: CloudTrail delivery vs ALB access logs delivery · Download PNG · Open card page
Comparison: Central log-archive account vs Logs in the app account. Central log-archive account: Harder for attackers in app account to wipe evidence. Logs in the app account: Simpler; weaker blast-radius isolation.
Comparison: Central log-archive account vs Logs in the app account · Download PNG · Open card page
Interview card: Who puts CloudTrail logs?. cloudtrail.amazonaws.com must be allowed on the destination prefix. Often with bucket-owner-full-control ACL condition. Objects appear under AWSLogs/account-id/.
Interview card: Who puts CloudTrail logs? · Download PNG · Open card page
Interview card: ALB access logs need?. Region-specific ELB account principal + GetBucketAcl/PutObject patterns per AWS docs. Enable on the load balancer after policy. Lines land under AWSLogs/…/elasticloadbalancing/.
Interview card: ALB access logs need? · Download PNG · Open card page
Interview card: What is cross-account here?. Log writers (service or member accounts) write into an archive account bucket. Resource policy on the archive bucket is essential. Member Trail → archive bucket in log account.
Interview card: What is cross-account here? · Download PNG · Open card page
Interview card: How protect log integrity?. Deny delete/overwrite where possible; versioning; Object Lock; MFA Delete; validate Trail. Separate duties for anyone who can change the policy. Random delete-object → AccessDenied.
Interview card: How protect log integrity? · Download PNG · Open card page

More cards: Ravi Sir's Cards.