#!/bin/sh # Client script for LXC container images. # # Copyright © 2018 Stéphane Graber # Copyright © 2018 Christian Brauner # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 # USA set -eu LXC_HOOK_DIR="@LXCHOOKDIR@" LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" LXC_NAME= LXC_PATH= LXC_ROOTFS= LXC_CONFIG= MODE="system" COMPAT_LEVEL=5 # Make sure the usual locations are in PATH export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin in_userns() { [ -e /proc/self/uid_map ] || { echo no; return; } while read -r line; do fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')" if [ "${fields}" = "0 0 4294967295" ]; then echo no; return; fi if echo "${fields}" | grep -q " 0 1$"; then echo userns-root; return; fi done < /proc/self/uid_map [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && { echo userns-root; return; } echo yes } usage() { cat < ]: The container name [ --path ]: The path to the container [ --rootfs ]: The path to the container's rootfs [ --mapped-uid ]: A uid map (user namespaces) [ --mapped-gid ]: A gid map (user namespaces) EOF return 0 } if ! options=$(getopt -o hm:f: -l help,metadata:,fstree:,name:,path:,rootfs:,mapped-uid:,mapped-gid: -- "$@"); then usage exit 1 fi eval set -- "$options" while :; do case "$1" in -h|--help) usage && exit 1;; --name) LXC_NAME="$2"; shift 2;; --path) LXC_PATH="$2"; shift 2;; --rootfs) LXC_ROOTFS="$2"; shift 2;; -m|--metadata) LXC_CONFIG="$2"; shift 2;; -f|--fstree) LXC_FSTREE="$2"; shift 2;; --mapped-uid) LXC_MAPPED_UID="$2"; shift 2;; --mapped-gid) LXC_MAPPED_GID="$2"; shift 2;; *) break;; esac done # Check for required binaries for bin in tar xz; do if ! command -V "${bin}" >/dev/null 2>&1; then echo "ERROR: Missing required tool: ${bin}" 1>&2 exit 1 fi done cleanup() { if [ -d "${LOCAL_TEMP}" ]; then rm -Rf "${LOCAL_TEMP}" fi } # Trap all exit signals trap cleanup EXIT HUP INT TERM USERNS="$(in_userns)" if [ "${USERNS}" != "no" ]; then if [ "${USERNS}" = "yes" ]; then if [ -z "${LXC_MAPPED_UID}" ] || [ "${LXC_MAPPED_UID}" = "-1" ]; then echo "ERROR: In a user namespace without a map" 1>&2 exit 1 fi MODE="user" else MODE="user" fi fi relevant_file() { FILE_PATH="${LOCAL_TEMP}/$1" if [ -e "${FILE_PATH}-${MODE}" ]; then FILE_PATH="${FILE_PATH}-${MODE}" fi if [ -e "${FILE_PATH}.${COMPAT_LEVEL}" ]; then FILE_PATH="${FILE_PATH}.${COMPAT_LEVEL}" fi echo "${FILE_PATH}" } # Unpack the rootfs echo "Unpacking the rootfs" # Create temporary directory to if ! command -V mktemp >/dev/null 2>&1; then LOCAL_TEMP=/tmp/lxc-local.$$ mkdir -p "${LOCAL_TEMP}" else LOCAL_TEMP=$(mktemp -d) fi # Unpack file that contains meta.tar.xz if ! tar Jxf "${LXC_CONFIG}" -C "${LOCAL_TEMP}"; then echo "ERROR: Invalid metadata file" 2>&1 exit 1 fi EXCLUDES="" excludelist=$(relevant_file excludes) if [ -f "${excludelist}" ]; then while read -r line; do EXCLUDES="${EXCLUDES} --exclude=${line}" done < "${excludelist}" fi # Do not surround ${EXCLUDES} by quotes. This does not work. The solution could # use array but this is not POSIX compliant. The only POSIX compliant solution # is to use a function wrapper, but the latter can't be used here as the args # are dynamic. We thus need to ignore the warning brought by shellcheck. # shellcheck disable=SC2086 tar --anchored ${EXCLUDES} --numeric-owner -xpJf "${LXC_FSTREE}" -C "${LXC_ROOTFS}" mkdir -p "${LXC_ROOTFS}/dev/pts/" # Setup the configuration # Setup the configuration configfile="$(relevant_file config)" if [ ! -e "${configfile}" ]; then echo "ERROR: meta tarball is missing the configuration file" 1>&2 exit 1 fi ## Extract all the network config entries sed -i -e "/lxc.net.0/{w ${LXC_PATH}/config-network" -e "d}" "${LXC_PATH}/config" ## Extract any other config entry sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" "${LXC_PATH}/config" ## Append the defaults { echo "" echo "# Distribution configuration" cat "$configfile" } >> "${LXC_PATH}/config" ## Add the container-specific config { echo "" echo "# Container specific configuration" if [ -e "${LXC_PATH}/config-auto" ]; then cat "${LXC_PATH}/config-auto" rm "${LXC_PATH}/config-auto" fi } >> "${LXC_PATH}/config" fstab="$(relevant_file fstab)" if [ -e "${fstab}" ]; then echo "lxc.mount.fstab = ${LXC_PATH}/fstab" >> "${LXC_PATH}/config" fi echo "lxc.uts.name = ${LXC_NAME}" >> "${LXC_PATH}/config" ## Re-add the previously removed network config if [ -e "${LXC_PATH}/config-network" ]; then { echo "" echo "# Network configuration" cat "${LXC_PATH}/config-network" rm "${LXC_PATH}/config-network" } >> "${LXC_PATH}/config" fi TEMPLATE_FILES="${LXC_PATH}/config" # Setup the fstab if [ -e "${fstab}" ]; then cp "${fstab}" "${LXC_PATH}/fstab" TEMPLATE_FILES="${TEMPLATE_FILES};${LXC_PATH}/fstab" fi # Look for extra templates if [ -e "$(relevant_file templates)" ]; then while read -r line; do fullpath="${LXC_ROOTFS}/${line}" [ ! -e "${fullpath}" ] && continue TEMPLATE_FILES="${TEMPLATE_FILES};${fullpath}" done < "$(relevant_file templates)" fi # Replace variables in all templates OLD_IFS=${IFS} IFS=";" for file in ${TEMPLATE_FILES}; do [ ! -f "${file}" ] && continue sed -i "s#LXC_NAME#${LXC_NAME}#g" "${file}" sed -i "s#LXC_PATH#${LXC_PATH}#g" "${file}" sed -i "s#LXC_ROOTFS#${LXC_ROOTFS}#g" "${file}" sed -i "s#LXC_TEMPLATE_CONFIG#${LXC_TEMPLATE_CONFIG}#g" "${file}" sed -i "s#LXC_HOOK_DIR#${LXC_HOOK_DIR}#g" "${file}" done IFS=${OLD_IFS} # prevent mingetty from calling vhangup(2) since it fails with userns on CentOS / Oracle if [ -f "${LXC_ROOTFS}/etc/init/tty.conf" ]; then sed -i 's|mingetty|mingetty --nohangup|' "${LXC_ROOTFS}/etc/init/tty.conf" fi if [ -e "$(relevant_file create-message)" ]; then echo "" echo "---" cat "$(relevant_file create-message)" fi exit 0