From b4581fa8cc4bc5a48dc5943f5d5239c4340630f7 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 09:23:27 +0100 Subject: [PATCH 1/7] chore: update dind examples --- docs/docker.md | 15 +++++++------ examples/docker/02_dind/Dockerfile | 20 +++++++++++++---- examples/docker/02_dind/devcontainer.json | 5 +++-- examples/docker/02_dind/entrypoint.sh | 7 ------ examples/docker/02_dind/on-create.sh | 22 +++++++++++++++++++ examples/docker/03_dind_feature/Dockerfile | 19 ++++++++++++++-- .../docker/03_dind_feature/devcontainer.json | 3 ++- examples/docker/03_dind_feature/entrypoint.sh | 7 ------ examples/docker/03_dind_feature/on-create.sh | 7 ++++++ 9 files changed, 75 insertions(+), 30 deletions(-) delete mode 100755 examples/docker/02_dind/entrypoint.sh create mode 100755 examples/docker/02_dind/on-create.sh delete mode 100755 examples/docker/03_dind_feature/entrypoint.sh create mode 100755 examples/docker/03_dind_feature/on-create.sh diff --git a/docs/docker.md b/docs/docker.md index ca09c724..0c0a4107 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -9,6 +9,7 @@ from inside Envbuilder. > you may need to instead add the relevant content of the init script to your > agent startup script in your template. > For example: +> > ``` > resource "coder_agent" "dev" { > ... @@ -43,7 +44,6 @@ docker run -it --rm \ ghcr.io/coder/envbuilder:latest ``` - ## Docker-in-Docker (DinD) **Security:** Low @@ -57,8 +57,8 @@ Example: > Note that due to a lack of init system, the Docker daemon > needs to be started separately inside the container. In this example, we -> create a custom entrypoint to start the Docker daemon in the background and -> call this entrypoint via `ENVBUILDER_INIT_SCRIPT`. +> create a custom script to start the Docker daemon in the background and +> call this entrypoint via the Devcontainer `onCreateCommand` lifecycle hook. ```console docker run -it --rm \ @@ -66,7 +66,7 @@ docker run -it --rm \ -v /tmp/envbuilder:/workspaces \ -e ENVBUILDER_GIT_URL=https://github.com/coder/envbuilder \ -e ENVBUILDER_DEVCONTAINER_DIR=/workspaces/envbuilder/examples/docker/02_dind \ - -e ENVBUILDER_INIT_SCRIPT=/entrypoint.sh \ + -e ENVBUILDER_INIT_SCRIPT=bash \ ghcr.io/coder/envbuilder:latest ``` @@ -75,7 +75,7 @@ docker run -it --rm \ The above can also be accomplished using the [`docker-in-docker` Devcontainer feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docker). -> Note: we still need the custom entrypoint to start the docker startup script. +> Note: we still need `onCreateCommand` to start the docker startup script. > See https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L60 Example: @@ -86,7 +86,7 @@ docker run -it --rm \ -v /tmp/envbuilder:/workspaces \ -e ENVBUILDER_GIT_URL=https://github.com/coder/envbuilder \ -e ENVBUILDER_DEVCONTAINER_DIR=/workspaces/envbuilder/examples/docker/03_dind_feature \ - -e ENVBUILDER_INIT_SCRIPT=/entrypoint.sh \ + -e ENVBUILDER_INIT_SCRIPT=bash \ ghcr.io/coder/envbuilder:latest ``` @@ -95,7 +95,7 @@ docker run -it --rm \ **Security:** Medium **Convenience:** Medium -This approach runs a Docker daemon in *rootless* mode. +This approach runs a Docker daemon in _rootless_ mode. While this still requires a privileged container, this allows you to restrict usage of the `root` user inside the container, as the Docker daemon will be run under a "fake" root user (via `rootlesskit`). The user inside the workspace can @@ -129,6 +129,7 @@ including transparently enabling Docker inside workspaces. Most notably, it access inside their workspaces, if required. Example: + ```console docker run -it --rm \ -v /tmp/envbuilder:/workspaces \ diff --git a/examples/docker/02_dind/Dockerfile b/examples/docker/02_dind/Dockerfile index 70a215b0..4f38da6a 100644 --- a/examples/docker/02_dind/Dockerfile +++ b/examples/docker/02_dind/Dockerfile @@ -1,6 +1,18 @@ FROM ubuntu:noble +# Install Docker using Docker's convenience script. RUN apt-get update && \ - apt-get install -y curl apt-transport-https && \ - curl -fsSL https://get.docker.com/ | sh -s - -ADD entrypoint.sh /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file + apt-get install -y curl sudo apt-transport-https && \ + curl -fsSL https://get.docker.com/ | sh -s - +# Add a non-root user with sudo privileges and allow +# passwordless sudo for the user. +# Note: we chown /var/run/docker.sock to the non-root user +# in the onCreateCommand script. Ideally you would add the +# non-root user to the docker group, but in this scenario +# this is a 'single-user' environment. It also avoids us +# having to run `newgrp docker`. +RUN useradd -m -s /bin/bash -G sudo coder && \ + echo "coder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/coder +# Add our onCreateCommand script. +ADD on-create.sh /on-create.sh +# Switch to the non-root user. +USER coder diff --git a/examples/docker/02_dind/devcontainer.json b/examples/docker/02_dind/devcontainer.json index 1933fd86..6649501c 100644 --- a/examples/docker/02_dind/devcontainer.json +++ b/examples/docker/02_dind/devcontainer.json @@ -1,5 +1,6 @@ { "build": { "dockerfile": "Dockerfile" - } -} \ No newline at end of file + }, + "onCreateCommand": "/on-create.sh" +} diff --git a/examples/docker/02_dind/entrypoint.sh b/examples/docker/02_dind/entrypoint.sh deleted file mode 100755 index 38ac3318..00000000 --- a/examples/docker/02_dind/entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -nohup dockerd > /var/log/docker.log 2>&1 & - -exec bash --login \ No newline at end of file diff --git a/examples/docker/02_dind/on-create.sh b/examples/docker/02_dind/on-create.sh new file mode 100755 index 00000000..0425c6ff --- /dev/null +++ b/examples/docker/02_dind/on-create.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Start Docker in the background +sudo -u root /bin/sh -c 'nohup dockerd 2>&1 > /var/log/docker.log &' + +# Wait for Docker to start +for attempt in $(seq 1 10); do + if [[ $attempt -eq 10 ]]; then + echo "Failed to start Docker" + exit 1 + fi + if [[ ! -e /var/run/docker.sock ]]; then + sleep 1 + else + break + fi +done +# Change the owner of the Docker socket so that the coder user can use it. +# Using `newgrp docker` is kind of annoying. +sudo chown coder:coder /var/run/docker.sock diff --git a/examples/docker/03_dind_feature/Dockerfile b/examples/docker/03_dind_feature/Dockerfile index 12f1c1a0..bae50505 100644 --- a/examples/docker/03_dind_feature/Dockerfile +++ b/examples/docker/03_dind_feature/Dockerfile @@ -1,3 +1,18 @@ FROM ubuntu:noble -ADD entrypoint.sh /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file +# Install some dependencies such as curl and sudo. +# Also set up passwordless sudo for the ubuntu user. +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + curl \ + sudo \ + apt-transport-https && \ + echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ubuntu +# Add our onCreateCommand script. +ADD on-create.sh /on-create.sh +# Switch to the non-root user. +USER ubuntu +# The devcontainer feature provides /usr/local/share/docker-init.sh +# which will handle most of the steps of setting up Docker. +# We can't put this in the entrypoint as it gets overridden, so +# we call it in the on-create script. +ENTRYPOINT ["bash"] diff --git a/examples/docker/03_dind_feature/devcontainer.json b/examples/docker/03_dind_feature/devcontainer.json index e1b5a18a..58616a6d 100644 --- a/examples/docker/03_dind_feature/devcontainer.json +++ b/examples/docker/03_dind_feature/devcontainer.json @@ -2,7 +2,8 @@ "build": { "dockerfile": "Dockerfile" }, + "onCreateCommand": "/on-create.sh", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {} } -} \ No newline at end of file +} diff --git a/examples/docker/03_dind_feature/entrypoint.sh b/examples/docker/03_dind_feature/entrypoint.sh deleted file mode 100755 index d18fb7dd..00000000 --- a/examples/docker/03_dind_feature/entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -/usr/local/share/docker-init.sh - -exec bash --login \ No newline at end of file diff --git a/examples/docker/03_dind_feature/on-create.sh b/examples/docker/03_dind_feature/on-create.sh new file mode 100755 index 00000000..d75a93a1 --- /dev/null +++ b/examples/docker/03_dind_feature/on-create.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Run the devcontainer init script. This needs to be +# run as root. +sudo /usr/local/share/docker-init.sh From 482a21283ed7d048e2ed430edc092bf9da45cc61 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 15:16:13 +0100 Subject: [PATCH 2/7] fix group membership --- examples/docker/02_dind/on-create.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docker/02_dind/on-create.sh b/examples/docker/02_dind/on-create.sh index 0425c6ff..9e80a7eb 100755 --- a/examples/docker/02_dind/on-create.sh +++ b/examples/docker/02_dind/on-create.sh @@ -19,4 +19,4 @@ for attempt in $(seq 1 10); do done # Change the owner of the Docker socket so that the coder user can use it. # Using `newgrp docker` is kind of annoying. -sudo chown coder:coder /var/run/docker.sock +sudo chown coder:docker /var/run/docker.sock From cd83f04193447d29abf84fd79393a9317e7b1b84 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 16:31:44 +0100 Subject: [PATCH 3/7] update rootless example --- examples/docker/04_dind_rootless/Dockerfile | 3 ++- examples/docker/04_dind_rootless/devcontainer.json | 5 +++-- .../docker/04_dind_rootless/{entrypoint.sh => on-create.sh} | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename examples/docker/04_dind_rootless/{entrypoint.sh => on-create.sh} (100%) diff --git a/examples/docker/04_dind_rootless/Dockerfile b/examples/docker/04_dind_rootless/Dockerfile index 5358ce60..591250dd 100644 --- a/examples/docker/04_dind_rootless/Dockerfile +++ b/examples/docker/04_dind_rootless/Dockerfile @@ -3,6 +3,7 @@ FROM ubuntu:noble ENV XDG_RUNTIME_DIR /run/user/1000 ENV DOCKER_HOST unix:///${XDG_RUNTIME_DIR}/docker.sock # Setup as root +USER root RUN apt-get update && \ # Install prerequisites apt-get install -y apt-transport-https curl iproute2 uidmap && \ @@ -20,5 +21,5 @@ RUN dockerd-rootless-setuptool.sh install && \ docker context use rootless && \ mkdir -p /home/ubuntu/.local/share/docker # Add our custom entrypoint -ADD entrypoint.sh /entrypoint.sh +ADD on-create.sh /on-create.sh ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/examples/docker/04_dind_rootless/devcontainer.json b/examples/docker/04_dind_rootless/devcontainer.json index 1933fd86..6649501c 100644 --- a/examples/docker/04_dind_rootless/devcontainer.json +++ b/examples/docker/04_dind_rootless/devcontainer.json @@ -1,5 +1,6 @@ { "build": { "dockerfile": "Dockerfile" - } -} \ No newline at end of file + }, + "onCreateCommand": "/on-create.sh" +} diff --git a/examples/docker/04_dind_rootless/entrypoint.sh b/examples/docker/04_dind_rootless/on-create.sh similarity index 100% rename from examples/docker/04_dind_rootless/entrypoint.sh rename to examples/docker/04_dind_rootless/on-create.sh From e7139d9d91d7a5afd1447fdad9b2e346f56cd307 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 18:19:03 +0100 Subject: [PATCH 4/7] address code review comments --- docs/docker.md | 10 ++++++++-- examples/docker/02_dind/Dockerfile | 15 ++++++++++----- examples/docker/02_dind/on-create.sh | 12 ++++++------ examples/docker/03_dind_feature/Dockerfile | 4 ++++ examples/docker/03_dind_feature/on-create.sh | 9 +++++++-- examples/docker/04_dind_rootless/Dockerfile | 8 ++++++-- examples/docker/04_dind_rootless/on-create.sh | 4 +--- 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 0c0a4107..7c353768 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -10,7 +10,7 @@ from inside Envbuilder. > agent startup script in your template. > For example: > -> ``` +> ```terraform > resource "coder_agent" "dev" { > ... > startup_script = <<-EOT @@ -76,7 +76,13 @@ The above can also be accomplished using the [`docker-in-docker` Devcontainer feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docker). > Note: we still need `onCreateCommand` to start the docker startup script. -> See https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L60 +> See +> [here](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L65) +> for more details. +> +> Known issue: for some reason, you still need `sudo` to execute Docker +> commands despite the permissions on the socket being set. More investigation +> required. Example: diff --git a/examples/docker/02_dind/Dockerfile b/examples/docker/02_dind/Dockerfile index 4f38da6a..aa29519b 100644 --- a/examples/docker/02_dind/Dockerfile +++ b/examples/docker/02_dind/Dockerfile @@ -1,18 +1,23 @@ FROM ubuntu:noble + # Install Docker using Docker's convenience script. RUN apt-get update && \ apt-get install -y curl sudo apt-transport-https && \ curl -fsSL https://get.docker.com/ | sh -s - -# Add a non-root user with sudo privileges and allow -# passwordless sudo for the user. + +# The ubuntu:noble image includes a non-root user by default, +# but it does not have sudo privileges. We need to set this up. # Note: we chown /var/run/docker.sock to the non-root user # in the onCreateCommand script. Ideally you would add the # non-root user to the docker group, but in this scenario # this is a 'single-user' environment. It also avoids us # having to run `newgrp docker`. -RUN useradd -m -s /bin/bash -G sudo coder && \ - echo "coder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/coder +RUN echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ubuntu + # Add our onCreateCommand script. ADD on-create.sh /on-create.sh + # Switch to the non-root user. -USER coder +USER ubuntu + +ENTRYPOINT ["bash"] diff --git a/examples/docker/02_dind/on-create.sh b/examples/docker/02_dind/on-create.sh index 9e80a7eb..8b369e23 100755 --- a/examples/docker/02_dind/on-create.sh +++ b/examples/docker/02_dind/on-create.sh @@ -2,10 +2,10 @@ set -euo pipefail -# Start Docker in the background -sudo -u root /bin/sh -c 'nohup dockerd 2>&1 > /var/log/docker.log &' +# Start Docker in the background. +sudo -u root /bin/sh -c 'nohup dockerd > /var/log/docker.log &' -# Wait for Docker to start +# Wait up to 10 seconds for Docker to start. for attempt in $(seq 1 10); do if [[ $attempt -eq 10 ]]; then echo "Failed to start Docker" @@ -17,6 +17,6 @@ for attempt in $(seq 1 10); do break fi done -# Change the owner of the Docker socket so that the coder user can use it. -# Using `newgrp docker` is kind of annoying. -sudo chown coder:docker /var/run/docker.sock + +# Change the owner of the Docker socket so that the non-root user can use it. +sudo chown ubuntu:docker /var/run/docker.sock diff --git a/examples/docker/03_dind_feature/Dockerfile b/examples/docker/03_dind_feature/Dockerfile index bae50505..49c6646a 100644 --- a/examples/docker/03_dind_feature/Dockerfile +++ b/examples/docker/03_dind_feature/Dockerfile @@ -1,4 +1,5 @@ FROM ubuntu:noble + # Install some dependencies such as curl and sudo. # Also set up passwordless sudo for the ubuntu user. RUN apt-get update && \ @@ -7,10 +8,13 @@ RUN apt-get update && \ sudo \ apt-transport-https && \ echo "ubuntu ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ubuntu + # Add our onCreateCommand script. ADD on-create.sh /on-create.sh + # Switch to the non-root user. USER ubuntu + # The devcontainer feature provides /usr/local/share/docker-init.sh # which will handle most of the steps of setting up Docker. # We can't put this in the entrypoint as it gets overridden, so diff --git a/examples/docker/03_dind_feature/on-create.sh b/examples/docker/03_dind_feature/on-create.sh index d75a93a1..d80083f3 100755 --- a/examples/docker/03_dind_feature/on-create.sh +++ b/examples/docker/03_dind_feature/on-create.sh @@ -2,6 +2,11 @@ set -euo pipefail -# Run the devcontainer init script. This needs to be -# run as root. +# Run the docker init script. This needs to be +# run as root. It will take care of starting the +# daemon and adding the ubuntu user to the docker +# group. sudo /usr/local/share/docker-init.sh + +# Change the owner of the Docker socket so that the non-root user can use it. +sudo chown ubuntu:docker /var/run/docker.sock diff --git a/examples/docker/04_dind_rootless/Dockerfile b/examples/docker/04_dind_rootless/Dockerfile index 591250dd..2d88aa17 100644 --- a/examples/docker/04_dind_rootless/Dockerfile +++ b/examples/docker/04_dind_rootless/Dockerfile @@ -1,7 +1,9 @@ FROM ubuntu:noble + # Based on UID of ubuntu user in container. ENV XDG_RUNTIME_DIR /run/user/1000 ENV DOCKER_HOST unix:///${XDG_RUNTIME_DIR}/docker.sock + # Setup as root USER root RUN apt-get update && \ @@ -20,6 +22,8 @@ USER ubuntu RUN dockerd-rootless-setuptool.sh install && \ docker context use rootless && \ mkdir -p /home/ubuntu/.local/share/docker -# Add our custom entrypoint + +# Add our onCreateCommand script. ADD on-create.sh /on-create.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file + +ENTRYPOINT ["bash"] \ No newline at end of file diff --git a/examples/docker/04_dind_rootless/on-create.sh b/examples/docker/04_dind_rootless/on-create.sh index 6c8a6260..ba2fced5 100755 --- a/examples/docker/04_dind_rootless/on-create.sh +++ b/examples/docker/04_dind_rootless/on-create.sh @@ -3,6 +3,4 @@ set -euo pipefail # Start the rootless docker daemon as a non-root user -nohup rootlesskit --net=slirp4netns --mtu=1500 --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run dockerd > "/tmp/dockerd-rootless.log" 2>&1 & - -exec bash --login \ No newline at end of file +nohup rootlesskit --net=slirp4netns --mtu=1500 --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run dockerd >"/tmp/dockerd-rootless.log" 2>&1 & From e9a45f9850ba9165944e7b8248b05cfcfa43174f Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 18:25:44 +0100 Subject: [PATCH 5/7] 03_dind_feature: apply workaround for /var/run --- docs/docker.md | 6 +++--- examples/docker/03_dind_feature/on-create.sh | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 7c353768..405617b1 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -80,9 +80,9 @@ feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docke > [here](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L65) > for more details. > -> Known issue: for some reason, you still need `sudo` to execute Docker -> commands despite the permissions on the socket being set. More investigation -> required. +> Known issue: `/run` does not get symlinked correctly to `/var/run`. +> To work around this, we create the symlink manually before running +> the script to start the Docker daemon. Example: diff --git a/examples/docker/03_dind_feature/on-create.sh b/examples/docker/03_dind_feature/on-create.sh index d80083f3..96bef1ca 100755 --- a/examples/docker/03_dind_feature/on-create.sh +++ b/examples/docker/03_dind_feature/on-create.sh @@ -2,6 +2,12 @@ set -euo pipefail +# Known issue: Kaniko does not symlink /run => /var/run properly. +# This results in /var/run/ being owned by root:root which interferes +# with accessing the Docker socket even if the permissions are set +# correctly. Workaround: symlink it manually +sudo ln -s /run /var/run + # Run the docker init script. This needs to be # run as root. It will take care of starting the # daemon and adding the ubuntu user to the docker From df95d7d62741085b644bfb7376c4922556717ffa Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 18:29:35 +0100 Subject: [PATCH 6/7] fixup! 03_dind_feature: apply workaround for /var/run --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index 405617b1..3cd9e26e 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -75,7 +75,7 @@ docker run -it --rm \ The above can also be accomplished using the [`docker-in-docker` Devcontainer feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docker). -> Note: we still need `onCreateCommand` to start the docker startup script. +> Note: we still need the `onCreateCommand` to start the docker startup script. > See > [here](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L65) > for more details. From c11e0e3ae9461560e8582e09f36c977245c367ac Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Sep 2024 18:29:57 +0100 Subject: [PATCH 7/7] fixup! fixup! 03_dind_feature: apply workaround for /var/run --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index 3cd9e26e..56ce9d05 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -75,7 +75,7 @@ docker run -it --rm \ The above can also be accomplished using the [`docker-in-docker` Devcontainer feature](https://github.com/devcontainers/features/tree/main/src/docker-in-docker). -> Note: we still need the `onCreateCommand` to start the docker startup script. +> Note: we still need the `onCreateCommand` to start Docker. > See > [here](https://github.com/devcontainers/features/blob/main/src/docker-in-docker/devcontainer-feature.json#L65) > for more details.