mirror_lxc/templates/lxc-busybox.in
Stéphane Graber 97f93be72e
meson: Align SPDX license id
Signed-off-by: Stéphane Graber <stgraber@stgraber.org>
2024-04-02 20:42:09 -04:00

329 lines
7.6 KiB
Bash
Executable File

#!/bin/sh
#
# SPDX-License-Identifier: LGPL-2.1+
#
# Client script for LXC container images.
LXC_MAPPED_UID=
LXC_MAPPED_GID=
BUSYBOX_EXE=$(command -v busybox)
# 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
if [ -e /proc/1/uid_map ]; then
if [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ]; then
echo userns-root
return
fi
fi
echo yes
}
USERNS="$(in_userns)"
install_busybox()
{
rootfs="${1}"
name="${2}"
res=0
fstree="\
${rootfs}/selinux \
${rootfs}/dev \
${rootfs}/home \
${rootfs}/root \
${rootfs}/etc \
${rootfs}/etc/init.d \
${rootfs}/bin \
${rootfs}/usr/bin \
${rootfs}/sbin \
${rootfs}/usr/sbin \
${rootfs}/proc \
${rootfs}/sys \
${rootfs}/mnt \
${rootfs}/tmp \
${rootfs}/var/log \
${rootfs}/var/run \
${rootfs}/dev/pts \
${rootfs}/lib \
${rootfs}/usr/lib \
${rootfs}/lib64 \
${rootfs}/usr/lib64"
# shellcheck disable=SC2086
mkdir -p ${fstree} || return 1
# shellcheck disable=SC2086
chmod 755 ${fstree} || return 1
# make /tmp accessible to any user (with sticky bit)
chmod 1777 "${rootfs}/tmp" || return 1
# root user defined
cat <<EOF >> "${rootfs}/etc/passwd"
root:x:0:0:root:/root:/bin/sh
EOF
cat <<EOF >> "${rootfs}/etc/group"
root:x:0:root
EOF
# mount everything
cat <<EOF >> "${rootfs}/etc/init.d/rcS"
#!/bin/sh
/bin/syslogd
/bin/mount -a
/bin/udhcpc
EOF
# executable
chmod 744 "${rootfs}/etc/init.d/rcS" || return 1
# launch rcS first then make a console available
# and propose a shell on the tty, the last one is
# not needed
cat <<EOF >> "${rootfs}/etc/inittab"
::sysinit:/etc/init.d/rcS
tty1::respawn:/bin/getty -L tty1 115200 vt100
console::askfirst:/bin/sh
EOF
# writable and readable for other
chmod 644 "${rootfs}/etc/inittab" || return 1
# Look for the pathname of "default.script" from the help of udhcpc
DEF_SCRIPT=$(${BUSYBOX_EXE} udhcpc --help 2>&1 | egrep -- '-s.*Run PROG' | cut -d'/' -f2- | cut -d')' -f1)
DEF_SCRIPT_DIR=$(dirname /${DEF_SCRIPT})
mkdir -p ${rootfs}/${DEF_SCRIPT_DIR}
chmod 644 ${rootfs}/${DEF_SCRIPT_DIR} || return 1
cat <<EOF >> ${rootfs}/${DEF_SCRIPT}
#!/bin/sh
case "\$1" in
deconfig)
ip addr flush dev \$interface
;;
renew|bound)
# flush all the routes
if [ -n "\$router" ]; then
ip route del default 2> /dev/null
fi
# check broadcast
if [ -n "\$broadcast" ]; then
broadcast="broadcast \$broadcast"
fi
# add a new ip address
ip addr add \$ip/\$mask \$broadcast dev \$interface
if [ -n "\$router" ]; then
ip route add default via \$router dev \$interface
fi
[ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
for i in \$dns ; do
grep "nameserver \$i" /etc/resolv.conf > /dev/null 2>&1
if [ \$? -ne 0 ]; then
echo nameserver \$i >> /etc/resolv.conf
fi
done
;;
esac
exit 0
EOF
chmod 744 ${rootfs}/${DEF_SCRIPT}
return "${res}"
}
configure_busybox()
{
rootfs="${1}"
# copy busybox in the rootfs
if ! cp "${BUSYBOX_EXE}" "${rootfs}/bin"; then
echo "ERROR: Failed to copy busybox binary" 1>&2
return 1
fi
# symlink busybox for the commands it supports
# it would be nice to just use "chroot $rootfs busybox --install -s /bin"
# but that only works right in a chroot with busybox >= 1.19.0
(
cd "${rootfs}/bin" || return 1
./busybox --list | grep -v busybox | xargs -n1 ln -s busybox
)
# relink /sbin/init
ln "${rootfs}/bin/busybox" "${rootfs}/sbin/init"
# /etc/fstab must exist for "mount -a"
touch "${rootfs}/etc/fstab"
# passwd exec must be setuid
chmod +s "${rootfs}/bin/passwd"
touch "${rootfs}/etc/shadow"
return 0
}
copy_configuration()
{
path="${1}"
rootfs="${2}"
name="${3}"
grep -q "^lxc.rootfs.path" "${path}/config" 2>/dev/null || echo "lxc.rootfs.path = ${rootfs}" >> "${path}/config"
cat <<EOF >> "${path}/config"
lxc.signal.halt = SIGUSR1
lxc.signal.reboot = SIGTERM
lxc.uts.name = "${name}"
lxc.autodev = 1
lxc.tty.max = 5
lxc.pty.max = 1
lxc.cap.drop = sys_module mac_admin mac_override sys_time
# When using LXC with apparmor, uncomment the next line to run unconfined:
#lxc.apparmor.profile = unconfined
lxc.mount.auto = cgroup:mixed proc:mixed sys:ro
lxc.mount.entry = shm dev/shm tmpfs defaults,create=dir 0 0
lxc.mount.entry = mqueue dev/mqueue mqueue defaults,optional,create=dir 0 0
EOF
libdirs="\
lib \
usr/lib \
lib64 \
usr/lib64"
for dir in ${libdirs}; do
if [ -d "/${dir}" ] && [ -d "${rootfs}/${dir}" ]; then
echo "lxc.mount.entry = /${dir} ${dir} none ro,bind 0 0" >> "${path}/config"
fi
done
echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >> "${path}/config"
}
remap_userns()
{
path="${1}"
if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
chown "${LXC_MAPPED_UID}" "${path}/config" > /dev/null 2>&1
chown -R root "${path}/rootfs" > /dev/null 2>&1
fi
if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
chgrp "${LXC_MAPPED_GID}" "${path}/config" > /dev/null 2>&1
chgrp -R root "${path}/rootfs" > /dev/null 2>&1
fi
}
usage() {
cat <<EOF
LXC busybox image builder
Special arguments:
[ -h | --help ]: Print this help message and exit.
LXC internal arguments:
[ --name <name> ]: The container name
[ --path <path> ]: The path to the container
[ --rootfs <rootfs> ]: The path to the container's rootfs (default: config or <path>/rootfs)
[ --mapped-uid <map> ]: A uid map (user namespaces)
[ --mapped-gid <map> ]: A gid map (user namespaces)
BUSYBOX template specific arguments:
[ --busybox-path <path> ]: busybox pathname (default: ${BUSYBOX_EXE})
EOF
return 0
}
if ! options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,busybox-path: -- "$@"); then
usage
exit 1
fi
eval set -- "$options"
while true
do
case "$1" in
-h|--help) usage && exit 0;;
-n|--name) name=$2; shift 2;;
-p|--path) path=$2; shift 2;;
--rootfs) rootfs=$2; shift 2;;
--mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
--mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
--busybox-path) BUSYBOX_EXE=$2; shift 2;;
--) shift 1; break ;;
*) break ;;
esac
done
# Check that we have all variables we need
if [ -z "${name}" ] || [ -z "${path}" ]; then
echo "ERROR: Please pass the name and path for the container" 1>&2
exit 1
fi
# Make sure busybox is present
if [ -z "${BUSYBOX_EXE}" ]; then
echo "ERROR: Please pass a pathname for busybox binary" 1>&2
exit 1
fi
if [ ! -x "${BUSYBOX_EXE}" ]; then
echo "ERROR: Failed to find busybox binary (${BUSYBOX_EXE})" 1>&2
exit 1
fi
# detect rootfs
config="$path/config"
if [ -z "$rootfs" ]; then
if grep -q '^lxc.rootfs.path' "${config}" 2> /dev/null ; then
rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}")
else
rootfs="${path}/rootfs"
fi
fi
if ! install_busybox "${rootfs}" "${name}"; then
echo "ERROR: Failed to install rootfs" 1>&2
exit 1
fi
if ! configure_busybox "${rootfs}"; then
echo "ERROR: Failed to configure busybox" 1>&2
exit 1
fi
if ! copy_configuration "${path}" "${rootfs}" "${name}"; then
echo "ERROR: Failed to write config file" 1>&2
exit 1
fi
if ! remap_userns "${path}"; then
echo "ERROR: Failed to change idmappings" 1>&2
exit 1
fi