Setting Up Non-Root User and Group in Bazel oci_image with Proper Permissions

211 Views Asked by At

I'm working with Bazel to build an OCI image for a Python application, and I'm trying to configure it to run as a non-root user (mo1). While I've managed to specify this user in the Bazel oci_image rule, I'm encountering permission issues when the container runs.

oci_image(
    name = "my_image",
    base = "@python3_11",
    entrypoint = ["python", "my_app.py"],
    user = "mo1:mo1",
    # Other configurations...
)

However, when running the container, the mo1 user doesn't seem to have the necessary permissions to execute certain files, leading to errors like:

/bin/sh: 1: /opt/services/metadata/metadata_bin.runfiles: Permission denied

So, how can I configure the oci_image in Bazel to set up a non-root user (mo1) and group, ensuring they have the correct permissions to access and run the application files?

I created this script create_user_and_group.sh

#!/bin/bash
set -e
WORKDIR="rootfs"
mkdir -p $WORKDIR/etc $WORKDIR/home/<some folder>
echo "mo1:x:1000:" > $WORKDIR/etc/group
echo "mo1:x:1000:1000::/home/<some folder>:/bin/bash" > $WORKDIR/etc/passwd
tar -czf accelerate_user_layer.tar -C $WORKDIR .

genrule(
    name = "generate_user_layer",
    srcs = ["create_user_and_group.sh"],
    outs = ["user_layer.tar"],
    cmd = "(./$(location create_user_and_group.sh) && cp user_layer.tar $(location user_layer.tar))",
    visibility = ["//visibility:public"],
)
1

There are 1 best solutions below

3
Vertexwahn On

Using rules_docker

It seems that rules_docker had support for this. The functionality for this is now moved to rules_distroless.

The old way via rules_docker (tested with Bazel 7.0.2):

Add to your WORKSPACE.bazel file:

# This file is used for legacy dependencies that do not have Bzlmod support

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
    
http_archive(
    name = "io_bazel_rules_docker",
    sha256 = "b1e80761a8a8243d03ebca8845e9cc1ba6c82ce7c5179ce2b295cd36f7e394bf",
    urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"],
)

Note: rules_docker is deprecated now and there is no support for Bzlmod (at least it is not supported by BCR). Nevertheless, Bazel 7.x still supports the traditional WORKSPACE approach. Anyways we need only a few utility functions from rules_docker.

load("@io_bazel_rules_docker//contrib:passwd.bzl", "passwd_entry", "passwd_file")

passwd_entry(
    name = "nonroot_user",
    info = "nonroot",
    uid = 1002,
    username = "nonroot",
)

passwd_file(
    name = "passwd",
    entries = [
        ":nonroot_user",
    ],
)

pkg_tar(
    name = "passwd_tar",
    srcs = [":passwd"],
    mode = "0644",
    package_dir = "etc",
)

oci_image(
    name = "image",
    base = "@distroless_SOMETHING",
    ....
    tars = [
        ":passwd_tar",
    ],
)

I tested a docker image build this way - but it seems not to be non-root. Maybe you get it working.

Using rules_distorless

Add to your MODULE.bazel file bazel_dep(name = "rules_distroless", version = "0.1.3")

And to your BUILD.bazel file:

load("@rules_distroless//distroless:defs.bzl", "passwd")

passwd(
    name = "passwd",
    passwds = [
        dict(
            gecos = ["nonroot"],
            gid = 1000,
            home = "/nonroot",
            shell = "/usr/bin/bash",
            uid = 1000,
            username = "nonroot",
        ),
    ],
)

oci_image(
    name = "image",
    ...
    tars = [
        ...
        ":passwd",
    ],
)

Did also not work for me.

Switch to a nonroot image variant

I finally switched to a non-root base image. And this worked.

oci_image(
    name = "image",
    #base = "@distroless_java21",
    base = "@distroless_java21_nonroot",