1
+ #! /usr/bin/env bash
2
+ # -------------------------------------------------------------------------------------------------------------
3
+ # Copyright (c) Microsoft Corporation. All rights reserved.
4
+ # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
5
+ # -------------------------------------------------------------------------------------------------------------
6
+ #
7
+ # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md
8
+ # Maintainer: The VS Code and Codespaces Teams
9
+ #
10
+ # Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby]
11
+
12
+ ENABLE_NONROOT_DOCKER=${1:- " true" }
13
+ SOURCE_SOCKET=${2:- " /var/run/docker-host.sock" }
14
+ TARGET_SOCKET=${3:- " /var/run/docker.sock" }
15
+ USERNAME=${4:- " automatic" }
16
+ USE_MOBY=${5:- " true" }
17
+ MICROSOFT_GPG_KEYS_URI=" https://packages.microsoft.com/keys/microsoft.asc"
18
+
19
+ set -e
20
+
21
+ if [ " $( id -u) " -ne 0 ]; then
22
+ echo -e ' Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
23
+ exit 1
24
+ fi
25
+
26
+ # Determine the appropriate non-root user
27
+ if [ " ${USERNAME} " = " auto" ] || [ " ${USERNAME} " = " automatic" ]; then
28
+ USERNAME=" "
29
+ POSSIBLE_USERS=(" vscode" " node" " codespace" " $( awk -v val=1000 -F " :" ' $3==val{print $1}' /etc/passwd) " )
30
+ for CURRENT_USER in ${POSSIBLE_USERS[@]} ; do
31
+ if id -u ${CURRENT_USER} > /dev/null 2>&1 ; then
32
+ USERNAME=${CURRENT_USER}
33
+ break
34
+ fi
35
+ done
36
+ if [ " ${USERNAME} " = " " ]; then
37
+ USERNAME=root
38
+ fi
39
+ elif [ " ${USERNAME} " = " none" ] || ! id -u ${USERNAME} > /dev/null 2>&1 ; then
40
+ USERNAME=root
41
+ fi
42
+
43
+ # Get central common setting
44
+ get_common_setting () {
45
+ if [ " ${common_settings_file_loaded} " != " true" ]; then
46
+ curl -sfL " https://aka.ms/vscode-dev-containers/script-library/settings.env" 2> /dev/null -o /tmp/vsdc-settings.env || echo " Could not download settings file. Skipping."
47
+ common_settings_file_loaded=true
48
+ fi
49
+ if [ -f " /tmp/vsdc-settings.env" ]; then
50
+ local multi_line=" "
51
+ if [ " $2 " = " true" ]; then multi_line=" -z" ; fi
52
+ local result=" $( grep ${multi_line} -oP " $1 =\" ?\K[^\" ]+" /tmp/vsdc-settings.env | tr -d ' \0' ) "
53
+ if [ ! -z " ${result} " ]; then declare -g $1 =" ${result} " ; fi
54
+ fi
55
+ echo " $1 =${! 1} "
56
+ }
57
+
58
+ # Function to run apt-get if needed
59
+ apt_get_update_if_needed ()
60
+ {
61
+ if [ ! -d " /var/lib/apt/lists" ] || [ " $( ls /var/lib/apt/lists/ | wc -l) " = " 0" ]; then
62
+ echo " Running apt-get update..."
63
+ apt-get update
64
+ else
65
+ echo " Skipping apt-get update."
66
+ fi
67
+ }
68
+
69
+ # Checks if packages are installed and installs them if not
70
+ check_packages () {
71
+ if ! dpkg -s " $@ " > /dev/null 2>&1 ; then
72
+ apt_get_update_if_needed
73
+ apt-get -y install --no-install-recommends " $@ "
74
+ fi
75
+ }
76
+
77
+ # Ensure apt is in non-interactive to avoid prompts
78
+ export DEBIAN_FRONTEND=noninteractive
79
+
80
+ # Install dependencies
81
+ check_packages apt-transport-https curl ca-certificates gnupg2
82
+
83
+ # Install Docker / Moby CLI if not already installed
84
+ if type docker > /dev/null 2>&1 ; then
85
+ echo " Docker / Moby CLI already installed."
86
+ else
87
+ # Source /etc/os-release to get OS info
88
+ . /etc/os-release
89
+ if [ " ${USE_MOBY} " = " true" ]; then
90
+ # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install
91
+ get_common_setting MICROSOFT_GPG_KEYS_URI
92
+ curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
93
+ echo " deb [arch=$( dpkg --print-architecture) signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID} -${VERSION_CODENAME} -prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
94
+ apt-get update
95
+ apt-get -y install --no-install-recommends moby-cli moby-buildx moby-compose
96
+ else
97
+ # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install
98
+ curl -fsSL https://download.docker.com/linux/${ID} /gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg
99
+ echo " deb [arch=$( dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list
100
+ apt-get update
101
+ apt-get -y install --no-install-recommends docker-ce-cli
102
+ fi
103
+ fi
104
+
105
+ # Install Docker Compose if not already installed and is on a supported architecture
106
+ if type docker-compose > /dev/null 2>&1 ; then
107
+ echo " Docker Compose already installed."
108
+ else
109
+ TARGET_COMPOSE_ARCH=" $( uname -m) "
110
+ if [ " ${TARGET_COMPOSE_ARCH} " = " amd64" ]; then
111
+ TARGET_COMPOSE_ARCH=" x86_64"
112
+ fi
113
+ if [ " ${TARGET_COMPOSE_ARCH} " != " x86_64" ]; then
114
+ # Use pip to get a version that runns on this architecture
115
+ if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv pipx > /dev/null 2>&1 ; then
116
+ apt_get_update_if_needed
117
+ apt-get -y install python3-minimal python3-pip libffi-dev python3-venv pipx
118
+ fi
119
+ export PIPX_HOME=/usr/local/pipx
120
+ mkdir -p ${PIPX_HOME}
121
+ export PIPX_BIN_DIR=/usr/local/bin
122
+ export PIP_CACHE_DIR=/tmp/pip-tmp/cache
123
+ pipx install --system-site-packages --pip-args ' --no-cache-dir --force-reinstall' docker-compose
124
+ rm -rf /tmp/pip-tmp
125
+ else
126
+ LATEST_COMPOSE_VERSION=$( basename " $( curl -fsSL -o /dev/null -w " %{url_effective}" https://github.com/docker/compose/releases/latest) " )
127
+ curl -fsSL " https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION} /docker-compose-$( uname -s) -${TARGET_COMPOSE_ARCH} " -o /usr/local/bin/docker-compose
128
+ chmod +x /usr/local/bin/docker-compose
129
+ fi
130
+ fi
131
+
132
+ # If init file already exists, exit
133
+ if [ -f " /usr/local/share/docker-init.sh" ]; then
134
+ exit 0
135
+ fi
136
+
137
+ # By default, make the source and target sockets the same
138
+ if [ " ${SOURCE_SOCKET} " != " ${TARGET_SOCKET} " ]; then
139
+ touch " ${SOURCE_SOCKET} "
140
+ ln -s " ${SOURCE_SOCKET} " " ${TARGET_SOCKET} "
141
+ fi
142
+
143
+ # Add a stub if not adding non-root user access, user is root
144
+ if [ " ${ENABLE_NONROOT_DOCKER} " = " false" ] || [ " ${USERNAME} " = " root" ]; then
145
+ echo ' /usr/bin/env bash -c "\$@"' > /usr/local/share/docker-init.sh
146
+ chmod +x /usr/local/share/docker-init.sh
147
+ exit 0
148
+ fi
149
+
150
+ # If enabling non-root access and specified user is found, setup socat and add script
151
+ chown -h " ${USERNAME} " :root " ${TARGET_SOCKET} "
152
+ if ! dpkg -s socat > /dev/null 2>&1 ; then
153
+ apt_get_update_if_needed
154
+ apt-get -y install socat
155
+ fi
156
+ tee /usr/local/share/docker-init.sh > /dev/null \
157
+ << EOF
158
+ #!/usr/bin/env bash
159
+ #-------------------------------------------------------------------------------------------------------------
160
+ # Copyright (c) Microsoft Corporation. All rights reserved.
161
+ # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
162
+ #-------------------------------------------------------------------------------------------------------------
163
+
164
+ set -e
165
+
166
+ SOCAT_PATH_BASE=/tmp/vscr-docker-from-docker
167
+ SOCAT_LOG=\$ {SOCAT_PATH_BASE}.log
168
+ SOCAT_PID=\$ {SOCAT_PATH_BASE}.pid
169
+
170
+ # Wrapper function to only use sudo if not already root
171
+ sudoIf()
172
+ {
173
+ if [ "\$ (id -u)" -ne 0 ]; then
174
+ sudo "\$ @"
175
+ else
176
+ "\$ @"
177
+ fi
178
+ }
179
+
180
+ # Log messages
181
+ log()
182
+ {
183
+ echo -e "[\$ (date)] \$ @" | sudoIf tee -a \$ {SOCAT_LOG} > /dev/null
184
+ }
185
+
186
+ echo -e "\n** \$ (date) **" | sudoIf tee -a \$ {SOCAT_LOG} > /dev/null
187
+ log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET} "
188
+
189
+ # If enabled, try to add a docker group with the right GID. If the group is root,
190
+ # fall back on using socat to forward the docker socket to another unix socket so
191
+ # that we can set permissions on it without affecting the host.
192
+ if [ "${ENABLE_NONROOT_DOCKER} " = "true" ] && [ "${SOURCE_SOCKET} " != "${TARGET_SOCKET} " ] && [ "${USERNAME} " != "root" ] && [ "${USERNAME} " != "0" ]; then
193
+ SOCKET_GID=\$ (stat -c '%g' ${SOURCE_SOCKET} )
194
+ if [ "\$ {SOCKET_GID}" != "0" ]; then
195
+ log "Adding user to group with GID \$ {SOCKET_GID}."
196
+ if [ "\$ (cat /etc/group | grep :\$ {SOCKET_GID}:)" = "" ]; then
197
+ sudoIf groupadd --gid \$ {SOCKET_GID} docker-host
198
+ fi
199
+ # Add user to group if not already in it
200
+ if [ "\$ (id ${USERNAME} | grep -E "groups.*(=|,)\$ {SOCKET_GID}\(")" = "" ]; then
201
+ sudoIf usermod -aG \$ {SOCKET_GID} ${USERNAME}
202
+ fi
203
+ else
204
+ # Enable proxy if not already running
205
+ if [ ! -f "\$ {SOCAT_PID}" ] || ! ps -p \$ (cat \$ {SOCAT_PID}) > /dev/null; then
206
+ log "Enabling socket proxy."
207
+ log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode"
208
+ sudoIf rm -rf ${TARGET_SOCKET}
209
+ (sudoIf socat -t 3600 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)
210
+ else
211
+ log "Socket proxy already running."
212
+ fi
213
+ fi
214
+ log "Success"
215
+ fi
216
+
217
+ # Execute whatever commands were passed in (if any). This allows us
218
+ # to set this script to ENTRYPOINT while still executing the default CMD.
219
+ set +e
220
+ exec "\$ @"
221
+ EOF
222
+ chmod +x /usr/local/share/docker-init.sh
223
+ chown ${USERNAME} :root /usr/local/share/docker-init.sh
224
+ echo " Done!"
0 commit comments