dev_container: Fix podman and enable unprivileged containers

To enable podman, the Dockerfile has been split into a root
run Dockerfile and one that adds a non-root user.

The following combinations have been tested:

./tools/dev_container -v --clean --podman --unprivileged
./tools/dev_container -v --clean --podman
./tools/dev_container -v --clean --unprivileged

And warnings have been added to ensure users are aware that
the only fully supported variant is running a privileged
docker container:

./tools/dev_container -v --clean

The unprivileged containers will allow us to validate if
unit tests require privileged system access.

BUG=None
TEST=See above

Change-Id: I185b1d9c3829674986305b0e72a39b1a4ba11b98
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3971029
Reviewed-by: Zihan Chen <zihanchen@google.com>
Commit-Queue: Dennis Kempin <denniskempin@google.com>
Reviewed-by: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
Dennis Kempin 2022-10-21 21:51:51 +00:00 committed by crosvm LUCI
parent d57ff28353
commit 6a2b1fda85
5 changed files with 104 additions and 44 deletions

View file

@ -40,21 +40,36 @@ CACHE_DIR = os.environ.get("CROSVM_CONTAINER_CACHE", None)
DOCKER_ARGS = [
# Share cache dir
f"--volume {CACHE_DIR}:/cache:rw" if CACHE_DIR else None,
# Share devices and syslog
# Use tmpfs in the container for faster performance.
"--mount type=tmpfs,destination=/tmp",
# KVM is required to run a VM for testing.
"--device /dev/kvm",
f"--env OUTSIDE_UID={os.getuid()}",
f"--env OUTSIDE_GID={os.getgid()}",
f"gcr.io/crosvm-infra/crosvm_dev_user:{IMAGE_VERSION}",
]
PODMAN_ARGS = [
# Share cache dir
f"--volume {CACHE_DIR}:/cache:rw" if CACHE_DIR else None,
# Use tmpfs in the container for faster performance.
"--mount type=tmpfs,destination=/tmp",
# KVM is required to run a VM for testing.
"--device /dev/kvm",
f"gcr.io/crosvm-infra/crosvm_dev:{IMAGE_VERSION}",
]
PRIVILEGED_ARGS = [
# Share devices and syslog
"--volume /dev/log:/dev/log",
"--device /dev/net/tun",
"--device /dev/vhost-net",
"--device /dev/vhost-vsock",
# Use tmpfs in the container for faster performance.
"--mount type=tmpfs,destination=/tmp",
# For plugin process jail
"--mount type=tmpfs,destination=/var/empty",
f"--env OUTSIDE_UID={os.getuid()}",
f"--env OUTSIDE_GID={os.getgid()}",
f"gcr.io/crosvm-infra/crosvm_dev:{IMAGE_VERSION}",
]
PODMAN_IS_DEFAULT = shutil.which("docker") == None
@ -111,7 +126,9 @@ def ensure_container_is_alive(docker: cmd, docker_args: List[Optional[str]]):
if not container_is_running(docker):
# Run neverending sleep to keep container alive while we 'docker exec' commands.
docker(f"run --detach --name {CONTAINER_NAME}", *docker_args, "sleep infinity").stdout()
docker(f"run --detach --name {CONTAINER_NAME}", *docker_args, "sleep infinity").fg(
quiet=True
)
cid = container_id(docker)
print(f"Started dev-container ({cid}).")
else:
@ -130,22 +147,32 @@ def main(
podman: bool = PODMAN_IS_DEFAULT,
self_test: bool = False,
pull: bool = False,
unprivileged: bool = False,
):
chdir(CROSVM_ROOT)
docker = cmd("podman" if podman else "docker")
docker_args = [
# Podman will not share devices when `--privileged` is specified
"--privileged" if not podman else None,
*workspace_mount_args(),
*DOCKER_ARGS,
]
if podman:
print("WARNING: Running dev_container with podman is not fully supported.")
print("Some crosvm tests require privileges podman cannot provide and may fail.")
if unprivileged:
print("WARNING: Running dev_container with --unprivileged is a work in progress.")
print("Not all tests are expected to pass.")
print()
docker_args = [
*workspace_mount_args(),
*(PRIVILEGED_ARGS if not unprivileged else []),
]
if podman:
print("WARNING: Running dev_container with podman is experimental.")
print("It is strongly recommended to use docker.")
print()
docker = cmd("podman")
docker_args += [*PODMAN_ARGS]
else:
docker = cmd("docker")
docker_args += [
"--privileged" if not unprivileged else None,
*DOCKER_ARGS,
]
if self_test:
TestDevContainer.docker = docker
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestDevContainer)
@ -162,6 +189,7 @@ def main(
if pull:
docker("pull", f"gcr.io/crosvm-infra/crosvm_dev:{IMAGE_VERSION}").fg()
docker("pull", f"gcr.io/crosvm-infra/crosvm_dev_user:{IMAGE_VERSION}").fg()
return
# If a command is provided run non-interactive unless explicitly asked for.
@ -182,10 +210,14 @@ def main(
else:
# cmd is executed directly
cid = ensure_container_is_alive(docker, docker_args)
if not command:
command = ("/tools/entrypoint.sh",)
if podman:
if not command:
command = ("/bin/bash",)
else:
command = ("/tools/entrypoint.sh",) + tuple(command)
if not command:
command = ("/tools/entrypoint.sh",)
else:
command = ("/tools/entrypoint.sh",) + tuple(command)
quoted_cmd = list(map(quoted, command))
docker("exec", *tty_args, cid, *quoted_cmd).fg()

View file

@ -1,6 +1,14 @@
# Copyright 2021 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Development container for crosvm.
#
# Provides all dependencies specified in install-deps with some additonal
# logic to cache cargo data in CI runs.
#
# Note, if you are using docker, you will probably be using "Dockerfile.user".
FROM docker.io/debian:testing-20220822-slim
ENV RUSTUP_HOME=/usr/local/rustup \
@ -36,21 +44,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
&& ./install-armhf-deps \
&& ./install-docs-deps
# Add a new password-less sudoer user crosvmdev
RUN useradd -ms /bin/bash crosvmdev \
&& usermod -aG sudo crosvmdev \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
# Pass rust envs from rust toolchain image when sudo into new user
&& echo 'Defaults env_keep += "RUSTUP_HOME CARGO_HOME RUST_VERSION CARGO_TARGET_DIR"' >> /etc/sudoers \
# Allow dependencies and build files to be used and overwritten by user
&& chown -R crosvmdev:crosvmdev /scratch /cache
# Following operations will be run as crosvmdev to ensure correct permission.
USER crosvmdev
# Prepare path to rust toolchain for crosvmdev
RUN echo 'export PATH=/cache/cargo_home/bin:/usr/local/cargo/bin:$PATH' >> /home/crosvmdev/.profile
# Prepare wine64
RUN sudo ln -sf /usr/bin/wine64-stable /usr/bin/wine64 \
&& wine64 wineboot
@ -68,9 +61,3 @@ ENV CARGO_HOME=/cache/cargo_home
VOLUME /workspace
WORKDIR /workspace
# Switch back to root to avoid usermod crosvmdev as crosvmdev
USER root
COPY tools/impl/dev_container/entrypoint.sh tools/impl/dev_container/setup-user.sh /tools/
RUN chmod 755 /tools/entrypoint.sh /tools/setup-user.sh
ENTRYPOINT ["/tools/entrypoint.sh"]

View file

@ -0,0 +1,33 @@
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Extends the "./Dockerfile" created image by adding a non-root user and ensuring
# that user can access the necessary files and devices for development.
#
# This will allow the user to use the same UID/GID inside the container that they have
# on the outside, preventing container created files from being owned by root.
ARG VERSION
FROM gcr.io/crosvm-infra/crosvm_dev:${VERSION}
# Add a new password-less sudoer user crosvmdev
RUN useradd -ms /bin/bash crosvmdev \
&& usermod -aG sudo crosvmdev \
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers \
# Pass rust envs from rust toolchain image when sudo into new user
&& echo 'Defaults env_keep += "RUSTUP_HOME CARGO_HOME RUST_VERSION CARGO_TARGET_DIR"' >> /etc/sudoers \
# Allow dependencies and build files to be used and overwritten by user
&& chown -R crosvmdev:crosvmdev /scratch /cache
# Following operations will be run as crosvmdev to ensure correct permission.
USER crosvmdev
# Prepare path to rust toolchain for crosvmdev
RUN echo 'export PATH=/cache/cargo_home/bin:/usr/local/cargo/bin:$PATH' >> /home/crosvmdev/.profile
# Switch back to root to avoid usermod crosvmdev as crosvmdev
USER root
COPY tools/impl/dev_container/entrypoint.sh tools/impl/dev_container/setup-user.sh /tools/
RUN chmod 755 /tools/entrypoint.sh /tools/setup-user.sh
ENTRYPOINT ["/tools/entrypoint.sh"]

View file

@ -23,10 +23,11 @@ BUILD_CONTEXT=$(shell realpath ../../../)
DOCKER ?= docker
all: crosvm_dev
all: crosvm_dev crosvm_dev_user
upload: all
$(DOCKER) push $(TAG_BASE)/crosvm_dev:$(VERSION)
$(DOCKER) push $(TAG_BASE)/crosvm_dev_user:$(VERSION)
crosvm_dev:
$(DOCKER) build \
@ -34,4 +35,11 @@ crosvm_dev:
-f Dockerfile \
$(BUILD_CONTEXT)
crosvm_dev_user:
$(DOCKER) build \
-t $(TAG_BASE)/$@:$(VERSION) \
-f Dockerfile.user \
--build-arg=VERSION=$(VERSION) \
$(BUILD_CONTEXT)
.PHONY: all crosvm_dev upload

View file

@ -1 +1 @@
r0026
r0027