From 6bc41a928d70e57be6afb3fa53e686191c5018d2 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 29 Apr 2023 13:13:07 +0300 Subject: [PATCH] Debian Bullseye + node.js + DinD image --- Dockerfile | 27 ++++++ build.sh | 3 + dind | 45 ++++++++++ dockerd-entrypoint.sh | 200 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 Dockerfile create mode 100755 build.sh create mode 100755 dind create mode 100755 dockerd-entrypoint.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..efb83c7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Debian Bullseye + node.js + Docker-in-Docker + +FROM debian:bullseye + +ENV SUDO_GROUP=sudo DOCKER_GROUP=docker DOCKER_TLS_CERTDIR=/certs + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends git docker.io nodejs python3 ca-certificates net-tools openssl pigz xz-utils; \ + rm -rf /var/lib/apt/lists/* + +RUN set -eux; \ + addgroup --system dockremap; \ + adduser --system --ingroup dockremap dockremap; \ + echo 'dockremap:165536:65536' >> /etc/subuid; \ + echo 'dockremap:165536:65536' >> /etc/subgid + +# https://github.com/docker/docker/tree/master/hack/dind +ADD dind /usr/local/bin/dind + +COPY dockerd-entrypoint.sh /usr/local/bin/ + +VOLUME /var/lib/docker +EXPOSE 2375 2376 + +ENTRYPOINT ["dockerd-entrypoint.sh"] +CMD [] diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7d6913a --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t yourcmc.ru/vitalif/gitea-ci-dind . diff --git a/dind b/dind new file mode 100755 index 0000000..087270a --- /dev/null +++ b/dind @@ -0,0 +1,45 @@ +#!/bin/sh +set -e + +# DinD: a wrapper script which allows docker to be run inside a docker container. +# Original version by Jerome Petazzoni +# See the blog post: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/ +# +# This script should be executed inside a docker container in privileged mode +# ('docker run --privileged', introduced in docker 0.6). + +# Usage: dind CMD [ARG...] + +# apparmor sucks and Docker needs to know that it's in a container (c) @tianon +export container=docker + +if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then + mount -t securityfs none /sys/kernel/security || { + echo >&2 'Could not mount /sys/kernel/security.' + echo >&2 'AppArmor detection and --privileged mode might break.' + } +fi + +# Mount /tmp (conditionally) +if ! mountpoint -q /tmp; then + mount -t tmpfs none /tmp +fi + +# cgroup v2: enable nesting +if [ -f /sys/fs/cgroup/cgroup.controllers ]; then + # move the processes from the root group to the /init group, + # otherwise writing subtree_control fails with EBUSY. + # An error during moving non-existent process (i.e., "cat") is ignored. + mkdir -p /sys/fs/cgroup/init + xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : + # enable controllers + sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ + > /sys/fs/cgroup/cgroup.subtree_control +fi + +if [ $# -gt 0 ]; then + exec "$@" +fi + +echo >&2 'ERROR: No command specified.' +echo >&2 'You probably want to run hack/make.sh, or maybe a shell?' diff --git a/dockerd-entrypoint.sh b/dockerd-entrypoint.sh new file mode 100755 index 0000000..0dd29c1 --- /dev/null +++ b/dockerd-entrypoint.sh @@ -0,0 +1,200 @@ +#!/bin/sh +set -eu + +_tls_ensure_private() { + local f="$1"; shift + [ -s "$f" ] || openssl genrsa -out "$f" 4096 +} +_tls_san() { + { + ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }' + { + cat /etc/hostname + echo 'docker' + echo 'localhost' + hostname -f + hostname -s + } | sed 's/^/DNS:/' + [ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN" + } | sort -u | xargs printf '%s,' | sed "s/,\$//" +} +_tls_generate_certs() { + local dir="$1"; shift + + # if server/{ca,key,cert}.pem && !ca/key.pem, do NOTHING except verify (user likely managing CA themselves) + # if ca/key.pem || !ca/cert.pem, generate CA public if necessary + # if ca/key.pem, generate server public + # if ca/key.pem, generate client public + # (regenerating public certs every startup to account for SAN/IP changes and/or expiration) + + if [ -s "$dir/server/ca.pem" ] && [ -s "$dir/server/cert.pem" ] && [ -s "$dir/server/key.pem" ] && [ ! -s "$dir/ca/key.pem" ]; then + openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem" + return 0 + fi + + # https://github.com/FiloSottile/mkcert/issues/174 + local certValidDays='825' + + if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then + # if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA + mkdir -p "$dir/ca" + _tls_ensure_private "$dir/ca/key.pem" + openssl req -new -key "$dir/ca/key.pem" \ + -out "$dir/ca/cert.pem" \ + -subj '/CN=docker:dind CA' -x509 -days "$certValidDays" + fi + + if [ -s "$dir/ca/key.pem" ]; then + # if we have a CA private key, we should create/manage a server key + mkdir -p "$dir/server" + _tls_ensure_private "$dir/server/key.pem" + openssl req -new -key "$dir/server/key.pem" \ + -out "$dir/server/csr.pem" \ + -subj '/CN=docker:dind server' + cat > "$dir/server/openssl.cnf" <<-EOF + [ x509_exts ] + subjectAltName = $(_tls_san) + EOF + openssl x509 -req \ + -in "$dir/server/csr.pem" \ + -CA "$dir/ca/cert.pem" \ + -CAkey "$dir/ca/key.pem" \ + -CAcreateserial \ + -out "$dir/server/cert.pem" \ + -days "$certValidDays" \ + -extfile "$dir/server/openssl.cnf" \ + -extensions x509_exts + cp "$dir/ca/cert.pem" "$dir/server/ca.pem" + openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem" + fi + + if [ -s "$dir/ca/key.pem" ]; then + # if we have a CA private key, we should create/manage a client key + mkdir -p "$dir/client" + _tls_ensure_private "$dir/client/key.pem" + chmod 0644 "$dir/client/key.pem" # openssl defaults to 0600 for the private key, but this one needs to be shared with arbitrary client contexts + openssl req -new \ + -key "$dir/client/key.pem" \ + -out "$dir/client/csr.pem" \ + -subj '/CN=docker:dind client' + cat > "$dir/client/openssl.cnf" <<-'EOF' + [ x509_exts ] + extendedKeyUsage = clientAuth + EOF + openssl x509 -req \ + -in "$dir/client/csr.pem" \ + -CA "$dir/ca/cert.pem" \ + -CAkey "$dir/ca/key.pem" \ + -CAcreateserial \ + -out "$dir/client/cert.pem" \ + -days "$certValidDays" \ + -extfile "$dir/client/openssl.cnf" \ + -extensions x509_exts + cp "$dir/ca/cert.pem" "$dir/client/ca.pem" + openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem" + fi +} + +# no arguments passed +# or first arg is `-f` or `--some-option` +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + # set "dockerSocket" to the default "--host" *unix socket* value (for both standard or rootless) + uid="$(id -u)" + if [ "$uid" = '0' ]; then + dockerSocket='unix:///var/run/docker.sock' + else + # if we're not root, we must be trying to run rootless + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + dockerSocket="unix://$XDG_RUNTIME_DIR/docker.sock" + fi + case "${DOCKER_HOST:-}" in + unix://*) + dockerSocket="$DOCKER_HOST" + ;; + esac + + # add our default arguments + if [ -n "${DOCKER_TLS_CERTDIR:-}" ]; then + _tls_generate_certs "$DOCKER_TLS_CERTDIR" + # generate certs and use TLS if requested/possible (default in 19.03+) + set -- dockerd \ + --host="$dockerSocket" \ + --host=tcp://0.0.0.0:2376 \ + --tlsverify \ + --tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \ + --tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \ + --tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \ + "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2376:2376/tcp" + else + # TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs + set -- dockerd \ + --host="$dockerSocket" \ + --host=tcp://0.0.0.0:2375 \ + "$@" + DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:2375:2375/tcp" + fi +fi + +if [ "$1" = 'dockerd' ]; then + # explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file) + find /run /var/run -iname 'docker*.pid' -delete || : + + if dockerd --version | grep -qF ' 20.10.'; then + # XXX inject "docker-init" (tini) as pid1 to workaround https://github.com/docker-library/docker/issues/318 (zombie container-shim processes) + set -- docker-init -- "$@" + fi + + if ! iptables -nL > /dev/null 2>&1; then + # if iptables fails to run, chances are high the necessary kernel modules aren't loaded (perhaps the host is using nftables with the translating "iptables" wrappers, for example) + # https://github.com/docker-library/docker/issues/350 + # https://github.com/moby/moby/issues/26824 + modprobe ip_tables || : + fi + + uid="$(id -u)" + if [ "$uid" != '0' ]; then + # if we're not root, we must be trying to run rootless + if ! command -v rootlesskit > /dev/null; then + echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)" + exit 1 + fi + user="$(id -un 2>/dev/null || :)" + if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then + echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid" + exit 1 + fi + : "${XDG_RUNTIME_DIR:=/run/user/$uid}" + export XDG_RUNTIME_DIR + if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then + echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid" + exit 1 + fi + if [ -f /proc/sys/kernel/unprivileged_userns_clone ] && unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone)" && [ "$unprivClone" != '1' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1" + exit 1 + fi + if [ -f /proc/sys/user/max_user_namespaces ] && maxUserns="$(cat /proc/sys/user/max_user_namespaces)" && [ "$maxUserns" = '0' ]; then + echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value" + exit 1 + fi + # TODO overlay support detection? + exec rootlesskit \ + --net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \ + --mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \ + --disable-host-loopback \ + --port-driver=builtin \ + --copy-up=/etc \ + --copy-up=/run \ + ${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \ + "$@" + elif [ -x '/usr/local/bin/dind' ]; then + # if we have the (mostly defunct now) Docker-in-Docker wrapper script, use it + set -- '/usr/local/bin/dind' "$@" + fi +else + # if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too + set -- docker-entrypoint.sh "$@" +fi + +exec "$@"