lxc-busybox: make shellcheck clean

parent 832cb182
#!/bin/bash #!/bin/sh
# Client script for LXC container images.
# #
# lxc: linux Container library # Copyright @ Daniel Lezcano <daniel.lezcano@free.fr>
# Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>
# Authors: #
# Daniel Lezcano <daniel.lezcano@free.fr> # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# This library is free software; you can redistribute it and/or # License as published by the Free Software Foundation; either
# modify it under the terms of the GNU Lesser General Public # version 2.1 of the License, or (at your option) any later version.
# 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, # This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details. # Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public # You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software # License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA
LXC_MAPPED_UID= LXC_MAPPED_UID=
LXC_MAPPED_GID= LXC_MAPPED_GID=
SSH=
# Make sure the usual locations are in PATH # Make sure the usual locations are in PATH
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
am_in_userns() { in_userns() {
[ -e /proc/self/uid_map ] || { echo no; return; } [ -e /proc/self/uid_map ] || { echo no; return; }
[ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } while read -r line; do
line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) fields="$(echo "$line" | awk '{ print $1 " " $2 " " $3 }')"
[ "$line" = "0 0 4294967295" ] && { echo no; return; } if [ "${fields}" = "0 0 4294967295" ]; then
echo yes echo no;
} return;
in_userns=0
[ $(am_in_userns) = "yes" ] && in_userns=1
copy_binary()
{
binary_path=`which $1`
if [ $? -ne 0 ]; then
echo "Unable to find $1 binary on the system"
return 1
fi
dir_path="${binary_path%/*}"
echo /{,usr/}{,s}bin | grep $dir_path >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Binary $1 is located at $binary_path and will not be copied"
echo "($dir_path not supported)"
return 1
fi fi
if echo "${fields}" | grep -q " 0 1$"; then
cp $binary_path $rootfs/$binary_path echo userns-root;
if [ $? -ne 0 ]; then return;
echo "Failed to copy $binary_path to rootfs"
return 1
fi fi
done < /proc/self/uid_map
return 0 [ "$(cat /proc/self/uid_map)" = "$(cat /proc/1/uid_map)" ] && { echo userns-root; return; }
echo yes
} }
USERNS="$(in_userns)"
install_busybox() install_busybox()
{ {
rootfs=$1 rootfs="${1}"
name=$2 name="${2}"
res=0 res=0
tree="\ fstree="\
$rootfs/selinux \ ${rootfs}/selinux \
$rootfs/dev \ ${rootfs}/dev \
$rootfs/home \ ${rootfs}/home \
$rootfs/root \ ${rootfs}/root \
$rootfs/etc \ ${rootfs}/etc \
$rootfs/etc/init.d \ ${rootfs}/etc/init.d \
$rootfs/bin \ ${rootfs}/bin \
$rootfs/usr/bin \ ${rootfs}/usr/bin \
$rootfs/sbin \ ${rootfs}/sbin \
$rootfs/usr/sbin \ ${rootfs}/usr/sbin \
$rootfs/proc \ ${rootfs}/proc \
$rootfs/sys \ ${rootfs}/sys \
$rootfs/mnt \ ${rootfs}/mnt \
$rootfs/tmp \ ${rootfs}/tmp \
$rootfs/var/log \ ${rootfs}/var/log \
$rootfs/usr/share/udhcpc \ ${rootfs}/usr/share/udhcpc \
$rootfs/dev/pts \ ${rootfs}/dev/pts \
$rootfs/dev/shm \ ${rootfs}/dev/shm \
$rootfs/lib \ ${rootfs}/lib \
$rootfs/usr/lib \ ${rootfs}/usr/lib \
$rootfs/lib64 \ ${rootfs}/lib64 \
$rootfs/usr/lib64" ${rootfs}/usr/lib64"
mkdir -p $tree || return 1 # shellcheck disable=SC2086
chmod 755 $tree || return 1 mkdir -p ${fstree} || return 1
# shellcheck disable=SC2086
pushd $rootfs/dev > /dev/null || return 1 chmod 755 ${fstree} || return 1
# minimal devices needed for busybox # minimal devices needed for busybox
if [ $in_userns -eq 1 ]; then if [ "${USERNS}" = "yes" ]; then
for dev in tty console tty0 tty1 ram0 null urandom; do for dev in tty console tty0 tty1 ram0 null urandom; do
echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config echo "lxc.mount.entry = /dev/${dev} dev/${dev} none bind,optional,create=file 0 0" >> "${path}/config"
done done
else else
mknod -m 666 tty c 5 0 || res=1 mknod -m 666 "${rootfs}/tty" c 5 0 || res=1
mknod -m 666 console c 5 1 || res=1 mknod -m 666 "${rootfs}/console" c 5 1 || res=1
mknod -m 666 tty0 c 4 0 || res=1 mknod -m 666 "${rootfs}/tty0" c 4 0 || res=1
mknod -m 666 tty1 c 4 0 || res=1 mknod -m 666 "${rootfs}/tty1" c 4 0 || res=1
mknod -m 666 tty5 c 4 0 || res=1 mknod -m 666 "${rootfs}/tty5" c 4 0 || res=1
mknod -m 600 ram0 b 1 0 || res=1 mknod -m 600 "${rootfs}/ram0" b 1 0 || res=1
mknod -m 666 null c 1 3 || res=1 mknod -m 666 "${rootfs}/null" c 1 3 || res=1
mknod -m 666 zero c 1 5 || res=1 mknod -m 666 "${rootfs}/zero" c 1 5 || res=1
mknod -m 666 urandom c 1 9 || res=1 mknod -m 666 "${rootfs}/urandom" c 1 9 || res=1
fi fi
popd > /dev/null # root user defined
cat <<EOF >> "${rootfs}/etc/passwd"
# root user defined
cat <<EOF >> $rootfs/etc/passwd
root:x:0:0:root:/root:/bin/sh root:x:0:0:root:/root:/bin/sh
EOF EOF
cat <<EOF >> $rootfs/etc/group cat <<EOF >> "${rootfs}/etc/group"
root:x:0:root root:x:0:root
EOF EOF
# mount everything # mount everything
cat <<EOF >> $rootfs/etc/init.d/rcS cat <<EOF >> "${rootfs}/etc/init.d/rcS"
#!/bin/sh #!/bin/sh
/bin/syslogd /bin/syslogd
/bin/mount -a /bin/mount -a
/bin/udhcpc /bin/udhcpc
EOF EOF
# executable # executable
chmod 744 $rootfs/etc/init.d/rcS || return 1 chmod 744 "${rootfs}/etc/init.d/rcS" || return 1
# launch rcS first then make a console available # launch rcS first then make a console available
# and propose a shell on the tty, the last one is # and propose a shell on the tty, the last one is
# not needed # not needed
cat <<EOF >> $rootfs/etc/inittab cat <<EOF >> "${rootfs}/etc/inittab"
::sysinit:/etc/init.d/rcS ::sysinit:/etc/init.d/rcS
tty1::respawn:/bin/getty -L tty1 115200 vt100 tty1::respawn:/bin/getty -L tty1 115200 vt100
console::askfirst:/bin/sh console::askfirst:/bin/sh
EOF EOF
# writable and readable for other # writable and readable for other
chmod 644 $rootfs/etc/inittab || return 1 chmod 644 "${rootfs}/etc/inittab" || return 1
cat <<EOF >> $rootfs/usr/share/udhcpc/default.script cat <<EOF >> "${rootfs}/usr/share/udhcpc/default.script"
#!/bin/sh #!/bin/sh
case "\$1" in case "\$1" in
deconfig) deconfig)
ip addr flush dev \$interface ip addr flush dev \$interface
;; ;;
renew|bound) renew|bound)
# flush all the routes # flush all the routes
if [ -n "\$router" ]; then if [ -n "\$router" ]; then
ip route del default 2> /dev/null ip route del default 2> /dev/null
fi 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
echo nameserver \$i >> /etc/resolv.conf
done
;;
esac
exit 0
EOF
chmod 744 $rootfs/usr/share/udhcpc/default.script # check broadcast
if [ -n "\$broadcast" ]; then
broadcast="broadcast \$broadcast"
fi
return $res # add a new ip address
} ip addr add \$ip/\$mask \$broadcast dev \$interface
install_dropbear() if [ -n "\$router" ]; then
{ ip route add default via \$router dev \$interface
# copy dropbear binary
copy_binary dropbear || return 1
# make symlinks to various ssh utilities
utils="\
$rootfs/usr/bin/dbclient \
$rootfs/usr/bin/scp \
$rootfs/usr/bin/ssh \
$rootfs/usr/sbin/dropbearkey \
$rootfs/usr/sbin/dropbearconvert \
"
echo $utils | xargs -n1 ln -s /usr/sbin/dropbear
# add necessary config files
mkdir $rootfs/etc/dropbear
dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1
dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1
echo "'dropbear' ssh utility installed"
return 0
}
install_openssh()
{
# tools to be installed
server_utils="sshd"
client_utils="\
ssh \
scp \
"
client_optional_utils="\
sftp \
ssh-add \
ssh-agent \
ssh-keygen \
ssh-keyscan \
ssh-argv0 \
ssh-copy-id \
"
# new folders used by ssh
ssh_tree="\
$rootfs/etc/ssh \
$rootfs/var/empty/sshd \
$rootfs/var/lib/empty/sshd \
$rootfs/var/run/sshd \
"
# create folder structure
mkdir -p $ssh_tree
if [ $? -ne 0 ]; then
return 1
fi fi
# copy binaries [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf
for bin in $server_utils $client_utils; do for i in \$dns ; do
copy_binary $bin || return 1 echo nameserver \$i >> /etc/resolv.conf
done
for bin in $client_optional_utils; do
tool_path=`which $bin` && copy_binary $bin
done done
;;
# add user and group esac
cat <<EOF >> $rootfs/etc/passwd exit 0
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
EOF
cat <<EOF >> $rootfs/etc/group
sshd:x:74:
EOF
# generate container keys
ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1
ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1
# by default setup root password with no password
cat <<EOF > $rootfs/etc/ssh/sshd_config
Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
UsePrivilegeSeparation yes
KeyRegenerationInterval 3600
ServerKeyBits 768
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
RhostsRSAAuthentication no
HostbasedAuthentication no
PermitEmptyPasswords yes
ChallengeResponseAuthentication no
EOF EOF
echo "'OpenSSH' utility installed" chmod 744 "${rootfs}/usr/share/udhcpc/default.script"
return 0 return "${res}"
} }
configure_busybox() configure_busybox()
{ {
rootfs=$1 rootfs="${1}"
which busybox >/dev/null 2>&1 if ! which busybox > /dev/null 2>&1; then
echo "ERROR: Failed to find busybox binary"
if [ $? -ne 0 ]; then return 1
echo "busybox executable is not accessible" fi
return 1
fi # copy busybox in the rootfs
if ! cp "$(which busybox)" "${rootfs}/bin"; then
# copy busybox in the rootfs echo "ERROR: Failed to copy busybox binary"
cp $(which busybox) $rootfs/bin return 1
if [ $? -ne 0 ]; then fi
echo "failed to copy busybox in the rootfs"
return 1 # symlink busybox for the commands it supports
fi # 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
# symlink busybox for the commands it supports (
# it would be nice to just use "chroot $rootfs busybox --install -s /bin" cd "${rootfs}/bin" || return 1
# but that only works right in a chroot with busybox >= 1.19.0
pushd $rootfs/bin > /dev/null || return 1
./busybox --help | grep 'Currently defined functions:' -A300 | \ ./busybox --help | grep 'Currently defined functions:' -A300 | \
grep -v 'Currently defined functions:' | tr , '\n' | \ grep -v 'Currently defined functions:' | tr , '\n' | \
xargs -n1 ln -s busybox xargs -n1 ln -s busybox
popd > /dev/null )
# relink /sbin/init # relink /sbin/init
ln $rootfs/bin/busybox $rootfs/sbin/init ln "${rootfs}/bin/busybox" "${rootfs}/sbin/init"
# /etc/fstab must exist for "mount -a" # /etc/fstab must exist for "mount -a"
touch $rootfs/etc/fstab touch "${rootfs}/etc/fstab"
# passwd exec must be setuid # passwd exec must be setuid
chmod +s $rootfs/bin/passwd chmod +s "${rootfs}/bin/passwd"
touch $rootfs/etc/shadow touch "${rootfs}/etc/shadow"
return 0 return 0
} }
copy_configuration() copy_configuration()
{ {
path=$1 path="${1}"
rootfs=$2 rootfs="${2}"
name=$3 name="${3}"
grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config grep -q "^lxc.rootfs.path" "${path}/config" 2>/dev/null || echo "lxc.rootfs.path = ${rootfs}" >> "${path}/config"
cat <<EOF >> $path/config cat <<EOF >> "${path}/config"
lxc.signal.halt = SIGUSR1 lxc.signal.halt = SIGUSR1
lxc.signal.reboot = SIGTERM lxc.signal.reboot = SIGTERM
lxc.uts.name = $name lxc.uts.name = "${name}"
lxc.tty.max = 1 lxc.tty.max = 1
lxc.pty.max = 1 lxc.pty.max = 1
lxc.cap.drop = sys_module mac_admin mac_override sys_time lxc.cap.drop = sys_module mac_admin mac_override sys_time
...@@ -355,130 +227,104 @@ lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed ...@@ -355,130 +227,104 @@ lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0 lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0
EOF EOF
libdirs="\ libdirs="\
lib \ lib \
usr/lib \ usr/lib \
lib64 \ lib64 \
usr/lib64" usr/lib64"
for dir in $libdirs; do for dir in ${libdirs}; do
if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then if [ -d "/${dir}" ] && [ -d "${rootfs}/${dir}" ]; then
echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config echo "lxc.mount.entry = /${dir} ${dir} none ro,bind 0 0" >> "${path}/config"
fi fi
done done
echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >> "${path}/config"
} }
remap_userns() remap_userns()
{ {
path=$1 path="${1}"
if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1 chown "${LXC_MAPPED_UID}" "${path}/config" > /dev/null 2>&1
chown -R root $path/rootfs >/dev/null 2>&1 chown -R root "${path}/rootfs" > /dev/null 2>&1
fi fi
if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then
chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1 chgrp "${LXC_MAPPED_GID}" "${path}/config" > /dev/null 2>&1
chgrp -R root $path/rootfs >/dev/null 2>&1 chgrp -R root "${path}/rootfs" > /dev/null 2>&1
fi fi
} }
usage() usage() {
{ cat <<EOF
cat <<EOF LXC busybox image builder
$1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh}
Special arguments:
[ -h | --help ]: Print this help message and exit.
LXC internal arguments (do not pass manually!):
[ --name <name> ]: The container name
[ --path <path> ]: The path to the container
[ --rootfs <rootfs> ]: The path to the container's rootfs
[ --mapped-uid <map> ]: A uid map (user namespaces)
[ --mapped-gid <map> ]: A gid map (user namespaces)
EOF EOF
return 0 return 0
} }
options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@") if ! options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid: -- "$@"); then
if [ $? -ne 0 ]; then usage
usage $(basename $0) exit 1
exit 1
fi fi
eval set -- "$options" eval set -- "$options"
while true while true
do do
case "$1" in case "$1" in
-h|--help) usage $0 && exit 0;; -h|--help) usage exit 1;;
-p|--path) path=$2; shift 2;; -n|--name) name=$2; shift 2;;
--rootfs) rootfs=$2; shift 2;; -p|--path) path=$2; shift 2;;
-n|--name) name=$2; shift 2;; --rootfs) rootfs=$2; shift 2;;
--mapped-uid) LXC_MAPPED_UID=$2; shift 2;; --mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
--mapped-gid) LXC_MAPPED_GID=$2; shift 2;; --mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
-s|--ssh) SSH=$2; shift 2;; --) shift 1; break ;;
--) shift 1; break ;; *) break ;;
*) break ;; esac
esac
done done
if [ "$(id -u)" != "0" ]; then # Check that we have all variables we need
echo "This script should be run as 'root'" if [ -z "${name}" ] || [ -z "${path}" ] || [ -z "${rootfs}" ]; then
exit 1 echo "ERROR: Please pass the name, path, and rootfs for the container" 1>&2
fi
if [ -z "$path" ]; then
echo "'path' parameter is required"
exit 1 exit 1
fi fi
# detect rootfs # detect rootfs
config="$path/config" config="$path/config"
if [ -z "$rootfs" ]; then if [ -z "$rootfs" ]; then
if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then if grep -q '^lxc.rootfs.path' "${config}" 2> /dev/null ; then
rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}")
else else
rootfs=$path/rootfs rootfs="${path}/rootfs"
fi fi
fi fi
install_busybox $rootfs $name if ! install_busybox "${rootfs}" "${name}"; then
if [ $? -ne 0 ]; then echo "ERROR: Failed to install rootfs"
echo "failed to install busybox's rootfs" exit 1
exit 1
fi fi
configure_busybox $rootfs if ! configure_busybox "${rootfs}"; then
if [ $? -ne 0 ]; then echo "ERROR: Failed to configure busybox"
echo "failed to configure busybox template" exit 1
exit 1
fi fi
copy_configuration $path $rootfs $name if ! copy_configuration "${path}" "${rootfs}" "${name}"; then
if [ $? -ne 0 ]; then echo "ERROR: Failed to write config file"
echo "failed to write configuration file" exit 1
exit 1
fi fi
remap_userns $path if ! remap_userns "${path}"; then
if [ $? -ne 0 ]; then echo "ERROR: Failed to change idmappings"
echo "failed to remap files to user" exit 1
exit 1
fi
if [ -n "$SSH" ]; then
case "$SSH" in
"dropbear")
install_dropbear
if [ $? -ne 0 ]; then
echo "Unable to install 'dropbear' ssh utility"
exit 1
fi ;;
"openssh")
install_openssh
if [ $? -ne 0 ]; then
echo "Unable to install 'OpenSSH' utility"
exit 1
fi ;;
*)
echo "$SSH: unrecognized ssh utility"
exit 1
esac
else
which dropbear >/dev/null 2>&1
if [ $? -eq 0 ]; then
install_dropbear
fi
fi fi
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment