#!/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:,mapped-uid:,mapped-gid: -- "${@}") 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;; --mapped-uid) echo "Cannot run with lxc.idmap set in config. Use --uidmap/--gidmap template options instead."; exit 1; shift 2;; --mapped-gid) echo "Cannot run with lxc.idmap set in config. Use --uidmap/--gidmap template options instead."; exit 1; 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"} ) write_userns_to_config() ( # uses $path, $uidmap, $gidmap printf "lxc.idmap = %s\n" "$(printf "%s" "${uidmap}" | tr ':' ' ')" >> "${path}/config" printf "lxc.idmap = %s\n" "$(printf "%s" "${gidmap}" | tr ':' ' ')" >> "${path}/config" ) parse_args "${0}" "${@}" check_required_binary "${0}" mmdebstrap || exit 1 check_required_binary "${0}" lxc-usernsexec || exit 1 chown_mountpoint || exit 1 install_debian write_userns_to_config