#!/usr/bin/env bash #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- # Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] ENABLE_NONROOT_DOCKER=${1:-"true"} SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"} TARGET_SOCKET=${3:-"/var/run/docker.sock"} USERNAME=${4:-"vscode"} set -e if [ "$(id -u)" -ne 0 ]; then echo -e 'Script must be run a root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' exit 1 fi # Function to run apt-get if needed apt-get-update-if-needed() { if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then echo "Running apt-get update..." apt-get update else echo "Skipping apt-get update." fi } # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive # Install apt-transport-https, curl, lsb-release, gpg if missing if ! dpkg -s apt-transport-https curl ca-certificates lsb-release > /dev/null 2>&1 || ! type gpg > /dev/null 2>&1; then apt-get-update-if-needed apt-get -y install --no-install-recommends apt-transport-https curl ca-certificates lsb-release gnupg2 fi # Install Docker CLI if not already installed if type docker > /dev/null 2>&1; then echo "Docker CLI already installed." else curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | (OUT=$(apt-key add - 2>&1) || echo $OUT) echo "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list apt-get update apt-get -y install --no-install-recommends docker-ce-cli fi # Install Docker Compose if not already installed if type docker-compose > /dev/null 2>&1; then echo "Docker Compose already installed." else LATEST_COMPOSE_VERSION=$(curl -sSL "https://api.github.com/repos/docker/compose/releases/latest" | grep -o -P '(?<="tag_name": ").+(?=")') curl -sSL "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose fi # If init file already exists, exit if [ -f "/usr/local/share/docker-init.sh" ]; then exit 0 fi # By default, make the source and target sockets the same if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then touch "${SOURCE_SOCKET}" ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}" fi # Add a stub if not adding non-root user access, user is root, or the specified user does not exist if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then echo '/usr/bin/env bash -c "\$@"' > /usr/local/share/docker-init.sh chmod +x /usr/local/share/docker-init.sh exit 0 fi # If enabling non-root access and specified user is found, setup socat and add script chown -h "${USERNAME}":root "${TARGET_SOCKET}" if ! dpkg -s socat > /dev/null 2>&1; then apt-get-update-if-needed apt-get -y install socat fi tee /usr/local/share/docker-init.sh > /dev/null \ << EOF #!/usr/bin/env bash #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- set -e SOCAT_PATH_BASE=/tmp/vscr-dind-socat SOCAT_LOG=\${SOCAT_PATH_BASE}.log SOCAT_PID=\${SOCAT_PATH_BASE}.pid # Wrapper function to only use sudo if not already root sudoIf() { if [ "\$(id -u)" -ne 0 ]; then sudo "\$@" else "\$@" fi } # Log messages log() { echo -e "[\$(date)] \$@" | sudoIf tee -a \${SOCAT_LOG} > /dev/null } echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}" # If enabled, try to add a docker group with the right GID. If the group is root, # fall back on using socat to forward the docker socket to another unix socket so # that we can set permissions on it without affecting the host. if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET}) if [ "\${SOCKET_GID}" != "0" ]; then log "Adding user to group with GID \${SOCKET_GID}." if [ "\$(cat /etc/group | grep :\${SOCKET_GID}:)" = "" ]; then sudoIf groupadd --gid \${SOCKET_GID} docker-host fi # Add user to group if not already in it if [ "\$(id ${USERNAME} | grep -E 'groups=.+\${SOCKET_GID}\(')" = "" ]; then sudoIf usermod -aG \${SOCKET_GID} ${USERNAME} fi else # Enable proxy if not already running if [ ! -f "\${SOCAT_PID}" ] || ! ps -p \$(cat \${SOCAT_PID}) > /dev/null; then log "Enabling socket proxy." log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode" sudoIf rm -rf ${TARGET_SOCKET} (sudoIf socat UNIX-LISTEN:${TARGET_SOCKET},fork,mode=660,user=${USERNAME} UNIX-CONNECT:${SOURCE_SOCKET} 2>&1 | sudoIf tee -a \${SOCAT_LOG} > /dev/null & echo "\$!" | sudoIf tee \${SOCAT_PID} > /dev/null) else log "Socket proxy already running." fi fi log "Success" fi # Execute whatever commands were passed in (if any). This allows us # to set this script to ENTRYPOINT while still executing the default CMD. set +e exec "\$@" EOF chmod +x /usr/local/share/docker-init.sh chown ${USERNAME}:root /usr/local/share/docker-init.sh echo "Done!"