Просмотр исходного кода

add dockerd-rootless-setuptool.sh

Usage: `dockerd-rootless-setuptool.sh install --force` .
See `--help` for further information.

Co-authored-by: Tianon Gravi <admwiggin@gmail.com>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Akihiro Suda 5 лет назад
Родитель
Сommit
c696b95269
5 измененных файлов с 443 добавлено и 1 удалено
  1. 1 0
      Dockerfile
  2. 439 0
      contrib/dockerd-rootless-setuptool.sh
  3. 1 0
      hack/make/.binary-setup
  4. 1 1
      hack/make/binary-daemon
  5. 1 0
      hack/make/install-binary

+ 1 - 0
Dockerfile

@@ -234,6 +234,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
     --mount=type=bind,src=hack/dockerfile/install,target=/tmp/install \
         PREFIX=/build /tmp/install/install.sh rootlesskit
 COPY ./contrib/dockerd-rootless.sh /build
+COPY ./contrib/dockerd-rootless-setuptool.sh /build
 
 FROM djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit
 

+ 439 - 0
contrib/dockerd-rootless-setuptool.sh

@@ -0,0 +1,439 @@
+#!/bin/sh
+# dockerd-rootless-setuptool.sh: setup tool for dockerd-rootless.sh
+# Needs to be executed as a non-root user.
+#
+# Typical usage: dockerd-rootless-setuptool.sh install --force
+#
+# Documentation: https://docs.docker.com/engine/security/rootless/
+set -eu
+
+# utility functions
+INFO() {
+	/bin/echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@"
+}
+
+WARNING() {
+	/bin/echo >&2 -e "\e[101m\e[97m[WARNING]\e[49m\e[39m $@"
+}
+
+ERROR() {
+	/bin/echo >&2 -e "\e[101m\e[97m[ERROR]\e[49m\e[39m $@"
+}
+
+# constants
+DOCKERD_ROOTLESS_SH="dockerd-rootless.sh"
+SYSTEMD_UNIT="docker.service"
+
+# CLI opt: --force
+OPT_FORCE=""
+# CLI opt: --skip-iptables
+OPT_SKIP_IPTABLES=""
+
+# global vars
+ARG0="$0"
+DOCKERD_ROOTLESS_SH_FLAGS=""
+BIN=""
+SYSTEMD=""
+CFG_DIR=""
+XDG_RUNTIME_DIR_CREATED=""
+
+# run checks and also initialize global vars
+init() {
+	# OS verification: Linux only
+	case "$(uname)" in
+		Linux) ;;
+
+		*)
+			ERROR "Rootless Docker cannot be installed on $(uname)"
+			exit 1
+			;;
+	esac
+
+	# User verification: deny running as root
+	if [ "$(id -u)" = "0" ]; then
+		ERROR "Refusing to install rootless Docker as the root user"
+		exit 1
+	fi
+
+	# set BIN
+	if ! BIN="$(command -v "$DOCKERD_ROOTLESS_SH" 2> /dev/null)"; then
+		ERROR "$DOCKERD_ROOTLESS_SH needs to be present under \$PATH"
+		exit 1
+	fi
+	BIN=$(dirname "$BIN")
+
+	# set SYSTEMD
+	if systemctl --user show-environment > /dev/null 2>&1; then
+		SYSTEMD=1
+	fi
+
+	# HOME verification
+	if [ -z "${HOME:-}" ] || [ ! -d "$HOME" ]; then
+		ERROR "HOME needs to be set"
+		exit 1
+	fi
+	if [ ! -w "$HOME" ]; then
+		ERROR "HOME needs to be writable"
+		exit 1
+	fi
+
+	# set CFG_DIR
+	CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
+
+	# Existing rootful docker verification
+	if [ -w /var/run/docker.sock ] && [ -z "$OPT_FORCE" ]; then
+		ERROR "Aborting because rootful Docker (/var/run/docker.sock) is running and accessible. Set --force to ignore."
+		exit 1
+	fi
+
+	# Validate XDG_RUNTIME_DIR and set XDG_RUNTIME_DIR_CREATED
+	if [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -w "$XDG_RUNTIME_DIR" ]; then
+		if [ -n "$SYSTEMD" ]; then
+			ERROR "Aborting because systemd was detected but XDG_RUNTIME_DIR (\"$XDG_RUNTIME_DIR\") is not set, does not exist, or is not writable"
+			ERROR "Hint: this could happen if you changed users with 'su' or 'sudo'. To work around this:"
+			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>'"
+			ERROR "- or simply log back in as the desired unprivileged user (ssh works for remote machines, machinectl shell works for local machines)"
+			exit 1
+		fi
+		export XDG_RUNTIME_DIR="/tmp/docker-$(id -u)"
+		mkdir -p "$XDG_RUNTIME_DIR"
+		XDG_RUNTIME_DIR_CREATED=1
+	fi
+
+	instructions=""
+	# instruction: uidmap dependency check
+	if ! command -v newuidmap > /dev/null 2>&1; then
+		if command -v apt-get > /dev/null 2>&1; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Install newuidmap & newgidmap binaries
+					apt-get install -y uidmap
+				EOI
+			)
+		elif command -v dnf > /dev/null 2>&1; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Install newuidmap & newgidmap binaries
+					dnf install -y shadow-utils
+				EOI
+			)
+		elif command -v yum > /dev/null 2>&1; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Install newuidmap & newgidmap binaries
+					yum install -y shadow-utils
+				EOI
+			)
+		else
+			ERROR "newuidmap binary not found. Please install with a package manager."
+			exit 1
+		fi
+	fi
+
+	# instruction: iptables dependency check
+	faced_iptables_error=""
+	if ! command -v iptables > /dev/null 2>&1 && [ ! -f /sbin/iptables ] && [ ! -f /usr/sbin/iptables ]; then
+		faced_iptables_error=1
+		if [ -z "$OPT_SKIP_IPTABLES" ]; then
+			if command -v apt-get > /dev/null 2>&1; then
+				instructions=$(
+					cat <<- EOI
+						${instructions}
+						# Install iptables
+						apt-get install -y iptables
+					EOI
+				)
+			elif command -v dnf > /dev/null 2>&1; then
+				instructions=$(
+					cat <<- EOI
+						${instructions}
+						# Install iptables
+						dnf install -y iptables
+					EOI
+				)
+			elif command -v yum > /dev/null 2>&1; then
+				instructions=$(
+					cat <<- EOI
+						${instructions}
+						# Install iptables
+						yum install -y iptables
+					EOI
+				)
+			else
+				ERROR "iptables binary not found. Please install with a package manager."
+				exit 1
+			fi
+		fi
+	fi
+
+	# instruction: ip_tables module dependency check
+	if ! grep -q ip_tables /proc/modules 2> /dev/null && ! grep -q ip_tables /lib/modules/$(uname -r)/modules.builtin 2> /dev/null; then
+		faced_iptables_error=1
+		if [ -z "$OPT_SKIP_IPTABLES" ]; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Load ip_tables module
+					modprobe ip_tables
+				EOI
+			)
+		fi
+	fi
+
+	# set DOCKERD_ROOTLESS_SH_FLAGS
+	if [ -n "$faced_iptables_error" ] && [ -n "$OPT_SKIP_IPTABLES" ]; then
+		DOCKERD_ROOTLESS_SH_FLAGS="${DOCKERD_ROOTLESS_SH_FLAGS} --iptables=false"
+	fi
+
+	# instruction: Debian and Arch require setting unprivileged_userns_clone
+	if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then
+		if [ "1" != "$(cat /proc/sys/kernel/unprivileged_userns_clone)" ]; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Set kernel.unprivileged_userns_clone
+					cat <<EOT > /etc/sysctl.d/50-rootless.conf
+					kernel.unprivileged_userns_clone = 1
+					EOT
+					sysctl --system
+				EOI
+			)
+		fi
+	fi
+
+	# instruction: RHEL/CentOS 7 requires setting max_user_namespaces
+	if [ -f /proc/sys/user/max_user_namespaces ]; then
+		if [ "0" = "$(cat /proc/sys/user/max_user_namespaces)" ]; then
+			instructions=$(
+				cat <<- EOI
+					${instructions}
+					# Set user.max_user_namespaces
+					cat <<EOT > /etc/sysctl.d/51-rootless.conf
+					user.max_user_namespaces = 28633
+					EOT
+					sysctl --system
+				EOI
+			)
+		fi
+	fi
+
+	# instructions: validate subuid/subgid files for current user
+	if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subuid 2> /dev/null; then
+		instructions=$(
+			cat <<- EOI
+				${instructions}
+				# Add subuid entry for $(id -un)
+				echo "$(id -un):100000:65536" >> /etc/subuid
+			EOI
+		)
+	fi
+	if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subgid 2> /dev/null; then
+		instructions=$(
+			cat <<- EOI
+				${instructions}
+				# Add subgid entry for $(id -un)
+				echo "$(id -un):100000:65536" >> /etc/subgid
+			EOI
+		)
+	fi
+
+	# fail with instructions if requirements are not satisfied.
+	if [ -n "$instructions" ]; then
+		ERROR "Missing system requirements. Run the following commands to"
+		ERROR "install the requirements and run this tool again."
+		if [ -n "$faced_iptables_error" ] && [ -z "$OPT_SKIP_IPTABLES" ]; then
+			ERROR "Alternatively iptables checks can be disabled with --skip-iptables ."
+		fi
+		echo
+		echo "########## BEGIN ##########"
+		echo "sudo sh -eux <<EOF"
+		echo "$instructions" | sed -e '/^$/d'
+		echo "EOF"
+		echo "########## END ##########"
+		echo
+		exit 1
+	fi
+	# TODO: support printing non-essential but recommended instructions:
+	# - sysctl: "net.ipv4.ping_group_range"
+	# - sysctl: "net.ipv4.ip_unprivileged_port_start"
+	# - external binary: slirp4netns
+	# - external binary: fuse-overlayfs
+}
+
+# CLI subcommand: "check"
+cmd_entrypoint_check() {
+	# requirements are already checked in init()
+	INFO "Requirements are satisfied"
+}
+
+# install (systemd)
+install_systemd() {
+	mkdir -p "${CFG_DIR}/systemd/user"
+	unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}"
+	if [ -f "${unit_file}" ]; then
+		WARNING "File already exists, skipping: ${unit_file}"
+	else
+		INFO "Creating ${unit_file}"
+		cat <<- EOT > "${unit_file}"
+			[Unit]
+			Description=Docker Application Container Engine (Rootless)
+			Documentation=https://docs.docker.com/engine/security/rootless/
+
+			[Service]
+			Environment=PATH=$BIN:/sbin:/usr/sbin:$PATH
+			ExecStart=$BIN/dockerd-rootless.sh $DOCKERD_ROOTLESS_SH_FLAGS
+			ExecReload=/bin/kill -s HUP \$MAINPID
+			TimeoutSec=0
+			RestartSec=2
+			Restart=always
+			StartLimitBurst=3
+			StartLimitInterval=60s
+			LimitNOFILE=infinity
+			LimitNPROC=infinity
+			LimitCORE=infinity
+			TasksMax=infinity
+			Delegate=yes
+			Type=simple
+
+			[Install]
+			WantedBy=default.target
+		EOT
+		systemctl --user daemon-reload
+	fi
+	if ! systemctl --user --no-pager status "${SYSTEMD_UNIT}" > /dev/null 2>&1; then
+		INFO "starting systemd service ${SYSTEMD_UNIT}"
+		(
+			set -x
+			systemctl --user start "${SYSTEMD_UNIT}"
+			sleep 3
+		)
+	fi
+	(
+		set -x
+		systemctl --user --no-pager --full status "${SYSTEMD_UNIT}"
+		DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" $BIN/docker version
+		systemctl --user enable "${SYSTEMD_UNIT}"
+	)
+	INFO "Installed ${SYSTEMD_UNIT} successfully."
+	INFO "To control ${SYSTEMD_UNIT}, run: \`systemctl --user (start|stop|restart) ${SYSTEMD_UNIT}\`"
+	INFO "To run ${SYSTEMD_UNIT} on system startup, run: \`sudo loginctl enable-linger $(id -un)\`"
+	echo
+}
+
+# install (non-systemd)
+install_nonsystemd() {
+	INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be started manually:"
+	echo
+	echo "PATH=$BIN:/sbin:/usr/sbin:\$PATH ${DOCKERD_ROOTLESS_SH} ${DOCKERD_ROOTLESS_SH_FLAGS}"
+	echo
+}
+
+# CLI subcommand: "install"
+cmd_entrypoint_install() {
+	# requirements are already checked in init()
+	if [ -z "$SYSTEMD" ]; then
+		install_nonsystemd
+	else
+		install_systemd
+	fi
+
+	INFO "Make sure the following environment variables are set (or add them to ~/.bashrc):"
+	echo
+	if [ -n "$XDG_RUNTIME_DIR_CREATED" ]; then
+		echo "export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}"
+	fi
+	echo "export PATH=${BIN}:\$PATH"
+	echo "export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/docker.sock"
+	echo
+
+}
+
+# CLI subcommand: "uninstall"
+cmd_entrypoint_uninstall() {
+	# requirements are already checked in init()
+	if [ -z "$SYSTEMD" ]; then
+		INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be stopped manually:"
+	else
+		unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}"
+		(
+			set -x
+			systemctl --user stop "${SYSTEMD_UNIT}"
+		) || :
+		(
+			set -x
+			systemctl --user disable "${SYSTEMD_UNIT}"
+		) || :
+		rm -f "${unit_file}"
+		INFO "Uninstalled ${SYSTEMD_UNIT}"
+	fi
+
+	INFO "This uninstallation tool does NOT remove Docker binaries and data."
+	INFO "To remove data, run: \`$BIN/rootlesskit rm -rf $HOME/.local/share/docker\`"
+}
+
+# text for --help
+usage() {
+	echo "Usage: ${ARG0} [OPTIONS] COMMAND"
+	echo
+	echo "A setup tool for Rootless Docker (${DOCKERD_ROOTLESS_SH})."
+	echo
+	echo "Documentation: https://docs.docker.com/engine/security/rootless/"
+	echo
+	echo "Options:"
+	echo "  -f, --force                Ignore rootful Docker (/var/run/docker.sock)"
+	echo "      --skip-iptables        Ignore missing iptables"
+	echo
+	echo "Commands:"
+	echo "  check        Check prerequisites"
+	echo "  install      Install systemd unit (if systemd is available) and show how to manage the service"
+	echo "  uninstall    Uninstall systemd unit"
+}
+
+# parse CLI args
+if ! args="$(getopt -o hf --long help,force,skip-iptables -n "$ARG0" -- "$@")"; then
+	usage
+	exit 1
+fi
+eval set -- "$args"
+while [ "$#" -gt 0 ]; do
+	arg="$1"
+	shift
+	case "$arg" in
+		-h | --help)
+			usage
+			exit 0
+			;;
+		-f | --force)
+			OPT_FORCE=1
+			;;
+		--skip-iptables)
+			OPT_SKIP_IPTABLES=1
+			;;
+		--)
+			break
+			;;
+		*)
+			# XXX this means we missed something in our "getopt" arguments above!
+			ERROR "Scripting error, unknown argument '$arg' when parsing script arguments."
+			exit 1
+			;;
+	esac
+done
+
+command="${1:-}"
+if [ -z "$command" ]; then
+	ERROR "No command was specified. Run with --help to see the usage. Maybe you want to run \`$ARG0 install\`?"
+	exit 1
+fi
+
+if ! command -v "cmd_entrypoint_${command}" > /dev/null 2>&1; then
+	ERROR "Unknown command: ${command}. Run with --help to see the usage."
+	exit 1
+fi
+
+# main
+init
+"cmd_entrypoint_${command}"

