A workload runs as the image's default superuser account - what does that actually cost you?
answer
- the loose default, chosen for convenience
- who the process is, not what it runs
- what a code-execution bug inherits
- a number, not an account name
- checkable against zero before start
basics
~20 sRunning as the superuser hands any code-execution bug full in-container privilege: it can rewrite the program files the running process reads, open every mounted credential regardless of ownership, and exploit whatever the boundary fails to close. An unprivileged, explicitly declared numeric id removes that head start.
solid answer
~50 sThe image's default account is the superuser inside the container - numeric id `0` - because that is the state in which everything the build installed just works. The cost shows up only after something goes wrong: a process that is compromised inherits the identity it was running under, so as the superuser it can overwrite the program files and configuration the running process reads, open any mounted file regardless of which account it was meant for, install tooling into the writable layer, and make use of any gap in the boundary, most of which are only worth anything to a privileged caller. Declaring an unprivileged **numeric** id (not an account name) removes that. A number needs no lookup inside the image and a reviewer or an admission gate can check it is non-zero before the container is ever allowed to start.
go deeper
Know that the default account inside a container is the superuser, that this is a convenience default, and that hardened workloads declare an unprivileged numeric id instead.
Explain what actually changes after a compromise: which writes fail, which mounted files become unreadable, and why a numeric id is checkable before start while an account name is not.
Show the whole posture: the id set in the image, re-declared in the spec, ownership fixed at build time, and a gate refusing id zero - plus an honest account of what the change does not protect.
Frame it as a standard other teams inherit: the default your base images ship with, what admission refuses, and how you absorb the first-start breakages the switch causes across an estate.
## The default is the superuser, and that is a convenience decision Every container image records the account its first process starts under. If neither the image nor the workload spec says otherwise, that account is the **superuser** inside the container - numeric id `0`, the same identity the build itself ran under. Images ship that way because it is the state in which nothing breaks: every path the build created is writable, every port is bindable, every file the build installed is readable no matter what its permission bits say. The convenient answer and the safe answer point in opposite directions here, which is exactly why this is a first-screen interview question. It helps to keep two questions apart, because they get blurred constantly: - **Who is the process?** The account id it runs under, which the filesystem checks on every open and the kernel checks on most operations. - **What may the process do beyond that account?** The set of extra powers the runtime grants it, and whether it can gain more later. This question is the first one. The second is a separate declaration and a separate decision. ## What the superuser identity buys an attacker Take a service that renders reports and uploads them, and suppose a bug in its input parsing gives an attacker code execution inside the container. What happens next depends almost entirely on which account that code inherits. As the superuser, the attacker can: - **rewrite the program files and configuration** that the running process reads, changing its behaviour under it for the life of that container; - **open every file mounted into the container**, including a credential file whose permission bits were set for one specific account, because the superuser is not subject to those bits; - **write anywhere**, so tooling can be dropped in and run; - **reach devices and paths** that the boundary exposes but that ordinary accounts are not permitted to touch; - and decisively, be in the best possible position to exploit any weakness in the isolation boundary itself, because nearly every such weakness needs a privileged caller to be worth anything. As an unprivileged account, most of that list simply fails. Writes to the image's paths are refused, files owned by other accounts are unreadable unless their bits say otherwise, and the attacker starts one rung lower with work to do before anything interesting is reachable. That is the whole value: it does not prevent the compromise, it shortens what the compromise is worth. ## Declare a number, not a name The declaration can name an account or state a numeric id, and those are not equivalent. | declared as | what must be true | what a reviewer or a gate can check | |---|---|---| | an account name | the image's own account database contains that name | nothing before start - the name is resolved inside the image, and it can perfectly well map to id `0` | | a numeric id | nothing; the kernel only ever deals in numbers | that the number is non-zero, checked against the spec before the container is allowed to start | A numeric id with no matching entry in the image's account database is normal and mostly harmless - the kernel does not care. What does care is anything that looks the account up by name or wants a home directory: those calls fail or return something surprising, so point such settings at a path you have declared writable. ## What an unprivileged id does not buy you Being honest about the limits is half of what the interviewer is listening for: - It does not narrow what the workload was **already authorized** to do. The credentials mounted for it, the services it may call, the data it may read - a compromise still costs you all of that, because the legitimate process needed it. - It is not, by itself, the isolation boundary. It lowers the value of a gap in that boundary; it does not close one. - It says nothing about the extra powers the runtime grants the process, nor about whether the process can gain more when it executes another program. Those are separate declarations. ## Making the declaration stick 1. Set an unprivileged account in the **image**, so the safe state is the default even for someone who runs it with no spec at all. 2. Re-declare the numeric user and group id in the **workload spec**, where a reviewer sees it and a gate can read it. 3. Set the ownership of the files the process must read to that same id **at build time**, so the switch does not break the first start. 4. Refuse specs that ask for id `0` at admission, rather than relying on everyone remembering. The steps are ordered deliberately: the image makes it true, the spec makes it visible, the build makes it work, and the gate makes it stay that way.
- Why is a numeric id preferred over an account name created inside the image?A name has to be resolved against the image's own account database, which nothing outside the image can see, and it may resolve to id `0`. A number needs no lookup, and a reviewer or an admission gate can verify it is non-zero before the container starts. The cost is that the id may have no matching account entry, so anything doing a name or home-directory lookup needs pointing elsewhere.
- Does running unprivileged protect the data the workload is allowed to reach?No. Everything the legitimate process is entitled to - its mounted credentials, the services it may call, the rows it may query - is entitled to the attacker too, because the attacker is that process. An unprivileged id shrinks what a compromise can do to the container and to the host; it does nothing about what the workload was authorized to do in the first place.
- Is it enough to set the account in the workload spec and leave the image alone?It works, but it is fragile. The image still defaults to the superuser for anyone who runs it without that spec, and the files it shipped are still owned by the build-time account, so the first start under the new id usually fails on an unreadable or unwritable path. Set it in both places and fix ownership at build time.
A master key left in the lock because it opens every door in the building. Nothing is wrong until someone else picks it up.
saying these in an interview costs you the question
- The isolation boundary makes superuser inside the container harmless.
- Nobody logs into containers, so which account it runs as does not matter.
- An unprivileged account means the workload cannot be compromised.
- Declaring an account name is equivalent to declaring a numeric id.
- It is a compliance checkbox with no effect on a real incident.