Days or weeks after a deploy, strangers have your files. The S3 access logs (or a sudden data-transfer line in the bill) show GET requests from unknown IPs all over the world against the model bucket. In the worst version, the bucket also held a customer csv, and the team learns about it from a security researcher's email — or a client's lawyer. The change that caused it looked harmless in the console: Block Public Access switched off and a public-read policy added, "because the Lambda kept getting AccessDenied".
First cloud deploy of an ML service. A Lambda or container service needed to
read the model file from S3 and threw AccessDenied — the execution role was
missing or pointed at the wrong bucket. Under demo pressure, someone "fixed"
it by making the bucket public: Block Public Access off, objects readable by
anyone with the URL. It worked instantly, the demo went fine, and the setting
was forgotten. Automated scanners find newly public buckets within days —
they watch for them continuously. Linked modules: 4.7 (Cloud deployment),
3.11 (Safety and DPDP).
The error was real; the fix treated the wrong layer. AccessDenied meant "this
identity lacks permission", and the correct dial is the identity's role — but
public access turns the dial that controls the whole world instead. Public is
not "our Lambda can read it"; it is "every scraper, competitor and stranger
can read it, without logs tying reads to any identity". A model file leaking
loses IP; a customer csv leaking is a DPDP Act incident with real liability.
Confirm in one place: the bucket's Permissions tab shows Block Public Access
off — and aws s3api get-public-access-block returns the four flags false.
The service never needed a public bucket; it needed one policy statement.
Close the door first: re-enable Block Public Access on the bucket (all four
flags) and delete any public-read bucket policy — access stops within seconds.
Then fix the actual problem: give the service's execution role a least-privilege
statement — s3:GetObject on arn:aws:s3:::<bucket>/* — and confirm the
service reads the model again. Then assess the exposure honestly: check access
logs for foreign reads; if customer data was reachable, treat it as an
incident — inform the team lead, rotate anything sensitive, and follow the
DPDP process, not the hope process. If a human needs to download a file once,
use a presigned URL that expires, never a public object.
Block Public Access stays on at the account level, so no single bucket can go
public by accident. AccessDenied is always an identity-and-role question
first — the fix lives in IAM, not in the bucket's public settings. Keep
people's data out of artifact buckets entirely: models and coefficients ship,
customer csvs do not. And make the rule cultural: any proposal containing
"make it public so the demo works" gets the same answer as "switch off the
tests so the merge works" — it is the same trade, with worse consequences.
Q: "Your service throws AccessDenied on its model bucket ten minutes before a
demo. A teammate suggests making the bucket public. What do you do?" Strong
answer: reject it by naming the blast radius (public means the world, not the
service — and scrapers find new public buckets in days), then give the
five-minute correct fix: one s3:GetObject statement on one bucket ARN in the
service's role. Mentioning account-level Block Public Access as the standing
guard, and presigned URLs for one-off human sharing, shows you know the
tooling. Tying customer data exposure to DPDP liability shows you know why it
matters in India.