TL;DR: Capture the exact caller, action and object first. Check applicable grants and denies, including session restrictions, organization policies, the endpoint and any KMS key. Bucket owner enforced disables ACLs. Automatic KMS key rotation preserves access to old ciphertext.
How to approach it
Ask who the caller is (IAM user, role, which account) and what exactly failed (ListBucket versus GetObject fail differently). Compare the failing request with a known working request, including credentials, endpoint, object key and encryption metadata. Use that difference to choose the next check.
A strong answer
Use the following as a diagnostic checklist. It is not a literal sequence of independent Allow gates.
1. Identity policy. Run aws sts get-caller-identity with the same credentials as the app. Check s3:GetObject grants on the object ARN, plus boundaries and session policies. Same-account identity and resource grants can combine; cross-account access generally needs permission on both sides. Check for a recent role-session change too: instance profiles and IRSA roles get swapped by deployments nobody counts as "changing access".
2. Organization policies. An applicable SCP or resource control policy (RCP) can deny access despite an identity Allow. Inspect the account’s organization hierarchy and the resource account’s applicable controls. Guardrail rollouts (deny unencrypted transport, restrict region lists, enforce bucket-name conditions) land from the management account with no local deploy, which is precisely how "nobody changed anything" happens. Use aws organizations describe-policy --policy-id POLICY_ID for an identified policy and review attachments with the organization administrator. A different account changes several variables and is not a conclusive test.
3. VPC endpoint policy. Traffic routed through a gateway endpoint is evaluated against the endpoint policy as well as everything else. Tightening one endpoint (often to "fix" another team's exfiltration finding) silently breaks readers in that subnet while everyone else stays fine.
4. Bucket policy. Cross-account reads live or die here. Watch for condition keys: aws:SecureTransport false after a TLS-enforcement rollout, or a Deny with aws:PrincipalOrgID that fires because the caller's account just joined or left an Organization.
5. Block Public Access and ownership. Inspect the settings at the account, bucket and access point where applicable. With Bucket owner enforced, the bucket owner owns the objects and ACLs are disabled. Uploads without an ACL, or with bucket-owner-full-control, are supported. Writer-owned object ACLs matter when ACLs remain enabled. AWS’s Object Ownership documentation explains the distinction.
6. KMS. If the bucket encrypts with SSE-KMS, reading needs kms:Decrypt on that specific key, granted via key policy, and key policies are separate documents nobody associates with the bucket. A disabled key, scheduled deletion, a changed key policy, or new objects encrypted under a different key can explain the failure. Automatic rotation keeps previous key material available for decryption; it does not invalidate old objects. See AWS KMS rotation behavior.
7. The clock. SigV4 signatures expire; wildly skewed system clocks produce signature errors that surface as access failures.
Reproduce a read of a known nonsensitive test object with aws s3api get-object --bucket BUCKET --key TEST_KEY /tmp/s3-access-check.out. This performs a real read; get-object has no dry-run option. Capture the request ID and error message. Where S3 data-event logging is enabled, correlate the CloudTrail event. Enhanced denial context has account and endpoint limitations and may report only one reason. Also check Requester Pays, access-point policies and expired credentials when relevant. AWS’s 403 troubleshooting guide covers these branches. Restore the narrow intended access and repeat the same request to verify the fix.
What interviewers probe next
"Why does ListBucket succeed but GetObject fail?" They are different actions on different resources: listing evaluates against the bucket ARN, reading against arn:aws:s3:::bucket/key. A grant of s3:ListBucket on the bucket without s3:GetObject on its objects produces this split. The reverse grant can allow object reads while listing fails.
"Where do you see which gate fired?" CloudTrail data events plus, on recent denials, the reason string. These provide evidence when available; otherwise correlate the client error, request ID and policy changes. Enable the required data events for future diagnosis.
"How do you prevent the silent-gate class?" Change detection on SCPs, endpoint policies and key policies (Config rules or CI diffing exported policies), including controls managed outside the application repository.
Common mistakes
Fixating on the bucket policy because it is the only gate most tutorials mention. Organization controls, credentials and encryption metadata can change independently of the application.
Adding s3:* to the role as remediation. It broadens access without overriding an explicit deny, endpoint restriction or missing KMS permission.
Forgetting KMS entirely because "encryption is transparent". Transparent until the key policy changes.