RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
S3 VPC Endpoint Policy Action Verifier
02. Root Cause Vector

S3 GetObject requests fail with Access Denied when the VPC endpoint policy omits explicit s3:GetObject permissions, blocking all traffic ...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Security

S3 VPC Endpoint Policy Action Verifier

S3 GetObject requests fail with Access Denied when the VPC endpoint policy omits explicit s3:GetObject permissions, blocking all traffic regardless of bucket policy grants.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(142L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Legacy PCI zone S3 bucket with VPC endpoint for legacy-api. TODO: Migrate to new architecture.",
  "Parameters": {
    "EnvironmentName": {
      "Type": "String",
      "Default": "pci-secure-zone-prod",
      "Description": "The environment name for tagging (DO NOT CHANGE)"
    },
    "LegacyApiKey": {
      "Type": "String",
      "Default": "AKIAIOSFODNN7EXAMPLE",
      "Description": "Legacy IAM key for internal service (rotate quarterly)"
    }
  },
  "Resources": {
    "PCIS3Bucket": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "${EnvironmentName}-legacy-data-${AWS::AccountId}"
        },
        "AccessControl": "Private",
        "BucketEncryption": {
          "ServerSideEncryptionConfiguration": [
            {
              "ServerSideEncryptionByDefault": {
                "SSEAlgorithm": "AES256"
              }
            }
          ]
        },
        "Tags": [
          {
            "Key": "CostCenter",
            "Value": "PCI-789"
          }
        ]
      }
    },
    "S3VPCEndpoint": {
      "Type": "AWS::EC2::VPCEndpoint",
      "Properties": {
        "ServiceName": {
          "Fn::Sub": "com.amazonaws.${AWS::Region}.s3"
        },
        "VpcId": {
          "Ref": "VPC"
        },
        "RouteTableIds": [
          {
            "Ref": "PrivateRouteTable"
          }
        ],
        "VpcEndpointType": "Gateway",
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": "*",
              "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
                // TODO: Add GetObject after compliance review
                // "s3:GetObject"
              ],
              "Resource": "*"
            },
            {
              "Effect": "Deny",
              "Principal": "*",
              "Action": "s3:*",
              "Resource": "*",
              "Condition": {
                "StringNotEquals": {
                  "aws:PrincipalArn": "arn:aws:iam::123456789012:role/LegacyAppRole"
                }
              }
            }
          ]
        }
      }
    },
    "S3BucketPolicy": {
      "Type": "AWS::S3::BucketPolicy",
      "Properties": {
        "Bucket": {
          "Ref": "PCIS3Bucket"
        },
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "AWS": "*"
              },
              "Action": "s3:GetObject",
              "Resource": {
                "Fn::Sub": "arn:aws:s3:::${PCIS3Bucket}/*"
              },
              "Condition": {
                "StringEquals": {
                  "aws:SourceVpce": {
                    "Ref": "S3VPCEndpoint"
                  }
                }
              }
              // VULNERABILITY: Bucket policy allows GetObject from VPC endpoint, but endpoint policy only allows ListBucket and GetBucketLocation, not GetObject. This mismatch causes Access Denied.
            },
            {
              "Effect": "Deny",
              "Principal": "*",
              "Action": "s3:*",
              "Resource": {
                "Fn::Sub": "arn:aws:s3:::${PCIS3Bucket}/*"
              },
              "Condition": {
                "Bool": {
                  "aws:SecureTransport": "false"
                }
              }
            }
          ]
        }
      }
    },
    "LegacyAppConfig": {
      "Type": "AWS::SSM::Parameter",
      "Properties": {
        "Name": {
          "Fn::Sub": "/${EnvironmentName}/legacy-api/config"
        },
        "Type": "String",
        "Value": {
          "Fn::Sub": "{\"database_uri\":\"postgresql://admin:SuperSecretDBPass123!@my-registry.internal:5432/pci_db\",\"stripe_key\":\"sk_live_51Kj8p2LmEXAMPLEfaketoken9876\",\"ssh_private_key\":\"-----BEGIN RSA PRIVATE KEY-----\\nMIIEowIBAAKCAQEArXJ7... (truncated for brevity) ...\\n-----END RSA PRIVATE KEY-----\"}"
        }
      }
    }
  }
}
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