+ 1 - 0
hack/make/.binary-setup

@@ -11,3 +11,4 @@ DOCKER_ROOTLESSKIT_BINARY_NAME='rootlesskit'
 DOCKER_ROOTLESSKIT_DOCKER_PROXY_BINARY_NAME='rootlesskit-docker-proxy'
 DOCKER_VPNKIT_BINARY_NAME='vpnkit'
 DOCKER_DAEMON_ROOTLESS_SH_BINARY_NAME='dockerd-rootless.sh'
+DOCKER_DAEMON_ROOTLESS_SETUPTOOL_SH_BINARY_NAME='dockerd-rootless-setuptool.sh'

+ 1 - 1
hack/make/binary-daemon

@@ -14,7 +14,7 @@ copy_binaries() {
 		return
 	fi
 	echo "Copying nested executables into $dir"
-	for file in containerd containerd-shim containerd-shim-runc-v2 ctr runc docker-init docker-proxy rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh; do
+	for file in containerd containerd-shim containerd-shim-runc-v2 ctr runc docker-init docker-proxy rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh dockerd-rootless-setuptool.sh; do
 		cp -f "$(command -v "$file")" "$dir/"
 		if [ "$hash" = "hash" ]; then
 			hash_files "$dir/$file"

+ 1 - 0
hack/make/install-binary

@@ -29,6 +29,7 @@ install_binary() {
 	install_binary "${DEST}/${DOCKER_ROOTLESSKIT_BINARY_NAME}"
 	install_binary "${DEST}/${DOCKER_ROOTLESSKIT_DOCKER_PROXY_BINARY_NAME}"
 	install_binary "${DEST}/${DOCKER_DAEMON_ROOTLESS_SH_BINARY_NAME}"
+	install_binary "${DEST}/${DOCKER_DAEMON_ROOTLESS_SETUPTOOL_SH_BINARY_NAME}"
 	if [ -f "${DEST}/${DOCKER_VPNKIT_BINARY_NAME}" ]; then
 		install_binary "${DEST}/${DOCKER_VPNKIT_BINARY_NAME}"
 	fi