DEV Community

Cover image for Day 39: Commit Captures the Layer, Not the Recipe, and AccessDenied Is Not Always IAM
Nnamdi Felix Ibe
Nnamdi Felix Ibe

Posted on AI-assisted

Day 39: Commit Captures the Layer, Not the Recipe, and AccessDenied Is Not Always IAM

Today's two tasks are about things that get left out. docker commit captures a container's filesystem and quietly excludes the volumes you probably cared about, along with any record of how the changes were made. And S3 refuses a bucket policy with an error naming the wrong culprit entirely.

One Docker task, one AWS task. Turn a modified container into an image, then host a public static website out of an S3 bucket. The tasks come from the KodeKloud Engineer platform.

docker commit, and the two things it does not take

docker commit ubuntu_latest media:devops
docker images
Enter fullscreen mode Exit fullscreen mode

That is the whole task. Whatever the container has written to its writable layer becomes a new layer stacked on the image it started from, and the result gets a new image ID.

Three behaviours worth knowing before you rely on it.

The container and its processes are paused for the duration by default, so the filesystem is not shifting under the snapshot. --no-pause skips that, at the cost of possibly capturing a half-written file. Leave the default alone unless you have a specific reason.

Data in mounted volumes is not included. Docker's documentation is explicit that commits do not include any data contained in mounted volumes, which means the database you assumed you were capturing is precisely the part that did not come. Only the container's own writable layer is committed.

And the honest limitation: a committed image records what the filesystem looks like, not how it got that way. Six months later nobody can say which packages were installed or why, and there is no way to rebuild it from source. A Dockerfile is the reproducible path, and it is what Day 41 covers.

Where commit genuinely earns its place is capturing state you are about to lose. A container that has reproduced a bug, or a debugging session you want to hand to a colleague before the container is removed. It is a snapshot tool, not a build tool.

The order of four commands

The AWS task was a static site served directly from S3. Public, no CloudFront, no web server. Four steps, and three of them only work if the one before has already run.

  1. Turn the public access block off
  2. Put a bucket policy granting s3:GetObject to everyone
  3. Set the website configuration naming the index document
  4. Upload the file

Run step 2 first and you get this:

An error occurred (AccessDenied) when calling the PutBucketPolicy operation
Enter fullscreen mode Exit fullscreen mode

Which reads as "your user is not allowed to set policies on this bucket". It is not that. BlockPublicPolicy is on by default for every new bucket, and AWS documents its job as causing S3 to reject calls to PutBucketPolicy when the policy allows public access. Your credentials are fine. Your policy is fine. The bucket is refusing on principle, and the error names the symptom rather than the rule.

It is one of the more misleading messages in S3, because everything about the wording points at IAM.

The four flags are not one switch

aws s3api put-public-access-block --bucket $BUCKET \
  --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
Enter fullscreen mode Exit fullscreen mode

Two of those govern ACLs and two govern policies, and each pair splits into reject-new and ignore-existing. BlockPublicAcls rejects requests that set a public ACL. IgnorePublicAcls ignores public ACLs that are already there. BlockPublicPolicy rejects a public bucket policy. RestrictPublicBuckets restricts a bucket that already has one to service principals and the owning account.

Only the policy pair strictly matters here, since no ACLs are involved. Turning all four off is the honest answer to "make this public" and matches what the console's single checkbox does.

It is worth saying that turning these off is a real decision rather than a formality. They exist because publicly readable buckets have been behind a long run of data leaks. Doing it deliberately for a site meant to be public is correct. Doing it to make an error go away is how those leaks happen.

One check worth knowing about before you spend ten minutes debugging the bucket:

aws s3control get-public-access-block --account-id $(aws sts get-caller-identity --query Account --output text)
# An error occurred (NoSuchPublicAccessBlockConfiguration)
Enter fullscreen mode Exit fullscreen mode

That error is the good outcome. It means no account-wide block exists, so the bucket-level setting is authoritative. If an account-level block had been set, nothing you do per-bucket could override it, and the policy would keep failing with no clue about where the refusal came from.

Two hostnames, one of which is a website

The bucket ends up with two addresses that behave differently:

http://datacenter-web-1882914365.s3-website-us-east-1.amazonaws.com   website endpoint
https://datacenter-web-1882914365.s3.amazonaws.com                    REST API endpoint
Enter fullscreen mode Exit fullscreen mode

Only the website endpoint honours the index document. Hit the REST endpoint at the root and you get XML or a 403, which looks exactly like put-bucket-website never took effect. It did. You are talking to the wrong hostname.

Three differences worth holding onto. The website endpoint is HTTP only, with no TLS, which is why almost no production static site uses it raw and why CloudFront in front of it is the standard answer. It returns friendly HTML errors and honours redirect rules, where the REST endpoint returns XML. And the hostname itself splits by region, with no rule you can derive. Older regions put a hyphen before the region, s3-website-us-east-1, and eu-west-1 and ap-southeast-1 are in that group too. Newer ones use a dot, s3-website.us-east-2. Guessing wrong gives you a DNS failure rather than an HTTP error, which sends you debugging the bucket instead of the URL.

One more thing that catches people following older guides. aws s3 cp --acl public-read no longer works on new buckets. AWS began disabling ACLs for all new buckets in April 2023, and that call now fails with AccessControlListNotSupported. A bucket policy is the only current way to make objects public.

Then finish the way Day 38 taught me to:

curl -s -m 15 http://datacenter-web-1882914365.s3-website-us-east-1.amazonaws.com
# Welcome to KKE labs!
Enter fullscreen mode Exit fullscreen mode

get-bucket-website returning a configuration proves the setting saved. Only fetching the URL proves the four pieces line up. And if it does not, the status code splits the causes: 403 is the policy or the access block, 404 is an object name that does not match the index document.

What did not come along

A commit that leaves the volumes behind and an error that names IAM instead of the block setting are the same kind of trap. In both cases the tool did exactly what it documents, and the mental model most people carry is slightly wider than the truth.

The fix in both directions is the same: know what the operation covers before you depend on it, and verify the thing you actually wanted rather than the thing that returned zero.

So here is the Day 39 question. The last snapshot or backup you took, do you know for certain which parts of the system it includes?

Day 39 down. Sixty-one to go.

Top comments (0)