Re: [meta-virtualization][PATCH 1/7] classes: add container-nonroot-user.bbclass

Tim Orling <[email protected]> Fri, 03 Jul 2026 08:27:26 -0700
Newsgroups org.yoctoproject.lists.meta-virtualization
Message-ID <178309244647.29524.5128328472101493920.b4-reply@b4>
On 2026-06-12 09:54:17-07:00, Bruce Ashfield wrote:
> Hi Tim,
> 
> A few things on this one — none of them blocking the intent, but worth a
> small follow-up either as a fixup in this series or a v2.
> 
> On Fri, May 29, 2026 at 18:31 -0700, Tim Orling wrote:
> 
> > For secure and production environments, we want to run containers as a
> > non-root user. Some applications, such as Python, require a $HOME
> > directory with proper permissions. Because OCI_LAYERS :directories:
> > copies with 'cp -a --no-preserve=ownership', we need a fixup function
> > to create the proper permissions and ownership in a new raw layer.
> >
> > The behavior here is inspired by dhi.io/python:3 (Docker Hardened Image)
> >
> > Signed-off-by: Tim Orling <[email protected]>
> 
> The non-root + DHI-style image is a real gap in what we ship today;
> glad to see it filled.
> 
> > +inherit extrausers
> > +
> > +NONROOT_USER ?= "nonroot"
> > +NONROOT_UID ?= "65532"
> > +NONROOT_GID ?= "65532"
> > +
> > +# ---------------------------------------------------------------------------
> > +# Create the unprivileged "nonroot" user (uid 65532, group 65532)
> > +# ---------------------------------------------------------------------------
> > +EXTRA_USERS_PARAMS = "\
> > +    groupadd -g ${NONROOT_GID} ${NONROOT_USER}; \
> > +    useradd -m -u ${NONROOT_UID} -g ${NONROOT_GID} -d /home/${NONROOT_USER}  ${NONROOT_USER}; \
> > +"
> 
> This hard-assigns EXTRA_USERS_PARAMS rather than appending. Any image
> that inherits container-nonroot-user AND wants its own extrausers (e.g.
> a recipe that needs an additional service account) loses whatever else
> was set, and the order of inheritance silently determines who wins.
> 
> Suggest:
> 
>   EXTRA_USERS_PARAMS += " \
>       groupadd -g ${NONROOT_GID} ${NONROOT_USER}; \
>       useradd -m -u ${NONROOT_UID} -g ${NONROOT_GID} \
>               -d /home/${NONROOT_USER} ${NONROOT_USER}; \
>   "
> 
> That way the class composes cleanly with anyone else who sets
> EXTRA_USERS_PARAMS earlier.
> 

Good catch and suggestion. Applied in v2.

> > +# Make sure we can write to e.g. /home/nonroot/.python_history
> > +# using :directories: in OCI_LAYERS does not preserve permissions
> > +fakeroot fix_oci_home_perms() {
> > +    cd ${IMGDEPLOYDIR}
> > +    image_name="${IMAGE_NAME}${IMAGE_NAME_SUFFIX}-oci"
> > +    layer_tar="${WORKDIR}/oci-home-fix-layer.tar"
> > +
> > +    rm -f "$layer_tar"
> > +
> > +    python3 - "$layer_tar" <<'PYEOF'
> > +import sys, tarfile, time
> > +
> > +layer_tar = sys.argv[1]
> > +mtime = int(time.time())
> > +
> > +# (path, mode, uid, gid)
> > +entries = [
> > +    ("home",         0o755, 0,     0),
> > +    ("home/${NONROOT_USER}", 0o700, ${NONROOT_UID}, ${NONROOT_GID}),
> > +]
> 
> Two things about the Python heredoc:
> 
> 1. The 'PYEOF' delimiter uses single quotes, which would normally suppress
>    shell expansion inside the heredoc — but bitbake expands ${NONROOT_USER}
>    etc. at parse time *before* the shell ever sees the body, so the
>    expansion does happen, just via a different path than the reader expects.
>    Worth a one-line comment so the next person doesn't try to "fix" it.
> 
> 2. While the values are simple here (alphanumeric user, integer uid/gid)
>    it's fine, but if someone ever sets NONROOT_USER to something with a
>    quote or backslash, the Python source is no longer well-formed. Since
>    this is a meta-virt-internal class, low risk — but a comment
>    ("NONROOT_USER must be a bare identifier") would protect it.
> 

Added comments for 1. and 2. in v2.

> > +    umoci raw add-layer --image "$image_name:${OCI_IMAGE_TAG}" "$layer_tar"
> > +    rm -f "$layer_tar"
> > +
> > +    rm -f "$image_name.tar" "$image_name-dir.tar"
> > +    ( cd "$image_name" && tar -cf "../$image_name.tar" "." )
> > +    tar -cf "$image_name-dir.tar" "$image_name"
> > +}
> > +do_image_oci[postfuncs] += "fix_oci_home_perms"
> 
> The repackaging step at the bottom assumes do_image_oci's output layout
> (image_name.tar / image_name-dir.tar) is stable. If image-oci.bbclass
> ever changes its packaging naming, this silently produces a broken
> tarball — there's no error checking against the assumption.
> 
> A defensive option: stat the expected files before rm -f / tar -cf, and
> bbwarn if either is missing rather than recreating from a possibly-empty
> directory. Or, if there's a helper in image-oci.bbclass that already
> does the repackaging, call that instead of rebuilding by hand.
> 

There is no helper in image-oci.bbclass, the repackaging is handled in
do_image_oci(). Added logic gated on OCI_IMAGE_TAR_OUTPUT to detect the
presence of $image_name/index.json and throw bbfatal if it is not found.
We could bbwarn, but I think we should fail loudly.

> Otherwise the series looks good — I'll keep going through 2/7..7/7 and
> respond per-patch as I have specific notes.
> 
> Bruce