lxc-templates/templates/lxc-debian.in
Antonio Terceiro 56a2d30c41 lxc-debian: avoid installing unauthenticated packages
--force-yes is deprecated, and is a shortcut for
--allow-unauthenticated, --allow-downgrades, --allow-remove-essential,
--allow-change-held-packages. --allow-unauthenticated is the only
likely to be needed here, so instead of allowing unauthenticated
packages, copy any custom keyring used inside the rootfs so any packages
we need to install are properly authenticated. The common case is
debian-ports-archive-keyring.

Signed-off-by: Antonio Terceiro <terceiro@debian.org>
2022-04-10 06:39:07 -03:00

922 lines
28 KiB
Bash

#!/bin/bash
#
# lxc: linux Container library
# Authors:
# Daniel Lezcano <daniel.lezcano@free.fr>
# Greg Olsen <gregolsen@computer.org> (lxc-devuan)
# Tanya <tanyadegurechaff@waifu.club>
# 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
# Detect use under userns (unsupported)
for arg in "$@"; do
[ "$arg" = "--" ] && break
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
echo "This template can't be used for unprivileged containers." 1>&2
echo "You may want to try the \"download\" template instead." 1>&2
exit 1
fi
done
# Make sure the usual locations are in PATH
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
export GREP_OPTIONS=""
DEBIAN_MIRROR=http://deb.debian.org/debian
DEBIAN_PORTS_MIRROR=${MIRROR:-http://deb.debian.org/debian-ports}
MIRROR=${MIRROR:-${DEBIAN_MIRROR}}
SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.debian.org/}
LOCALSTATEDIR="@LOCALSTATEDIR@"
LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@"
# Allows the lxc-cache directory to be set by environment variable
LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"}
DEBIAN_ARCHIVE_KEYRING=/usr/share/keyrings/debian-archive-keyring.gpg
DEBIAN_PORTS_ARCHIVE_KEYRING=/usr/share/keyrings/debian-ports-archive-keyring.gpg
KEYRING=${KEYRING:-${DEBIAN_ARCHIVE_KEYRING}}
DOWNLOAD_KEYRING=${DOWNLOAD_KEYRING:-1}
find_interpreter()
{
given_interpreter=$(basename "$1")
if [ ! -d /proc/sys/fs/binfmt_misc/ ] ; then
return 1
fi
for file in /proc/sys/fs/binfmt_misc/* ; do
if [ "$file" = "/proc/sys/fs/binfmt_misc/register" -o \
"$file" = "/proc/sys/fs/binfmt_misc/status" ] ; then
continue
fi
interpreter_path=$(sed -n "/^interpreter/s/interpreter \([^[:space:]]*\)/\1/p" "$file")
interpreter=$(basename "$interpreter_path")
if [ "$given_interpreter" = "$interpreter" ] ; then
echo "$interpreter_path"
return 0
fi
done
return 1
}
release_info() {
if wget "${1}/dists/${release}/Release" -O /dev/null --quiet 2> /dev/null; then
local release_file=$(wget "${1}/dists/${release}/Release" -O - --quiet 2> /dev/null)
distro_suite=${release_file#*Suite: }
distro_suite=${distro_suite%%$'\n'*}
return 0
fi
return 1
}
configure_debian()
{
rootfs=$1
hostname=$2
num_tty=$3
# squeeze only has /dev/tty and /dev/tty0 by default,
# therefore creating missing device nodes for tty1-4.
for tty in $(seq 1 "$num_tty"); do
if [ ! -e "$rootfs/dev/tty$tty" ]; then
mknod "$rootfs/dev/tty$tty" c 4 "$tty"
fi
done
# configure the inittab
cat <<EOF > $rootfs/etc/inittab
id:3:initdefault:
si::sysinit:/etc/init.d/rcS
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
1:2345:respawn:/sbin/getty 38400 console
$(for tty in $(seq 1 "$num_tty"); do echo "c${tty}:12345:respawn:/sbin/getty 38400 tty${tty} linux" ; done;)
p6::ctrlaltdel:/sbin/init 6
p0::powerfail:/sbin/init 0
EOF
# symlink mtab
[ -e "$rootfs/etc/mtab" ] && rm "$rootfs/etc/mtab"
ln -s /proc/self/mounts "$rootfs/etc/mtab"
# disable selinux in debian
mkdir -p "$rootfs/selinux"
echo 0 > "$rootfs/selinux/enforce"
# configure the network using the dhcp
cat <<EOF > $rootfs/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
EOF
# set the hostname
cat <<EOF > $rootfs/etc/hostname
$hostname
EOF
# set minimal hosts
cat <<EOF > $rootfs/etc/hosts
127.0.0.1 localhost
127.0.1.1 $hostname
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
# reconfigure some services
# but first reconfigure locales - so we get no noisy perl-warnings
if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then
cat >> "$rootfs/etc/locale.gen" << EOF
en_US.UTF-8 UTF-8
EOF
chroot "$rootfs" locale-gen en_US.UTF-8 UTF-8
chroot "$rootfs" update-locale LANG=en_US.UTF-8
else
encoding=$(locale charmap)
[ -z "${encoding}" ] && encoding="UTF-8"
chroot "$rootfs" sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \
-i /etc/locale.gen 2> /dev/null
cat >> "$rootfs/etc/locale.gen" << EOF
$LANG $encoding
EOF
chroot "$rootfs" locale-gen "$LANG" "$encoding"
chroot "$rootfs" update-locale LANG="$LANG"
fi
# remove pointless services in a container
chroot "$rootfs" /usr/sbin/update-rc.d -f checkroot.sh disable
chroot "$rootfs" /usr/sbin/update-rc.d -f umountfs disable
chroot "$rootfs" /usr/sbin/update-rc.d -f hwclock.sh disable
chroot "$rootfs" /usr/sbin/update-rc.d -f hwclockfirst.sh disable
# generate new SSH keys
if [ -x "$rootfs/var/lib/dpkg/info/openssh-server.postinst" ]; then
cat > "$rootfs/usr/sbin/policy-rc.d" << EOF
#!/bin/sh
exit 101
EOF
chmod +x "$rootfs/usr/sbin/policy-rc.d"
if [ -f "$rootfs/etc/init/ssh.conf" ]; then
mv "$rootfs/etc/init/ssh.conf" "$rootfs/etc/init/ssh.conf.disabled"
fi
rm -f "$rootfs/etc/ssh/"ssh_host_*key*
DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot "$rootfs" /var/lib/dpkg/info/openssh-server.postinst configure
sed -i "s/root@$(hostname)/root@$hostname/g" "$rootfs/etc/ssh/"ssh_host_*.pub
if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then
mv "$rootfs/etc/init/ssh.conf.disabled" "$rootfs/etc/init/ssh.conf"
fi
rm -f "$rootfs/usr/sbin/policy-rc.d"
fi
# set initial timezone as on host
if [ -f /etc/timezone ]; then
cat /etc/timezone > "$rootfs/etc/timezone"
rm -f "$rootfs/etc/localtime"
chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata
elif [ -f /etc/sysconfig/clock ]; then
. /etc/sysconfig/clock
echo "$ZONE" > "$rootfs/etc/timezone"
rm -f "$rootfs/etc/localtime"
chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata
else
echo "Timezone in container is not configured. Adjust it manually."
fi
if [ -n "$authkey" ]; then
local ssh_dir_path="${rootfs}/root/.ssh"
mkdir -p "$ssh_dir_path"
cp "$authkey" "${ssh_dir_path}/authorized_keys"
chmod 700 "$ssh_dir_path"
echo "Inserted SSH public key from '$authkey' into /root/.ssh/authorized_keys"
fi
return 0
}
write_sourceslist()
{
local rootfs="$1"; shift
local release="$1"; shift
local arch="$1"; shift
local prefix="deb"
if [ -n "${arch}" ]; then
prefix="deb [arch=${arch}]"
fi
if [ "$mainonly" = 1 ]; then
non_main=''
else
non_main=' contrib non-free'
fi
cat >> "${rootfs}/etc/apt/sources.list" << EOF
${prefix} $MIRROR ${release} main${non_main}
EOF
if [ "$release" != "unstable" -a "$release" != "sid" ]; then
cat >> "${rootfs}/etc/apt/sources.list" << EOF
${prefix} $SECURITY_MIRROR ${release}-security main${non_main}
EOF
fi
}
apt_update_packages(){
local apt_update_args="update"
local lpath="${rootfs}/var/lib/apt/lists/$(echo "$MIRROR" | sed 's#.*://##;s#/#_#g')_dists_${release}_Release"
if [ -f "$lpath" ] && [ ! -z "$distro_suite" ]; then
local lsuite=$(cat "$lpath" | grep 'Suite:')
[ "${lsuite#Suite: }" != "$distro_suite" ] && apt_update_args="--allow-releaseinfo-change $apt_update_args"
fi
chroot "${rootfs}" apt-get $apt_update_args
return $?
}
install_packages()
{
local rootfs="$1"; shift
local packages="$*"
if [ -n "${packages}" ] && apt_update_packages; then
chroot "${rootfs}" apt-get install -y --no-install-recommends ${packages}
elif [ -n "${packages}" ]; then
echo "Apt update failed, not downloading packages"
fi
}
configure_debian_systemd()
{
path=$1
rootfs=$2
config=$3
num_tty=$4
# just in case systemd is not installed
mkdir -p "${rootfs}/lib/systemd/system"
mkdir -p "${rootfs}/etc/systemd/system/getty.target.wants"
# Fix getty-static-service as debootstrap does not install dbus
if [ -e "$rootfs//lib/systemd/system/getty-static.service" ] ; then
local tty_services
tty_services=$(for i in $(seq 2 "$num_tty"); do echo -n "getty@tty${i}.service "; done; )
sed 's/ getty@tty.*/'" $tty_services "'/g' \
"$rootfs/lib/systemd/system/getty-static.service" | \
sed 's/\(tty2-tty\)[5-9]/\1'"${num_tty}"'/g' > "$rootfs/etc/systemd/system/getty-static.service"
fi
# This function has been copied and adapted from lxc-fedora
rm -f "${rootfs}/etc/systemd/system/default.target"
chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/udev.service
chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/systemd-udevd.service
chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
# Setup getty service on the ttys we are going to allow in the
# default config. Number should match lxc.tty.max
( cd "${rootfs}/etc/systemd/system/getty.target.wants"
for i in $(seq 1 "$num_tty") ; do ln -sf ../getty\@.service getty@tty"${i}".service; done )
# Since we use static-getty.target; we need to mask container-getty@.service generated by
# container-getty-generator, so we don't get multiple instances of agetty running.
# See https://github.com/lxc/lxc/issues/520 and https://github.com/lxc/lxc/issues/484
( cd "${rootfs}/etc/systemd/system/getty.target.wants"
for i in $(seq 0 "$num_tty"); do ln -sf /dev/null container-getty\@"${i}".service; done )
return 0
}
# Check if given path is in a btrfs partition
is_btrfs()
{
[ -e "$1" -a "$(stat -f -c '%T' "$1")" = "btrfs" ]
}
# Check if given path is the root of a btrfs subvolume
is_btrfs_subvolume()
{
[ -d "$1" -a "$(stat -f -c '%T' "$1")" = "btrfs" -a "$(stat -c '%i' "$1")" -eq 256 ]
}
try_mksubvolume()
{
path=$1
[ -d "$path" ] && return 0
mkdir -p "$(dirname "$path")"
if which btrfs >/dev/null 2>&1 && is_btrfs "$(dirname "$path")"; then
btrfs subvolume create "$path"
else
mkdir -p "$path"
fi
}
try_rmsubvolume()
{
path=$1
[ -d "$path" ] || return 0
if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume "$path"; then
btrfs subvolume delete "$path"
else
rm -rf "$path"
fi
}
cleanup()
{
try_rmsubvolume "$cache/partial-$release-$arch"
try_rmsubvolume "$cache/rootfs-$release-$arch"
}
download_debian()
{
case "$release" in
wheezy)
init=sysvinit
iproute=iproute
;;
*)
init=init
iproute=iproute2
;;
esac
packages=\
$init,\
ifupdown,\
locales,\
ca-certificates,\
dialog,\
isc-dhcp-client,\
netbase,\
net-tools,\
$iproute,\
openssh-server,\
apt-transport-https
cache=$1
arch=$2
release=$3
interpreter="$4"
interpreter_path="$5"
trap cleanup EXIT SIGHUP SIGINT SIGTERM
# Create the cache
mkdir -p "$cache"
# If debian-archive-keyring isn't installed, fetch GPG keys directly
releasekeyring=${KEYRING}
lreleasekeyring=/etc/apt/trusted.gpg.d/debian-archive-$release-stable.gpg
if [ -f "$releasekeyring" ]; then
apt_gpg_opt="--keyring=${releasekeyring}"
elif [ -f "$lreleasekeyring" ]; then
apt_gpg_opt="--keyring=${lreleasekeyring}"
elif [ ${releasekeyring}" = "${DEBIAN_ARCHIVE_KEYRING} -a "$DOWNLOAD_KEYRING" = 1 ]; then
[ ! -d "/etc/apt/trusted.gpg.d" ] && lreleasekeyring="$cache/archive-key.gpg"
if [[ "$(id -u)" == "0" ]]; then
case $release in
"wheezy")
gpgkeyname="archive-key-7.0"
;;
*)
gpgkeyname="archive-key-8"
;;
esac
wget https://ftp-master.debian.org/keys/${gpgkeyname}.asc -O - --quiet \
| gpg --import --no-default-keyring --keyring="${lreleasekeyring}"
apt_gpg_opt="--keyring=${lreleasekeyring}"
else
echo "Must be root (sudo) to save $lreleasekeyring"
fi
fi
if [ -z "$apt_gpg_opt" ]; then
echo "WARNING: No GPG check"
apt_gpg_opt='--no-check-gpg'
fi
# check the mini debian was not already downloaded
try_mksubvolume "$cache/partial-$release-$arch"
if [ $? -ne 0 ]; then
echo "Failed to create '$cache/partial-$release-$arch' directory"
return 1
fi
# download a mini debian into a cache
echo "Downloading debian minimal ..."
if [ "$interpreter" = "" ] ; then
debootstrap --verbose --variant=minbase --arch="$arch" \
--include=$packages "${apt_gpg_opt}" \
"$release" "$cache/partial-$release-$arch" "$MIRROR"
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
else
debootstrap --foreign --verbose --variant=minbase --arch="$arch" \
--include=$packages "${apt_gpg_opt}" \
"$release" "$cache/partial-$release-$arch" "$MIRROR"
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
mkdir -p "$(dirname "$cache/partial-$release-$arch/$interpreter_path")"
cp "$interpreter" "$cache/partial-$release-$arch/$interpreter_path"
if [ $? -ne 0 ]; then
echo "failed to copy $interpreter to $cache/partial-$release-$arch/$interpreter_path"
return 1
fi
chroot "$cache/partial-$release-$arch" debootstrap/debootstrap --second-stage
if [ $? -ne 0 ]; then
echo "failed to update the rootfs, aborting"
return 1
fi
fi
mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch"
echo "Download complete."
trap EXIT
trap SIGINT
trap SIGTERM
trap SIGHUP
return 0
}
copy_debian()
{
cache=$1
arch=$2
rootfs=$3
release=$4
# make a local copy of the minidebian
echo -n "Copying rootfs to $rootfs..."
try_mksubvolume "$rootfs"
if which btrfs >/dev/null 2>&1 && \
is_btrfs_subvolume "$cache/rootfs-$release-$arch" && \
is_btrfs_subvolume "$rootfs"; then
realrootfs="$(dirname "$config")"/rootfs
[ "$rootfs" = "$realrootfs" ] || umount "$rootfs" || return 1
btrfs subvolume delete "$realrootfs" || return 1
btrfs subvolume snapshot "$cache/rootfs-$release-$arch" "$realrootfs" || return 1
[ "$rootfs" = "$realrootfs" ] || mount --bind "$realrootfs" "$rootfs" || return 1
else
rsync -SHaAX "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1
fi
return 0
}
install_debian()
{
rootfs=$1
release=$2
arch=$3
cache="$4/debian"
interpreter="$5"
interpreter_path="$6"
flushcache=$7
mkdir -p $LOCALSTATEDIR/lock/subsys/
(
flock -x 9
if [ $? -ne 0 ]; then
echo "Cache repository is busy."
return 1
fi
if [ "$flushcache" -eq 1 ]; then
echo "Flushing cache..."
cleanup
fi
echo "Checking cache download in $cache/rootfs-$release-$arch ... "
if [ ! -e "$cache/rootfs-$release-$arch" ]; then
download_debian "$cache" "$arch" "$release" "$interpreter" "$interpreter_path"
if [ $? -ne 0 ]; then
echo "Failed to download 'debian base'"
return 1
fi
fi
copy_debian "$cache" "$arch" "$rootfs" "$release"
if [ $? -ne 0 ]; then
echo "Failed to copy rootfs"
return 1
fi
return 0
) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian
return $?
}
copy_configuration()
{
path=$1
rootfs=$2
hostname=$3
arch=$4
num_tty=$5
# Generate the configuration file
# if there is exactly one veth network entry, make sure it has an
# associated hwaddr.
nics=$(grep -ce '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' "$path/config")
if [ "$nics" -eq 1 ]; then
grep -q "^lxc.net.0.hwaddr" "$path/config" || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" "$path/config"
fi
## Add all the includes
echo "" >> "$path/config"
echo "# Common configuration" >> "$path/config"
if [ -e "${LXC_TEMPLATE_CONFIG}/debian.common.conf" ]; then
echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.common.conf" >> "$path/config"
fi
if [ -e "${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" ]; then
echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" >> "$path/config"
fi
## Add the container-specific config
echo "" >> "$path/config"
echo "# Container specific configuration" >> "$path/config"
grep -q "^lxc.rootfs.path" "$path/config" 2> /dev/null || echo "lxc.rootfs.path = $rootfs" >> "$path/config"
cat <<EOF >> $path/config
lxc.tty.max = $num_tty
lxc.uts.name = $hostname
lxc.arch = $arch
lxc.pty.max = 1024
EOF
if [ $? -ne 0 ]; then
echo "Failed to add configuration"
return 1
fi
return 0
}
post_process()
{
local rootfs="$1"; shift
local release="$1"; shift
local arch="$1"; shift
local hostarch="$1"; shift
local interpreter="$1"; shift
local packages="$*"
# Disable service startup
cat > "${rootfs}/usr/sbin/policy-rc.d" << EOF
#!/bin/sh
exit 101
EOF
chmod +x "${rootfs}/usr/sbin/policy-rc.d"
# If the container isn't running a native architecture, setup multiarch
if [ "$interpreter" = "" -a "${arch}" != "${hostarch}" ]; then
# Test if dpkg supports multiarch
if ! chroot "$rootfs" dpkg --print-foreign-architectures 2>&1; then
chroot "$rootfs" dpkg --add-architecture "${hostarch}"
fi
fi
# Write a new sources.list containing both native and multiarch entries
> "${rootfs}/etc/apt/sources.list"
if [ "$interpreter" != "" -a "${arch}" = "${hostarch}" ]; then
write_sourceslist "${rootfs}" "${release}" "${arch}"
else
write_sourceslist "${rootfs}" "${release}"
fi
# Copy non-standard keyring inside rootfs
local extra_keyring
extra_keyring="${rootfs}/etc/apt/trusted.gpg.d/lxc-create-$(basename "${KEYRING}")"
if [ "${KEYRING}" != "${DEBIAN_ARCHIVE_KEYRING}" ]; then
cp "${KEYRING}" "${extra_keyring}"
fi
# Install Packages in container
if [ -n "${packages}" ]; then
local pack_list
pack_list="${packages//,/ }"
echo "Installing packages: ${pack_list}"
install_packages "${rootfs}" "${pack_list}"
fi
# Re-enable service startup
rm "${rootfs}/usr/sbin/policy-rc.d"
# Fix by Greg Olsen, avoid warning 'stdin not a tty' in some cases
if [[ -f "${rootfs}/root/.profile" ]]; then
sed -i -e 's/^mesg n/test -t 0 \&\& mesg n/g' ${rootfs}/root/.profile
fi
# end
}
clean()
{
cache=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc/debian"}
if [ ! -e "$cache" ]; then
exit 0
fi
# lock, so we won't purge while someone is creating a repository
(
flock -x 9
if [ $? != 0 ]; then
echo "Cache repository is busy."
exit 1
fi
echo -n "Purging the download cache..."
rm --preserve-root --one-file-system -rf "$cache" && echo "Done." || exit 1
exit 0
) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian
}
usage()
{
cat <<EOF
Template specific options can be passed to lxc-create after a '--' like this:
lxc-create --name=NAME [-lxc-create-options] -- [-template-options]
Usage: $1 -h|--help -p|--path=<path> [-c|--clean] [-a|--arch=<arch>] [-r|--release=<release>]
[--mirror=<mirror>] [--security-mirror=<security mirror>]
[--keyring=<keyring>]
[--package=<package_name1,package_name2,...>]
[-I|--interpreter-path=<interpreter path>]
[-F | --flush-cache] [-S|--auth-key=<keyfile>]
Options :
-h, --help print this help text
-p, --path=PATH directory where config and rootfs of this VM will be kept
-S, --auth-key=KEYFILE SSH public key to inject into the container as the root user.
-a, --arch=ARCH The container architecture. Can be one of: i686, x86_64,
amd64, armhf, armel, powerpc. Defaults to host arch.
-r, --release=RELEASE Debian release. Can be one of: wheezy, jessie, stretch, buster, sid.
Defaults to current stable.
--mirror=MIRROR Debian mirror to use during installation. Overrides the MIRROR
environment variable (see below).
--keyring=KEYRING GPG keyring to authenticate the repository against.
Overrides the KEYRING environment variable (see below).
--security-mirror=SECURITY_MIRROR
Debian mirror to use for security updates. Overrides the
SECURITY_MIRROR environment variable (see below).
--packages=PACKAGE_NAME1,PACKAGE_NAME2,...
List of additional packages to install. Comma separated, without space.
-c, --clean only clean up the cache and terminate
--enable-non-free include also Debian's contrib and non-free repositories.
-I|--interpreter-path=INTERPRETER-PATH
Path of the binfmt interpreter to copy to the rootfs
-F | --flush-cache Flush the debian release cache
Environment variables:
MIRROR The Debian package mirror to use. See also the --mirror switch above.
Defaults to '$MIRROR'
SECURITY_MIRROR The Debian package security mirror to use. See also the --security-mirror switch above.
Defaults to '$SECURITY_MIRROR'
KEYRING Defaults to '$DEBIAN_ARCHIVE_KEYRING'
DOWNLOAD_KEYRING Sets whether to download keyring when missing or ignore keyring checks
Defaults to 1
EOF
return 0
}
options=$(getopt -o hp:n:a:r:cI:FS: -l arch:,auth-key:,clean,help,enable-non-free,mirror:keyring:,name:,packages:,path:,release:,rootfs:,security-mirror:,interpreter-path:,flush-cache -- "$@")
if [ $? -ne 0 ]; then
usage "$(basename "$0")"
exit 1
fi
eval set -- "$options"
littleendian=$(lscpu | grep '^Byte Order' | grep -q Little && echo yes)
arch=$(uname -m)
if [ "$arch" = "i686" ]; then
arch="i386"
elif [ "$arch" = "x86_64" ]; then
arch="amd64"
elif [ "$arch" = "armv7l" ]; then
arch="armhf"
elif [ "$arch" = "aarch64" ]; then
arch="arm64"
elif [ "$arch" = "ppc" ]; then
arch="powerpc"
elif [ "$arch" = "ppc64le" ]; then
arch="ppc64el"
elif [ "$arch" = "mips" -a "$littleendian" = "yes" ]; then
arch="mipsel"
elif [ "$arch" = "mips64" -a "$littleendian" = "yes" ]; then
arch="mips64el"
fi
hostarch=$arch
packages=
mainonly=1
flushcache=0
while true
do
case "$1" in
-h|--help) usage "$0" && exit 1;;
--) shift 1; break ;;
-a|--arch) arch=$2; shift 2;;
-S|--auth-key) authkey=$2; shift 2;;
-I|--interpreter-path)
interpreter="$2"; shift 2;;
-c|--clean) clean=1; shift 1;;
--enable-non-free) mainonly=0; shift 1;;
--mirror) MIRROR=$2; shift 2;;
--keyring) KEYRING=$2; shift 2;;
-n|--name) name=$2; shift 2;;
--packages) packages=$2; shift 2;;
-p|--path) path=$2; shift 2;;
-r|--release) release=$2; shift 2;;
--rootfs) rootfs=$2; shift 2;;
--security-mirror) SECURITY_MIRROR=$2; shift 2;;
-F|--flush-cache) flushcache=1; shift 1;;
*) break ;;
esac
done
if [ ! -z "$clean" -a -z "$path" ]; then
clean || exit 1
exit 0
fi
if [ "$arch" = "i686" ]; then
arch=i386
fi
if [ "$arch" = "x86_64" ]; then
arch=amd64
fi
testing_release_file=${DEBIAN_MIRROR}/dists/testing/main/binary-${arch}/Release
if ! wget -q -O /dev/null "${testing_release_file}"; then
echo "${arch} does not look like a release architecture, trying debian ports"
# non-release architecture; assume debian-ports architecture
if [ "${MIRROR}" = "${DEBIAN_MIRROR}" ]; then
MIRROR="${DEBIAN_PORTS_MIRROR}"
fi
if [ "${KEYRING}" = "${DEBIAN_ARCHIVE_KEYRING}" ]; then
KEYRING="${DEBIAN_PORTS_ARCHIVE_KEYRING}"
fi
packages="debian-ports-archive-keyring,${packages}"
release="${release:-unstable}"
fi
if [ "$interpreter" = "" ] ; then
if [ $hostarch = "i386" -a $arch = "amd64" ]; then
echo "can't create $arch container on $hostarch"
exit 1
fi
if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \
[ $arch != "armhf" -a $arch != "armel" ]; then
echo "can't create $arch container on $hostarch"
exit 1
fi
if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then
echo "can't create $arch container on $hostarch"
exit 1
fi
if [ $hostarch = "mips" -a $arch != "mips" ] || \
[ $hostarch = "mipsel" -a $arch != "mipsel" ] || \
[ $hostarch = "mips64" -a $arch != "mips" -a $arch != "mips64" ] || \
[ $hostarch = "mips64el" -a $arch != "mipsel" -a $arch != "mips64el" ]; then
echo "can't create $arch container on $hostarch"
exit 1
fi
else
if ! file -b "${interpreter}" |grep -q "statically linked" ; then
echo "'${interpreter}' must be statically linked" 1>&2
exit 1
fi
interpreter_path=$(find_interpreter "$interpreter")
if [ $? -ne 0 ] ; then
echo "no binfmt interpreter using $(basename "$interpreter")" 1>&2
exit 1
fi
fi
type debootstrap
if [ $? -ne 0 ]; then
echo "'debootstrap' command is missing"
exit 1
fi
if [ -z "$path" ]; then
echo "'path' parameter is required"
exit 1
fi
if [ "$(id -u)" != "0" ]; then
echo "This script should be run as 'root'"
exit 1
fi
if [ -n "$authkey" ]; then
if [ ! -f "$authkey" ]; then
echo "SSH keyfile '$authkey' not found"
exit 1
fi
# This is mostly to prevent accidental uage of the private key instead
# of the public key.
if [ "${authkey: -4}" != ".pub" ]; then
echo "SSH keyfile '$authkey' does not end with '.pub'"
exit 1
fi
fi
release=${release:-stable}
permanent_releases=('stable' 'testing' 'sid' 'unstable')
if [[ ! "${permanent_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]]; then
if ! release_info "${MIRROR}"; then
echo "Invalid release ${release} (not found in mirror)"
exit 1
fi
else
release_info "${MIRROR}"
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[ \t]+=/{ print $2 }' "$config")
else
rootfs=$path/rootfs
fi
fi
# determine the number of ttys - default is 4
if grep -q '^lxc.tty.max' "$config" 2> /dev/null ; then
num_tty=$(awk -F= '/^lxc.tty.max[ \t]+=/{ print $2 }' "$config")
else
num_tty=4
fi
install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH" "$interpreter" "$interpreter_path" "$flushcache"
if [ $? -ne 0 ]; then
echo "failed to install debian"
exit 1
fi
configure_debian "$rootfs" "$name" $num_tty
if [ $? -ne 0 ]; then
echo "failed to configure debian for a container"
exit 1
fi
copy_configuration "$path" "$rootfs" "$name" $arch $num_tty
if [ $? -ne 0 ]; then
echo "failed write configuration file"
exit 1
fi
configure_debian_systemd "$path" "$rootfs" "$config" $num_tty
post_process "${rootfs}" "${release}" ${arch} ${hostarch} "${interpreter}" "${packages}"
if [ ! -z "$clean" ]; then
clean || exit 1
exit 0
fi