Fixing S3 Access Denied on GetObject: VPC Endpoint Policy Missing S3 Actions

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins


TL;DR

  • What broke: Your VPC endpoint policy is an implicit deny machine. Even if the S3 bucket policy grants s3:GetObject, the endpoint policy is a second, independent policy evaluation layer — if it doesn't explicitly allow the action, AWS denies the request.
  • How to fix it: Add s3:GetObject (and any other required S3 actions) to the Action block of your VPC Gateway Endpoint policy. Scope it to the specific bucket ARN under Resource.
  • Shortcut: Use our Client-Side Sandbox above to auto-refactor this — paste both your endpoint policy and bucket policy and get corrected JSON output without leaking your ARNs to a third-party server.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

You're seeing this in your application logs or AWS CloudTrail:

An error occurred (AccessDenied) when calling the GetObject operation:
 Access Denied

CloudTrail event will show:

{
  "eventName": "GetObject",
  "errorCode": "AccessDenied",
  "errorMessage": "Access Denied",
  "vpcEndpointId": "vpce-0abc1234def567890",
  "sourceIPAddress": "vpce-0abc1234def567890.s3.us-east-1.vpce.amazonaws.com"
}

Immediate consequence: Every EC2 instance, Lambda, or ECS task in your VPC that routes S3 traffic through the endpoint is dead in the water. No objects can be read. If this is a data pipeline, ETL job, or application config loader — it's fully blocked.

AWS evaluates both the VPC endpoint policy and the S3 bucket policy. Both must allow the action. One deny anywhere = full deny. There is no override.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is a misconfiguration-induced outage, not an external attack — but the blast radius is significant:

  • All principals routing through the endpoint are affected, not just one role or service.
  • If your endpoint policy uses "Action": "s3:*" scoped to the wrong resource, or uses "Effect": "Deny" without a matching allow, the failure is total and silent from the application's perspective — it just sees Access Denied.
  • Secondary risk: Teams responding to this outage sometimes "fix" it by opening the endpoint policy to "Action": "*" and "Resource": "*" — this is catastrophic. It allows any principal in the VPC to call any S3 API on any bucket, including s3:DeleteObject, s3:PutBucketPolicy, and s3:GetObject on buckets outside your account that the endpoint can reach.
  • Data exfiltration vector: An overly permissive endpoint policy (s3:* on *) combined with a compromised EC2 instance allows an attacker to exfiltrate data to an attacker-controlled S3 bucket through your own VPC endpoint — bypassing egress controls that only inspect public IPs.

How to Fix It (The Solution)

Basic Fix

Your current broken endpoint policy likely looks like this — either missing S3 actions entirely or scoped incorrectly:

{
  "Version": "2012-10-17",
  "Statement": [
    {
-     "Effect": "Allow",
-     "Principal": "*",
-     "Action": "s3:ListBucket",
-     "Resource": "arn:aws:s3:::my-app-bucket"
+     "Effect": "Allow",
+     "Principal": "*",
+     "Action": [
+       "s3:GetObject",
+       "s3:ListBucket"
+     ],
+     "Resource": [
+       "arn:aws:s3:::my-app-bucket",
+       "arn:aws:s3:::my-app-bucket/*"
+     ]
    }
  ]
}

