Basis Lab

What a Container Actually Is

Why the File It Wrote Is Owned by Root

Last timeLayers, and Why the Build Is Slow

The identity a container runs as is a number, the number means different things on the two sides of the boundary, and a permission error on a mounted directory is arithmetic over two of them.

The symptom is the same every time. A directory on the host is mounted into the

container, the directory is plainly writable, and the service inside reports

that permission was denied. Setting the mode to 0777 makes the message go away,

which is why that is the most common repair and why it is the wrong one. It did

not fix an arithmetic error. It granted write access on that directory to every

process on the host.

The arithmetic is worth doing instead, because there is not much of it. The

whole mechanism is two numbers, a set of further numbers, and nine bits.

The host directory is the one lesson three mounted: /var/lib/service-data on the

host, appearing as /srv/data inside. It is owned by the user who runs the

service, numbered 1000, in the group numbered 1000, with mode 0755. The image

declares that the service runs as the user numbered 10001, in the group numbered

10001, which is the sensible modern default and is where the trouble starts.

The identity is a number

What the kernel holds for a running process is not a user. It is a real user

number and an effective user number, the same pair for groups, and a set of

supplementary group numbers. Every one of those is an integer. The effective

user number is the one almost all permission checks consult, and it is the one

meant below wherever a single number is named.

The names are not in there. When a program prints that it is running as root, it

took the number 0 and looked it up in a text file, and that file is reached

through this process's own root directory, which lesson three replaced. So the

name beside the number is supplied by whichever view is doing the looking. The

image's own file calls 0 root. It almost certainly has no entry at all for

10001, which is why a shell inside such a container announces that it has no

name, and that announcement is not a fault. It is a lookup that failed in a file

that was never going to contain the answer.

FIG 1One write, from the process to the host's record of who owns the file
Follow the left-hand branch, because it is the one almost every container takes. There is no translation step on it, so the number the process runs as is the number recorded on the host's own file system, and the last box is the only place anybody finds out.

The same number means two things

With no mapping in place the number crosses unchanged, and that is not a

simplification. The number 0 inside is the number 0 outside. So a container

running as 0, which is still the default for a great many images, is the host's

root as far as that mounted directory is concerned, and the write succeeds for

the plainest possible reason.

The lesson stops here

6 more paragraphs to go

You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.

The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.

See the planThe contents

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

The rest of this course

  1. 01One Kernel, Many Processes, and No Boot Sequence Anywhere
  2. 02Six Views a Process Can Be Given Instead of Yoursopening only
  3. 03A Different Root Directory, and Why It Is Not a Wallopening only
  4. 04Half a Processor and 512 MB, and Why Only One of Them Killsopening only
  5. 05Layers, and Why One Line Rebuilds Sixty Megabytesopening only
  6. 06Why the File It Wrote Is Owned by Rootyou are here
  7. 07Follow One Request In, and Name Every Translation on the Wayopening only
  8. 08The Boundary Is a View, and the Kernel Underneath It Is Sharedopening only

Read alongside