mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-24 18:42:03 +00:00

Add an additional template parameter for SSH support in the container. Currently this can be implemented using the Dropbear or OpenSSH utility. The respective tool needs to be available on the host Linux. If the parameter is omitted, the template will look for the Dropbear utility on the host and install it if it is available (legacy behavior). Adding OpenSSH support has been done following the model in the lxc-sshd template. Signed-off-by: Bogdan Purcareata <bogdan.purcareata@freescale.com> Acked-by: Stéphane Graber <stgraber@ubuntu.com>
496 lines
12 KiB
Bash
496 lines
12 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=
|
|
SSH=
|
|
|
|
# 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 ram0 null urandom; do
|
|
echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config
|
|
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
|
|
|
|
# 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
|
|
}
|
|
|
|
install_dropbear()
|
|
{
|
|
# 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"
|
|
|
|
return 0
|
|
}
|
|
|
|
install_openssh()
|
|
{
|
|
# tools to be installed
|
|
server_utils="sshd"
|
|
client_utils="\
|
|
ssh \
|
|
scp \
|
|
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
|
|
|
|
# copy binaries
|
|
for bin in $server_utils $client_utils; do
|
|
tool_path=`which $bin`
|
|
cp $tool_path $rootfs/$tool_path
|
|
if [ $? -ne 0 ]; then
|
|
echo "Unable to copy $tool_path in the rootfs"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
# add user and group
|
|
cat <<EOF >> $rootfs/etc/passwd
|
|
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
|
|
|
|
echo "'OpenSSH' utility installed"
|
|
|
|
return 0
|
|
}
|
|
|
|
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
|
|
|
|
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.rebootsignal = SIGTERM
|
|
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
|
|
|
|
lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
|
|
lxc.mount.entry = shm /dev/shm tmpfs defaults 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
|
|
$1 -h|--help -p|--path=<path> -s|--ssh={dropbear,openssh}
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@")
|
|
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;;
|
|
-s|--ssh) SSH=$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
|
|
|
|
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
|