mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-13 23:12:50 +00:00

When running unprivileged, lxc-create will touch a fstab file, with bind-mounts for the ttys and other devices. Add this entry in the container config. Signed-off-by: Bogdan Purcareata <bogdan.purcareata@freescale.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
397 lines
9.7 KiB
Bash
397 lines
9.7 KiB
Bash
#!/bin/bash
|
|
|
|
#
|
|
# lxc: linux Container library
|
|
|
|
# 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
|
|
# 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
|
|
|
|
LXC_MAPPED_UID=
|
|
LXC_MAPPED_GID=
|
|
|
|
# Make sure the usual locations are in PATH
|
|
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
|
|
|
|
am_in_userns() {
|
|
[ -e /proc/self/uid_map ] || { echo no; return; }
|
|
[ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; }
|
|
line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map)
|
|
[ "$line" = "0 0 4294967295" ] && { echo no; return; }
|
|
echo yes
|
|
}
|
|
|
|
in_userns=0
|
|
[ $(am_in_userns) = "yes" ] && in_userns=1
|
|
|
|
install_busybox()
|
|
{
|
|
rootfs=$1
|
|
name=$2
|
|
res=0
|
|
tree="\
|
|
$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/usr/share/udhcpc \
|
|
$rootfs/dev/pts \
|
|
$rootfs/dev/shm \
|
|
$rootfs/lib \
|
|
$rootfs/usr/lib \
|
|
$rootfs/lib64 \
|
|
$rootfs/usr/lib64"
|
|
|
|
mkdir -p $tree || return 1
|
|
chmod 755 $tree || return 1
|
|
|
|
pushd $rootfs/dev > /dev/null || return 1
|
|
|
|
# minimal devices needed for busybox
|
|
if [ $in_userns -eq 1 ]; then
|
|
for dev in tty console tty0 tty1 tty5 ram0 null urandom; do
|
|
touch $rootfs/dev/$dev
|
|
echo "/dev/$dev dev/$dev none bind 0 0" >> $path/fstab
|
|
done
|
|
else
|
|
mknod -m 666 tty c 5 0 || res=1
|
|
mknod -m 666 console c 5 1 || res=1
|
|
mknod -m 666 tty0 c 4 0 || res=1
|
|
mknod -m 666 tty1 c 4 0 || res=1
|
|
mknod -m 666 tty5 c 4 0 || res=1
|
|
mknod -m 600 ram0 b 1 0 || res=1
|
|
mknod -m 666 null c 1 3 || res=1
|
|
mknod -m 666 zero c 1 5 || res=1
|
|
mknod -m 666 urandom c 1 9 || res=1
|
|
fi
|
|
|
|
popd > /dev/null
|
|
|
|
# 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
|
|
|
|
# mount points
|
|
cat <<EOF >> $rootfs/etc/fstab
|
|
shm /dev/shm tmpfs defaults 0 0
|
|
EOF
|
|
|
|
# writable and readable for other
|
|
chmod 644 $rootfs/etc/fstab || 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
|
|
|
|
cat <<EOF >> $rootfs/usr/share/udhcpc/default.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
|
|
echo nameserver \$i >> /etc/resolv.conf
|
|
done
|
|
;;
|
|
esac
|
|
exit 0
|
|
EOF
|
|
|
|
chmod 744 $rootfs/usr/share/udhcpc/default.script
|
|
|
|
return $res
|
|
}
|
|
|
|
configure_busybox()
|
|
{
|
|
rootfs=$1
|
|
|
|
which busybox >/dev/null 2>&1
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "busybox executable is not accessible"
|
|
return 1
|
|
fi
|
|
|
|
file -L $(which busybox) | grep -q "statically linked"
|
|
if [ $? -ne 0 ]; then
|
|
echo "warning : busybox is not statically linked."
|
|
echo "warning : The template script may not correctly"
|
|
echo "warning : setup the container environment."
|
|
fi
|
|
|
|
# copy busybox in the rootfs
|
|
cp $(which busybox) $rootfs/bin
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to copy busybox in the rootfs"
|
|
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
|
|
pushd $rootfs/bin > /dev/null || return 1
|
|
./busybox --help | grep 'Currently defined functions:' -A300 | \
|
|
grep -v 'Currently defined functions:' | tr , '\n' | \
|
|
xargs -n1 ln -s busybox
|
|
popd > /dev/null
|
|
|
|
# relink /sbin/init
|
|
ln $rootfs/bin/busybox $rootfs/sbin/init
|
|
|
|
# passwd exec must be setuid
|
|
chmod +s $rootfs/bin/passwd
|
|
touch $rootfs/etc/shadow
|
|
|
|
# setting passwd for root
|
|
CHPASSWD_FILE=$rootfs/root/chpasswd.sh
|
|
|
|
cat <<EOF >$CHPASSWD_FILE
|
|
echo "setting root password to \"root\""
|
|
|
|
mount -n --bind /lib $rootfs/lib
|
|
if [ \$? -ne 0 ]; then
|
|
echo "Failed bind-mounting /lib at $rootfs/lib"
|
|
exit 1
|
|
fi
|
|
|
|
chroot $rootfs chpasswd <<EOFF 2>/dev/null
|
|
root:root
|
|
EOFF
|
|
|
|
|
|
if [ \$? -ne 0 ]; then
|
|
echo "Failed to change root password"
|
|
exit 1
|
|
fi
|
|
|
|
umount $rootfs/lib
|
|
|
|
EOF
|
|
|
|
lxc-unshare -s MOUNT -- /bin/sh < $CHPASSWD_FILE
|
|
rm $CHPASSWD_FILE
|
|
|
|
# add ssh functionality if dropbear package available on host
|
|
which dropbear >/dev/null 2>&1
|
|
if [ $? -eq 0 ]; then
|
|
# copy dropbear binary
|
|
cp $(which dropbear) $rootfs/usr/sbin
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to copy dropbear in the rootfs"
|
|
return 1
|
|
fi
|
|
|
|
# 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"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
copy_configuration()
|
|
{
|
|
path=$1
|
|
rootfs=$2
|
|
name=$3
|
|
|
|
grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
|
|
cat <<EOF >> $path/config
|
|
lxc.haltsignal = SIGUSR1
|
|
lxc.utsname = $name
|
|
lxc.tty = 1
|
|
lxc.pts = 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.aa_profile = unconfined
|
|
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
|
|
echo "lxc.mount.auto = proc:mixed sys" >>$path/config
|
|
|
|
if [ -f "$path/fstab" ]; then
|
|
echo "lxc.mount = $path/fstab" >>$path/config
|
|
fi
|
|
}
|
|
|
|
remap_userns()
|
|
{
|
|
path=$1
|
|
|
|
if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then
|
|
chown $LXC_MAPPED_UID $path/config $path/fstab >/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 $path/fstab >/dev/null 2>&1
|
|
chgrp -R root $path/rootfs >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
usage()
|
|
{
|
|
cat <<EOF
|
|
$1 -h|--help -p|--path=<path>
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
options=$(getopt -o hp:n: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid: -- "$@")
|
|
if [ $? -ne 0 ]; then
|
|
usage $(basename $0)
|
|
exit 1
|
|
fi
|
|
eval set -- "$options"
|
|
|
|
while true
|
|
do
|
|
case "$1" in
|
|
-h|--help) usage $0 && exit 0;;
|
|
-p|--path) path=$2; shift 2;;
|
|
--rootfs) rootfs=$2; shift 2;;
|
|
-n|--name) name=$2; shift 2;;
|
|
--mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
|
|
--mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
|
|
--) shift 1; break ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
|
|
if [ "$(id -u)" != "0" ]; then
|
|
echo "This script should be run as 'root'"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$path" ]; then
|
|
echo "'path' parameter is required"
|
|
exit 1
|
|
fi
|
|
|
|
# detect rootfs
|
|
config="$path/config"
|
|
if [ -z "$rootfs" ]; then
|
|
if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
|
|
rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config)
|
|
else
|
|
rootfs=$path/rootfs
|
|
fi
|
|
fi
|
|
|
|
install_busybox $rootfs $name
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to install busybox's rootfs"
|
|
exit 1
|
|
fi
|
|
|
|
configure_busybox $rootfs
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to configure busybox template"
|
|
exit 1
|
|
fi
|
|
|
|
copy_configuration $path $rootfs $name
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to write configuration file"
|
|
exit 1
|
|
fi
|
|
|
|
remap_userns $path
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to remap files to user"
|
|
exit 1
|
|
fi
|