From b5243aaa2b9a0de8bcc856f2bac94a974f610ad7 Mon Sep 17 00:00:00 2001 From: MasterofJOKers Date: Sun, 26 Feb 2023 00:24:25 +0100 Subject: [PATCH] Add lxc-debian-userns LXC template script This script can be used as LXC template to install a Debian into a directory using `mmdebstrap`. It's intended use is to create a container filesystem with appropriate uids/gids for use in an unprivileged container. The script assumes that it runs as root and only changes the userns to install Debian. --- lxc/lxc-debian-userns | 153 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100755 lxc/lxc-debian-userns diff --git a/lxc/lxc-debian-userns b/lxc/lxc-debian-userns new file mode 100755 index 0000000..fa5b046 --- /dev/null +++ b/lxc/lxc-debian-userns @@ -0,0 +1,153 @@ +#!/bin/sh + +usage() ( + cat <<-EOF + Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME [-lxc-create-options] -- [-template-options] + + Usage: ${1} -h|--help --path= --name= --rootfs= + --uidmap= --gidmap= + --mirror= [--security-mirror=] + [--release=] + [--auth-key=] + + Options : + + -h, --help print this help text + --path=PATH directory where config of this VM will be kept + --name=NAME hostname name of the container + --rootfs=ROOTFS directory where the root filesystem can be accessed + --uidmap=UIDMAP Map of user ids for the userns. Format u:0:1000000:65535 + See lxc-usernsexec (1) for format details. + --gidmap=GIDMAP Map of group ids for the userns. Format g:0:1000000:65535 + See lxc-usernsexec (1) for format details. + --auth-key=KEYFILE SSH public key to inject into the container as the root user. + --release=RELEASE Debian release. Defaults to "stable" + --mirror=MIRROR Debian mirror to use during installation. + --security-mirror=SECURITY_MIRROR + Debian mirror to use for security updates. + + This template uses mmdebstrap to install Debian in a userns into a directory as provided by lxc-create via the + --rootfs option. It's intended use is to create a filesystem for unprivileged containers started as root. + EOF + return 0 +) + + +check_required_binary() ( + prog="${1}" + bin="${2}" + if ! which ${bin} > /dev/null; then + printf "error: Need '%s' executable in PATH to execute this template.\n\n" ${bin} + usage "${prog}" + return 1 + fi + return 0 +) + + +check_required_arg() ( + prog="${1}" + value="${2}" + opt="${3}" + if [ "${value}" = "" ]; then + printf "error: %s is a required option!\n\n" "${opt}" + usage "${prog}" + return 1 + fi + return 0 +) + + +parse_args() { + prog="${0}" + shift + + options=$(getopt -o h -l help,path:,name:,rootfs:,mirror:,security-mirror:,auth-key:,release:,uidmap:,gidmap: -- "${@}") + if [ $? -ne 0 ]; then + usage "${prog}" + exit 1 + fi + # printf "%s\n" "${options}" + + eval set -- "${options}" + + while true; do + case "${1}" in + -h|--help) usage "${prog}" && exit 1;; + --) shift 1; break;; + --path) path=${2}; shift 2;; + --name) name=${2}; shift 2;; + --rootfs) rootfs=${2}; shift 2;; + --release) release=${2}; shift 2;; + --mirror) mirror=${2}; shift 2;; + --security-mirror) security_mirror=${2}; shift 2;; + --auth-key) auth_key=${2}; shift 2;; + --uidmap) uidmap=${2}; shift 2;; + --gidmap) gidmap=${2}; shift 2;; + *) echo "programming error: found unknown opt ${1}"; exit 1; break;; + esac + done + + # check required args + check_required_arg "${prog}" "${path}" "--path" || exit 1 + check_required_arg "${prog}" "${name}" "--name" || exit 1 + check_required_arg "${prog}" "${rootfs}" "--rootfs" || exit 1 + check_required_arg "${prog}" "${mirror}" "--mirror" || exit 1 + check_required_arg "${prog}" "${uidmap}" "--uidmap" || exit 1 + check_required_arg "${prog}" "${gidmap}" "--gidmap" || exit 1 + + # set defaults where necessary + release=${release:-stable} + + if ! [ -d "${rootfs}" ]; then + printf "error: --rootfs must point to a valid directory. Got '%s'\n\n" "${rootfs}" + usage "${prog}" + exit 1 + fi + + if [ "${auth_key}" != "" ] && ! [ -f "${auth_key}" ]; then + printf "error: --auth-key must point to a valid file. Got '%s'\n\n" "${auth_key}" + usage "${prog}" + exit 1 + fi +} + + +chown_mountpoint() ( + # uses $rootfs, $uidmap, $gidmap + root_user_in_ns=$(echo $uidmap | cut -d : -f 3) + root_group_in_ns=$(echo $gidmap | cut -d : -f 3) + echo "Found root user ${root_user_in_ns} and group ${root_group_in_ns}" + chown ${root_user_in_ns}:${root_group_in_ns} "${rootfs}" + return 0 +) + + +install_debian() ( + # uses $rootfs, $name, $release, $uidmap, $gidmap, $mirror, $security_mirror, $auth_key + set -x + + lxc-usernsexec -m ${uidmap} -m ${gidmap} -- \ + lxc-unshare -s 'MOUNT|PID|UTSNAME|IPC' -- \ + mmdebstrap \ + --variant minbase \ + --mode unshare \ + --include 'init ifupdown locales dialog isc-dhcp-client netbase net-tools iproute2 openssh-server python3 vim' \ + --customize-hook 'echo '${name}' > $1/etc/hostname' \ + --customize-hook 'cp /etc/timezone $1/etc/timezone' \ + ${auth_key:+--customize-hook 'mkdir -p $1/root/.ssh' --customize-hook 'cat '${auth_key}' > $1/root/.ssh/authorized_keys'} \ + $release "${rootfs}" \ + "${mirror}" ${security_mirror:+"deb ${security_mirror} ${release}-security main contrib non-free"} +) + + +parse_args "${0}" "${@}" + +check_required_binary "${0}" mmdebstrap || exit 1 +check_required_binary "${0}" lxc-usernsexec || exit 1 + +chown_mountpoint || exit 1 + +install_debian