Critical: s3:ListBucket applies to the bucket ARN (arn:aws:s3:::bucket-name). s3:GetObject applies to object ARNs (arn:aws:s3:::bucket-name/*). Getting this wrong is the second most common cause of this exact error.


Enterprise Best Practice

Scope the endpoint policy to specific IAM principals and enforce aws:PrincipalOrgID so only your organization's identities can use the endpoint. Never use "Principal": "*" without a restricting condition.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowScopedS3Access",
      "Effect": "Allow",
-     "Principal": "*",
-     "Action": "s3:*",
-     "Resource": "*"
+     "Principal": "*",
+     "Action": [
+       "s3:GetObject",
+       "s3:ListBucket",
+       "s3:PutObject"
+     ],
+     "Resource": [
+       "arn:aws:s3:::my-app-bucket",
+       "arn:aws:s3:::my-app-bucket/*"
+     ],
+     "Condition": {
+       "StringEquals": {
+         "aws:PrincipalOrgID": "o-xxxxxxxxxx"
+       }
+     }
    },
    {
+     "Sid": "DenyExternalBucketAccess",
+     "Effect": "Deny",
+     "Principal": "*",
+     "Action": "s3:*",
+     "NotResource": [
+       "arn:aws:s3:::my-app-bucket",
+       "arn:aws:s3:::my-app-bucket/*"
+     ]
    }
  ]
}

The Deny on NotResource is the critical enterprise control — it prevents the endpoint from being used as a data exfiltration channel to external buckets.

Also lock down the bucket policy to enforce VPC endpoint access only:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNonVPCEAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-app-bucket",
        "arn:aws:s3:::my-app-bucket/*"
      ],
      "Condition": {
        "StringNotEquals": {
+         "aws:sourceVpce": "vpce-0abc1234def567890"
        }
      }
    }
  ]
}

💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

1. Checkov — catch overly permissive endpoint policies at plan time:

Checkov rule CKV_AWS_123 flags VPC endpoint policies with Action: * or Resource: *. Add to your pipeline:

checkov -d . --check CKV_AWS_123 --framework terraform

2. OPA/Conftest policy to block wildcard actions in endpoint policies:

package aws.vpce

deny[msg] {
  stmt := input.resource.aws_vpc_endpoint_policy.statements[_]
  stmt.actions[_] == "s3:*"
  msg := "VPC endpoint policy must not use wildcard s3:* action. Enumerate required actions explicitly."
}

deny[msg] {
  stmt := input.resource.aws_vpc_endpoint_policy.statements[_]
  stmt.resources[_] == "*"
  msg := "VPC endpoint policy must not use wildcard Resource. Scope to specific bucket ARNs."
}

3. Terraform — use aws_vpc_endpoint_policy resource, not inline policy on aws_vpc_endpoint:

This allows separate state management and policy drift detection via terraform plan.

4. AWS Config Rule: Enable vpc-endpoint-no-unrestricted-access managed rule. Set it to alert on any endpoint policy with Effect: Allow + Principal: * + Action: * in combination.

5. CloudTrail + EventBridge alert: Fire an alert any time ModifyVpcEndpoint is called — endpoint policy changes in production should be gated behind a change approval workflow, not ad-hoc CLI commands.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does my S3 bucket policy allow GetObject but I still get Access Denied through the VPC endpoint?
AWS performs an independent policy evaluation at the VPC endpoint layer. Both the endpoint policy and the bucket policy must explicitly allow the action. The endpoint policy is not a pass-through — if it doesn't list s3:GetObject in its Action block with the correct Resource ARN, the request is denied before the bucket policy is even evaluated. This is a fundamental architectural control point that most teams miss, leading to critical data access failures.
Q2.What is the difference between the bucket ARN and the object ARN for S3 IAM policies?
s3:ListBucket operates on the bucket itself, so its Resource must be the bucket ARN: arn:aws:s3:::my-bucket. s3:GetObject, s3:PutObject, and s3:DeleteObject operate on objects, so their Resource must be the object ARN with a wildcard: arn:aws:s3:::my-bucket/*. Manually specifying these ARNs is a notorious source of human error and security misconfiguration. A single typo creates a silent vulnerability. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix with precise, auditable ARN scoping.
Q3.Is it safe to set the VPC endpoint policy to allow all actions on all resources to unblock my team quickly?
No. Setting Action: s3:* and Resource: * on a VPC endpoint policy is a critical security misconfiguration and a known data exfiltration vector. Doing this manually across hundreds of endpoints in a large mono-repo is a compliance nightmare, guaranteeing audit failures and breach exposure. Deploy the StackEngine CI/CD Automator (Team Edition) for on-prem, automated pull request generation with Maker-Checker review that enforces least-privilege at scale and integrates directly into your security toolchain.