github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/contrib/dockerd-rootless-setuptool.sh (about) 1 #!/bin/sh 2 # dockerd-rootless-setuptool.sh: setup tool for dockerd-rootless.sh 3 # Needs to be executed as a non-root user. 4 # 5 # Typical usage: dockerd-rootless-setuptool.sh install --force 6 # 7 # Documentation: https://docs.docker.com/engine/security/rootless/ 8 set -eu 9 10 # utility functions 11 INFO() { 12 /bin/echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@" 13 } 14 15 WARNING() { 16 /bin/echo >&2 -e "\e[101m\e[97m[WARNING]\e[49m\e[39m $@" 17 } 18 19 ERROR() { 20 /bin/echo >&2 -e "\e[101m\e[97m[ERROR]\e[49m\e[39m $@" 21 } 22 23 # constants 24 DOCKERD_ROOTLESS_SH="dockerd-rootless.sh" 25 SYSTEMD_UNIT="docker.service" 26 27 # CLI opt: --force 28 OPT_FORCE="" 29 # CLI opt: --skip-iptables 30 OPT_SKIP_IPTABLES="" 31 32 # global vars 33 ARG0="$0" 34 DOCKERD_ROOTLESS_SH_FLAGS="" 35 BIN="" 36 SYSTEMD="" 37 CFG_DIR="" 38 XDG_RUNTIME_DIR_CREATED="" 39 40 # run checks and also initialize global vars 41 init() { 42 # OS verification: Linux only 43 case "$(uname)" in 44 Linux) ;; 45 46 *) 47 ERROR "Rootless Docker cannot be installed on $(uname)" 48 exit 1 49 ;; 50 esac 51 52 # User verification: deny running as root 53 if [ "$(id -u)" = "0" ]; then 54 ERROR "Refusing to install rootless Docker as the root user" 55 exit 1 56 fi 57 58 # set BIN 59 if ! BIN="$(command -v "$DOCKERD_ROOTLESS_SH" 2> /dev/null)"; then 60 ERROR "$DOCKERD_ROOTLESS_SH needs to be present under \$PATH" 61 exit 1 62 fi 63 BIN=$(dirname "$BIN") 64 65 # set SYSTEMD 66 if systemctl --user show-environment > /dev/null 2>&1; then 67 SYSTEMD=1 68 fi 69 70 # HOME verification 71 if [ -z "${HOME:-}" ] || [ ! -d "$HOME" ]; then 72 ERROR "HOME needs to be set" 73 exit 1 74 fi 75 if [ ! -w "$HOME" ]; then 76 ERROR "HOME needs to be writable" 77 exit 1 78 fi 79 80 # set CFG_DIR 81 CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}" 82 83 # Existing rootful docker verification 84 if [ -w /var/run/docker.sock ] && [ -z "$OPT_FORCE" ]; then 85 ERROR "Aborting because rootful Docker (/var/run/docker.sock) is running and accessible. Set --force to ignore." 86 exit 1 87 fi 88 89 # Validate XDG_RUNTIME_DIR and set XDG_RUNTIME_DIR_CREATED 90 if [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -w "$XDG_RUNTIME_DIR" ]; then 91 if [ -n "$SYSTEMD" ]; then 92 ERROR "Aborting because systemd was detected but XDG_RUNTIME_DIR (\"$XDG_RUNTIME_DIR\") is not set, does not exist, or is not writable" 93 ERROR "Hint: this could happen if you changed users with 'su' or 'sudo'. To work around this:" 94 ERROR "- try again by first running with root privileges 'loginctl enable-linger <user>' where <user> is the unprivileged user and export XDG_RUNTIME_DIR to the value of RuntimePath as shown by 'loginctl show-user <user>'" 95 ERROR "- or simply log back in as the desired unprivileged user (ssh works for remote machines, machinectl shell works for local machines)" 96 exit 1 97 fi 98 export XDG_RUNTIME_DIR="/tmp/docker-$(id -u)" 99 mkdir -p "$XDG_RUNTIME_DIR" 100 XDG_RUNTIME_DIR_CREATED=1 101 fi 102 103 instructions="" 104 # instruction: uidmap dependency check 105 if ! command -v newuidmap > /dev/null 2>&1; then 106 if command -v apt-get > /dev/null 2>&1; then 107 instructions=$( 108 cat <<- EOI 109 ${instructions} 110 # Install newuidmap & newgidmap binaries 111 apt-get install -y uidmap 112 EOI 113 ) 114 elif command -v dnf > /dev/null 2>&1; then 115 instructions=$( 116 cat <<- EOI 117 ${instructions} 118 # Install newuidmap & newgidmap binaries 119 dnf install -y shadow-utils 120 EOI 121 ) 122 elif command -v yum > /dev/null 2>&1; then 123 instructions=$( 124 cat <<- EOI 125 ${instructions} 126 # Install newuidmap & newgidmap binaries 127 yum install -y shadow-utils 128 EOI 129 ) 130 else 131 ERROR "newuidmap binary not found. Please install with a package manager." 132 exit 1 133 fi 134 fi 135 136 # instruction: iptables dependency check 137 faced_iptables_error="" 138 if ! command -v iptables > /dev/null 2>&1 && [ ! -f /sbin/iptables ] && [ ! -f /usr/sbin/iptables ]; then 139 faced_iptables_error=1 140 if [ -z "$OPT_SKIP_IPTABLES" ]; then 141 if command -v apt-get > /dev/null 2>&1; then 142 instructions=$( 143 cat <<- EOI 144 ${instructions} 145 # Install iptables 146 apt-get install -y iptables 147 EOI 148 ) 149 elif command -v dnf > /dev/null 2>&1; then 150 instructions=$( 151 cat <<- EOI 152 ${instructions} 153 # Install iptables 154 dnf install -y iptables 155 EOI 156 ) 157 elif command -v yum > /dev/null 2>&1; then 158 instructions=$( 159 cat <<- EOI 160 ${instructions} 161 # Install iptables 162 yum install -y iptables 163 EOI 164 ) 165 else 166 ERROR "iptables binary not found. Please install with a package manager." 167 exit 1 168 fi 169 fi 170 fi 171 172 # instruction: ip_tables module dependency check 173 if ! grep -q ip_tables /proc/modules 2> /dev/null && ! grep -q ip_tables /lib/modules/$(uname -r)/modules.builtin 2> /dev/null; then 174 faced_iptables_error=1 175 if [ -z "$OPT_SKIP_IPTABLES" ]; then 176 instructions=$( 177 cat <<- EOI 178 ${instructions} 179 # Load ip_tables module 180 modprobe ip_tables 181 EOI 182 ) 183 fi 184 fi 185 186 # set DOCKERD_ROOTLESS_SH_FLAGS 187 if [ -n "$faced_iptables_error" ] && [ -n "$OPT_SKIP_IPTABLES" ]; then 188 DOCKERD_ROOTLESS_SH_FLAGS="${DOCKERD_ROOTLESS_SH_FLAGS} --iptables=false" 189 fi 190 191 # instruction: Debian and Arch require setting unprivileged_userns_clone 192 if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then 193 if [ "1" != "$(cat /proc/sys/kernel/unprivileged_userns_clone)" ]; then 194 instructions=$( 195 cat <<- EOI 196 ${instructions} 197 # Set kernel.unprivileged_userns_clone 198 cat <<EOT > /etc/sysctl.d/50-rootless.conf 199 kernel.unprivileged_userns_clone = 1 200 EOT 201 sysctl --system 202 EOI 203 ) 204 fi 205 fi 206 207 # instruction: RHEL/CentOS 7 requires setting max_user_namespaces 208 if [ -f /proc/sys/user/max_user_namespaces ]; then 209 if [ "0" = "$(cat /proc/sys/user/max_user_namespaces)" ]; then 210 instructions=$( 211 cat <<- EOI 212 ${instructions} 213 # Set user.max_user_namespaces 214 cat <<EOT > /etc/sysctl.d/51-rootless.conf 215 user.max_user_namespaces = 28633 216 EOT 217 sysctl --system 218 EOI 219 ) 220 fi 221 fi 222 223 # instructions: validate subuid/subgid files for current user 224 if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subuid 2> /dev/null; then 225 instructions=$( 226 cat <<- EOI 227 ${instructions} 228 # Add subuid entry for $(id -un) 229 echo "$(id -un):100000:65536" >> /etc/subuid 230 EOI 231 ) 232 fi 233 if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subgid 2> /dev/null; then 234 instructions=$( 235 cat <<- EOI 236 ${instructions} 237 # Add subgid entry for $(id -un) 238 echo "$(id -un):100000:65536" >> /etc/subgid 239 EOI 240 ) 241 fi 242 243 # fail with instructions if requirements are not satisfied. 244 if [ -n "$instructions" ]; then 245 ERROR "Missing system requirements. Run the following commands to" 246 ERROR "install the requirements and run this tool again." 247 if [ -n "$faced_iptables_error" ] && [ -z "$OPT_SKIP_IPTABLES" ]; then 248 ERROR "Alternatively iptables checks can be disabled with --skip-iptables ." 249 fi 250 echo 251 echo "########## BEGIN ##########" 252 echo "sudo sh -eux <<EOF" 253 echo "$instructions" | sed -e '/^$/d' 254 echo "EOF" 255 echo "########## END ##########" 256 echo 257 exit 1 258 fi 259 # TODO: support printing non-essential but recommended instructions: 260 # - sysctl: "net.ipv4.ping_group_range" 261 # - sysctl: "net.ipv4.ip_unprivileged_port_start" 262 # - external binary: slirp4netns 263 # - external binary: fuse-overlayfs 264 } 265 266 # CLI subcommand: "check" 267 cmd_entrypoint_check() { 268 # requirements are already checked in init() 269 INFO "Requirements are satisfied" 270 } 271 272 # install (systemd) 273 install_systemd() { 274 mkdir -p "${CFG_DIR}/systemd/user" 275 unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}" 276 if [ -f "${unit_file}" ]; then 277 WARNING "File already exists, skipping: ${unit_file}" 278 else 279 INFO "Creating ${unit_file}" 280 cat <<- EOT > "${unit_file}" 281 [Unit] 282 Description=Docker Application Container Engine (Rootless) 283 Documentation=https://docs.docker.com/engine/security/rootless/ 284 285 [Service] 286 Environment=PATH=$BIN:/sbin:/usr/sbin:$PATH 287 ExecStart=$BIN/dockerd-rootless.sh $DOCKERD_ROOTLESS_SH_FLAGS 288 ExecReload=/bin/kill -s HUP \$MAINPID 289 TimeoutSec=0 290 RestartSec=2 291 Restart=always 292 StartLimitBurst=3 293 StartLimitInterval=60s 294 LimitNOFILE=infinity 295 LimitNPROC=infinity 296 LimitCORE=infinity 297 TasksMax=infinity 298 Delegate=yes 299 Type=simple 300 301 [Install] 302 WantedBy=default.target 303 EOT 304 systemctl --user daemon-reload 305 fi 306 if ! systemctl --user --no-pager status "${SYSTEMD_UNIT}" > /dev/null 2>&1; then 307 INFO "starting systemd service ${SYSTEMD_UNIT}" 308 ( 309 set -x 310 systemctl --user start "${SYSTEMD_UNIT}" 311 sleep 3 312 ) 313 fi 314 ( 315 set -x 316 systemctl --user --no-pager --full status "${SYSTEMD_UNIT}" 317 DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" $BIN/docker version 318 systemctl --user enable "${SYSTEMD_UNIT}" 319 ) 320 INFO "Installed ${SYSTEMD_UNIT} successfully." 321 INFO "To control ${SYSTEMD_UNIT}, run: \`systemctl --user (start|stop|restart) ${SYSTEMD_UNIT}\`" 322 INFO "To run ${SYSTEMD_UNIT} on system startup, run: \`sudo loginctl enable-linger $(id -un)\`" 323 echo 324 } 325 326 # install (non-systemd) 327 install_nonsystemd() { 328 INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be started manually:" 329 echo 330 echo "PATH=$BIN:/sbin:/usr/sbin:\$PATH ${DOCKERD_ROOTLESS_SH} ${DOCKERD_ROOTLESS_SH_FLAGS}" 331 echo 332 } 333 334 # CLI subcommand: "install" 335 cmd_entrypoint_install() { 336 # requirements are already checked in init() 337 if [ -z "$SYSTEMD" ]; then 338 install_nonsystemd 339 else 340 install_systemd 341 fi 342 343 INFO "Make sure the following environment variables are set (or add them to ~/.bashrc):" 344 echo 345 if [ -n "$XDG_RUNTIME_DIR_CREATED" ]; then 346 echo "export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}" 347 fi 348 echo "export PATH=${BIN}:\$PATH" 349 echo "export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/docker.sock" 350 echo 351 352 } 353 354 # CLI subcommand: "uninstall" 355 cmd_entrypoint_uninstall() { 356 # requirements are already checked in init() 357 if [ -z "$SYSTEMD" ]; then 358 INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be stopped manually:" 359 else 360 unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}" 361 ( 362 set -x 363 systemctl --user stop "${SYSTEMD_UNIT}" 364 ) || : 365 ( 366 set -x 367 systemctl --user disable "${SYSTEMD_UNIT}" 368 ) || : 369 rm -f "${unit_file}" 370 INFO "Uninstalled ${SYSTEMD_UNIT}" 371 fi 372 373 INFO "This uninstallation tool does NOT remove Docker binaries and data." 374 INFO "To remove data, run: \`$BIN/rootlesskit rm -rf $HOME/.local/share/docker\`" 375 } 376 377 # text for --help 378 usage() { 379 echo "Usage: ${ARG0} [OPTIONS] COMMAND" 380 echo 381 echo "A setup tool for Rootless Docker (${DOCKERD_ROOTLESS_SH})." 382 echo 383 echo "Documentation: https://docs.docker.com/engine/security/rootless/" 384 echo 385 echo "Options:" 386 echo " -f, --force Ignore rootful Docker (/var/run/docker.sock)" 387 echo " --skip-iptables Ignore missing iptables" 388 echo 389 echo "Commands:" 390 echo " check Check prerequisites" 391 echo " install Install systemd unit (if systemd is available) and show how to manage the service" 392 echo " uninstall Uninstall systemd unit" 393 } 394 395 # parse CLI args 396 if ! args="$(getopt -o hf --long help,force,skip-iptables -n "$ARG0" -- "$@")"; then 397 usage 398 exit 1 399 fi 400 eval set -- "$args" 401 while [ "$#" -gt 0 ]; do 402 arg="$1" 403 shift 404 case "$arg" in 405 -h | --help) 406 usage 407 exit 0 408 ;; 409 -f | --force) 410 OPT_FORCE=1 411 ;; 412 --skip-iptables) 413 OPT_SKIP_IPTABLES=1 414 ;; 415 --) 416 break 417 ;; 418 *) 419 # XXX this means we missed something in our "getopt" arguments above! 420 ERROR "Scripting error, unknown argument '$arg' when parsing script arguments." 421 exit 1 422 ;; 423 esac 424 done 425 426 command="${1:-}" 427 if [ -z "$command" ]; then 428 ERROR "No command was specified. Run with --help to see the usage. Maybe you want to run \`$ARG0 install\`?" 429 exit 1 430 fi 431 432 if ! command -v "cmd_entrypoint_${command}" > /dev/null 2>&1; then 433 ERROR "Unknown command: ${command}. Run with --help to see the usage." 434 exit 1 435 fi 436 437 # main 438 init 439 "cmd_entrypoint_${command}"