Private by default
A new bucket follows the rule you have now seen three times (IAM 2-2, security groups 3-3): everything is denied until a policy allows it. Only your account can touch the objects.
Access opens up two ways:
- IAM policies (lesson 2-2) grant your users and roles access, like the photo-app role reading
my-app-photos/*. - A bucket policy is a policy attached to the bucket itself, and it can grant access to anyone, including the whole internet, with
"Principal": "*".
Public buckets are behind years of headlines about leaked customer data, so AWS added Block Public Access, an account-wide master switch that overrides any accidental public policy. You switch it off only for buckets that are meant to be websites.
Block Public Access as a final override
Block Public Access sits on top of normal policy evaluation as a final override, which is the same explicit-deny-wins idea from lesson 2-2 packaged as one switch. This function is that decision in miniature, called for three buckets.
check() { local block=$1 policy_public=$2 if [ "$policy_public" != "yes" ]; then echo "public request: DENIED (no policy allows it)" elif [ "$block" = "on" ]; then echo "public request: DENIED (Block Public Access overrides the policy)" else echo "public request: ALLOWED (a policy allows it and the master switch is off)" fi } check on yes check off yes check off no
Output
public request: DENIED (Block Public Access overrides the policy) public request: ALLOWED (a policy allows it and the master switch is off) public request: DENIED (no policy allows it)
Mapping the three calls
- The switch on with a public policy is denied, and this is the case that saves companies from headlines. Someone wrote a public policy, and the account-wide switch refused to honour it.
- The switch off with a public policy is the only allowed case. Both conditions have to be true, which is deliberate: making a bucket public takes two separate decisions in two different places.
- The switch off with no public policy is still denied, because turning the master switch off grants nothing by itself. It only stops blocking.
Static website hosting
A static site is files that do not change per visitor: HTML, CSS, JavaScript, images. Think of a portfolio, docs, or the built output of a React app.
You have served files by running nginx in a container. S3 removes even that: enable website hosting on a bucket, make it publicly readable, upload, done. No instance, no security group, no patching, and it costs cents.
aws s3 website s3://my-portfolio --index-document index.html aws s3 sync ./build s3://my-portfolio
sync uploads whatever changed in ./build, so a deploy is one command. The capstone (lesson 9-1) builds a production version of this with HTTPS and a CDN (content delivery network) in front, a worldwide set of cache servers that hold copies of your files close to each visitor.
Why S3 beats EC2 plus nginx for a static build
For a React build folder with small traffic and no server-side code, S3 wins because there is no server to size, patch, firewall, or pay for hourly, and S3 scales and stores redundantly on its own.
Static files need no compute of yours. S3 already is a fleet of redundant file servers spread across at least three AZs, so renting an instance to do the same job adds an operating system to maintain and a security group to get right.
The cost difference is stark. A t3.micro runs about $7.50 a month from lesson 3-1's table, and it sits idle most of the time. The same site on S3 costs cents, because you pay for the bytes stored and the requests served rather than for a machine that has to exist whether anyone visits or not.
The picture changes when there is server-side code. Anything that has to run per request, such as authentication, database queries, or payment handling, needs EC2 or Lambda, and this comparison no longer applies.
The character that opened a payroll bucket
The character is the asterisk in "Principal": "*".
It means "every identity on the internet, signed in or not". Combined with Allow and s3:GetObject, anyone who guesses a URL can download payroll files, and lesson 4-1 showed just how predictable those URLs are.
The trick to spotting this is to read who a policy grants access to before reading what it grants. Principal names the identities a statement applies to, and everything else in the statement is scoped by it. An Action list can look perfectly reasonable while the Principal hands it to the world.
The fix has two parts. Scope Principal to the exact roles that need access, and keep Block Public Access on for buckets like this so that a future mistake in the policy cannot take effect.