#!/bin/bash set -e # Includes . $(miniade) || { echo "${0##*/}: ERROR: miniade failed (hint: run 'miniade' to see error)" >&2; exit 1; } # Based on /usr/share/lxc/templates/lxc-debian, which is the work of: # # Daniel Lezcano # Greg Olsen # Tanya # Configurable stuff TTY_COUNT=4 MIRROR=http://deb.debian.org/debian # Other globals main() { local MY_ARGS local PROGNAME # Defaults for options RELEASE= CONTAINER_ROOTDIR= CONTAINER_NAME= CONTAINER_LXC_PATH= FLUSH_CACHE_FLAG=false # Process options special_opts_handler() { case $1 in # Lxc-create-specified options --name=*) CONTAINER_NAME=${1#*=} ;; --path=*) CONTAINER_LXC_PATH=${2#*=} ;; --rootfs=*) CONTAINER_ROOTDIR=${1#*=} ;; # Template-specific options --release=*) RELEASE=${1#*=} ;; --mirror=*) MIRROR=${1#*=} ;; --stderr=*) eval "exec 2>&${1#*=}" ;; # urgent # lxc-create does not have --flush-cache/-F but the official template (and so also this template) # does. To use template options lxc-create needs to be called as: # # lxc-create -- # --flush-cache|-F) FLUSH_CACHE_FLAG=true ;; *) return 1 ;; esac } miniade_process_options --special-opts-handler=special_opts_handler MY_ARGS "$@" && set -- "${MY_ARGS[@]}" # Process arguments [ $# = 0 ] || usage # Sanity checks and derivations miniade_get_progname PROGNAME CACHE_ROOTDIR=/var/cache/$PROGNAME for VAR in CONTAINER_ROOTDIR CONTAINER_NAME RELEASE; do eval "[ \"X\$$VAR\" != X ]" || usage done [ $(uname -m) = x86_64 ] || miniade_error "this template only supports x86_64 on the host" ARCH=amd64 [ "$(id -u)" = 0 ] || miniade_error "this script must be run as root" for CMD in debootstrap; do type -p $CMD > /dev/null || miniade_error "$CMD: not found (hint: install it)" done [[ $RELEASE =~ ^(bullseye|bookworm|trixie)$ ]] || miniade_error "$RELEASE: release not supported (hint: ...)" [[ $CONTAINER_ROOTDIR =~ ^/.* ]] || miniade_error "$CONTAINER_ROOTDIR: not absolute (hint: ...)" [ -d "$CONTAINER_ROOTDIR" ] || miniade_error "$CONTAINER_ROOTDIR: not found (hint: this script will not create it for you)" LSA_OUTPUT=$(ls -A $CONTAINER_ROOTDIR 2>&1 | egrep -vx '(lost\+found)') || true [ "X$LSA_OUTPUT" = X ] || miniade_error "$CONTAINER_ROOTDIR: not empty or error examining contents (hint: 'ls -A $CONTAINER_ROOTDIR' reported '$LSA_OUTPUT')" # Guts flush_cache_if_requested download_debian_to_cache_if_necessary copy_debian_from_cache containerise_installation install_packages } usage() { local RC PROGNAME miniade_get_progname PROGNAME echo "Usage: $PROGNAME [ ... ] --name= [ --path= ] --rootfs= --release={bullseye|bookworm|trixie} [ --mirror= ] [ --stderr= ] [ --clean ]" exit 0 } copy_debian_from_cache() { miniade_info "copying debian out of cache ..." miniade_evaler "rsync -SHaAX \"$CACHE_ROOTDIR/$RELEASE-$ARCH/\" \"$CONTAINER_ROOTDIR/\"" } containerise_installation() { containerise_installation_$RELEASE "$@" } containerise_installation_bullseye() { local TTY_ID TTY_SERVICES I PROGNAME miniade_get_progname PROGNAME miniade_info "applying multiple small fixes ..." miniade_debug 10 "containerise_installation_bullseye: mknod'ing ttys in container ..." for TTY_ID in $(seq 1 $TTY_COUNT); do [ -e $CONTAINER_ROOTDIR/dev/tty$TTY_ID ] || miniade_evaler "mknod \"$CONTAINER_ROOTDIR/dev/tty$TTY_ID\" c 4 $TTY_ID" done miniade_debug 10 "containerise_installation_bullseye: writing /etc/inittab in container ..." { echo "id:3:initdefault:" echo "si::sysinit:/etc/init.d/rcS" for I in {0..6}; do echo "l$I:$I:wait:/etc/init.d/rc $I" done echo "z6:6:respawn:/sbin/sulogin" echo "1:2345:respawn:/sbin/getty 38400 console" for TTY_ID in $(seq 1 $TTY_COUNT); do echo "c$TTY_ID:12345:respawn:/sbin/getty 38400 tty$TTY_ID linux" done echo "p6::ctrlaltdel:/sbin/init 6" echo "p0::powerfail:/sbin/init 0" } > /tmp/$PROGNAME.$$.inittab miniade_evaler "cat /tmp/$PROGNAME.$$.inittab > $CONTAINER_ROOTDIR/etc/inittab" rm -f /tmp/$PROGNAME.$$.inittab # Symlink mtab miniade_debug 10 "containerise_installation_bullseye: fixing /etc/mtab in container ..." [ ! -h "$CONTAINER_ROOTDIR/etc/mtab" -a ! -e "$CONTAINER_ROOTDIR/etc/mtab" ] || miniade_evaler "rm \"$CONTAINER_ROOTDIR/etc/mtab\"" miniade_evaler "ln -s /proc/self/mounts \"$CONTAINER_ROOTDIR/etc/mtab\"" # Disable SELinux miniade_debug 10 "containerise_installation_bullseye: disabling SELinux in container ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/selinux\"" miniade_evaler "echo 0 > \"$CONTAINER_ROOTDIR/selinux/enforce\"" miniade_debug 10 "containerise_installation_bullseye: writing network interface config file in container ..." # Configure the network using the dhcp { echo "auto lo" echo "iface lo inet loopback" echo echo "auto eth0" echo "iface eth0 inet dhcp" } > /tmp/$PROGNAME.$$.interfaces miniade_evaler "cat /tmp/$PROGNAME.$$.interfaces > \"$CONTAINER_ROOTDIR/etc/network/interfaces\"" # Set the hostname miniade_debug 10 "containerise_installation_bullseye: setting hostname in container ..." miniade_evaler "echo \"$CONTAINER_NAME\" > \"$CONTAINER_ROOTDIR/etc/hostname\"" # Set up minimal /etc/hosts miniade_debug 10 "containerise_installation_bullseye: setting up minimal /etc/hosts in container ..." { echo "127.0.0.1 localhost" echo "127.0.1.1 $CONTAINER_NAME" echo echo "::1 localhost ip6-localhost ip6-loopback" echo "ff02::1 ip6-allnodes" echo "ff02::2 ip6-allrouters" } > /tmp/$PROGNAME.$$.hosts miniade_evaler "cat /tmp/$PROGNAME.$$.hosts > \"$CONTAINER_ROOTDIR/etc/hosts\"" rm -f /tmp/$PROGNAME.$$.hosts # Before reconfiguring some services, ensure that locale # is set else reconfiguring services gets noisy. If LANG # not set or set to C or C.* then use en_US.UTF-8. # but first reconfigure locales - so we get no noisy perl-warnings miniade_debug 10 "containerise_installation_bullseye: fixing locale in container ..." if [[ $LANG =~ ^(|C|C\..*)$ ]]; then miniade_evaler "echo \"en_US.UTF-8 UTF-8\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"en_US.UTF-8\" \"UTF-8\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=en_US.UTF-8\"" else ENCODING=$(locale charmap) [ "X$ENCODING" != X ] || ENCODING="UTF-8" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" sed -e \"s/^# \($LANG $ENCODING\)/\\1/\" -i /etc/locale.gen 2> /dev/null" miniade_evaler "echo \"$LANG $ENCODING\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"$LANG\" \"$ENCODING\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=$LANG\"" fi # Remove pointless services miniade_debug 10 "containerise_installation_bullseye: removing pointless services ..." #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f checkroot.sh disable" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f umountfs disable" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclock.sh disable 2>/dev/null" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclockfirst.sh disable" # Set up getty miniade_debug 10 "containerise_installation_bullseye: setting up getty ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/lib/systemd/system\"" miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants\"" # Fix getty-static-service as debootstrap does not install dbus if [ -e "$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service" ]; then miniade_debug 10 "containerise_installation_bullseye: fixing getty-static-service ..." TTY_SERVICES=$(for I in $(seq 2 $TTY_COUNT); do echo -n "getty@tty$I.service "; done) miniade_evaler "sed \"s/ getty@tty.*/ $TTY_SERVICES /g\" \"$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service\" | sed \"s/\\\\(tty2-tty\\\\)[5-9]/\\\\1$TTY_COUNT/g\" > \"$CONTAINER_ROOTDIR/etc/systemd/system/getty-static.service\"" fi # Setup getty service on the ttys we are going to allow in the # default config. Number should match lxc.tty.max for I in $(seq 1 $TTY_COUNT); do miniade_evaler "ln -sf ../getty@.service \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/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 for I in $(seq 0 $TTY_COUNT); do miniade_evaler "ln -sf /dev/null \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/container-getty@$I.service\"" done # This function has been copied and adapted from lxc-fedora miniade_debug 10 "containerise_installation_bullseye: fixing udev-related services ..." miniade_evaler "rm -f \"$CONTAINER_ROOTDIR/etc/systemd/system/default.target\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/udev.service" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/systemd-udevd.service" # Set default runlevel miniade_debug 10 "containerise_installation_bullseye: making multi-user target the default target ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target" # Avoid possible warning 'stdin not a tty'. Preserve single-commandedness of the thing # that gets substituted. miniade_evaler "[ ! -f \"$CONTAINER_ROOTDIR/root/.profile\" ] || sed -i -e \"s/^mesg n/{ [ ! -t 0 ] || mesg n; }/g\" \"$CONTAINER_ROOTDIR/root/.profile\"" # Disable more services not supported on containers. These don't crash # the container, but they do leave uply fail messages in startup. apt_and_systemctl_prologue miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-debug.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-tracing.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-config.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask systemd-journald-audit.socket" apt_and_systemctl_epilogue } containerise_installation_bookworm() { local TTY_ID TTY_SERVICES I PROGNAME miniade_get_progname PROGNAME miniade_info "applying multiple small fixes ..." miniade_debug 10 "containerise_installation_bookworm: mknod'ing ttys in container ..." for TTY_ID in $(seq 1 $TTY_COUNT); do [ -e $CONTAINER_ROOTDIR/dev/tty$TTY_ID ] || miniade_evaler "mknod \"$CONTAINER_ROOTDIR/dev/tty$TTY_ID\" c 4 $TTY_ID" done miniade_debug 10 "containerise_installation_bookworm: writing /etc/inittab in container ..." { echo "id:3:initdefault:" echo "si::sysinit:/etc/init.d/rcS" for I in {0..6}; do echo "l$I:$I:wait:/etc/init.d/rc $I" done echo "z6:6:respawn:/sbin/sulogin" echo "1:2345:respawn:/sbin/getty 38400 console" for TTY_ID in $(seq 1 $TTY_COUNT); do echo "c$TTY_ID:12345:respawn:/sbin/getty 38400 tty$TTY_ID linux" done echo "p6::ctrlaltdel:/sbin/init 6" echo "p0::powerfail:/sbin/init 0" } > /tmp/$PROGNAME.$$.inittab miniade_evaler "cat /tmp/$PROGNAME.$$.inittab > $CONTAINER_ROOTDIR/etc/inittab" rm -f /tmp/$PROGNAME.$$.inittab # Symlink mtab miniade_debug 10 "containerise_installation_bookworm: fixing /etc/mtab in container ..." [ ! -h "$CONTAINER_ROOTDIR/etc/mtab" -a ! -e "$CONTAINER_ROOTDIR/etc/mtab" ] || miniade_evaler "rm \"$CONTAINER_ROOTDIR/etc/mtab\"" miniade_evaler "ln -s /proc/self/mounts \"$CONTAINER_ROOTDIR/etc/mtab\"" # Disable SELinux miniade_debug 10 "containerise_installation_bookworm: disabling SELinux in container ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/selinux\"" miniade_evaler "echo 0 > \"$CONTAINER_ROOTDIR/selinux/enforce\"" miniade_debug 10 "containerise_installation_bookworm: writing network interface config file in container ..." # Configure the network using the dhcp { echo "auto lo" echo "iface lo inet loopback" echo echo "auto eth0" echo "iface eth0 inet dhcp" } > /tmp/$PROGNAME.$$.interfaces miniade_evaler "cat /tmp/$PROGNAME.$$.interfaces > \"$CONTAINER_ROOTDIR/etc/network/interfaces\"" # Set the hostname miniade_debug 10 "containerise_installation_bookworm: setting hostname in container ..." miniade_evaler "echo \"$CONTAINER_NAME\" > \"$CONTAINER_ROOTDIR/etc/hostname\"" # Set up minimal /etc/hosts miniade_debug 10 "containerise_installation_bookworm: setting up minimal /etc/hosts in container ..." { echo "127.0.0.1 localhost" echo "127.0.1.1 $CONTAINER_NAME" echo echo "::1 localhost ip6-localhost ip6-loopback" echo "ff02::1 ip6-allnodes" echo "ff02::2 ip6-allrouters" } > /tmp/$PROGNAME.$$.hosts miniade_evaler "cat /tmp/$PROGNAME.$$.hosts > \"$CONTAINER_ROOTDIR/etc/hosts\"" rm -f /tmp/$PROGNAME.$$.hosts # Before reconfiguring some services, ensure that locale # is set else reconfiguring services gets noisy. If LANG # not set or set to C or C.* then use en_US.UTF-8. # but first reconfigure locales - so we get no noisy perl-warnings miniade_debug 10 "containerise_installation_bookworm: fixing locale in container ..." if [[ $LANG =~ ^(|C|C\..*)$ ]]; then miniade_evaler "echo \"en_US.UTF-8 UTF-8\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"en_US.UTF-8\" \"UTF-8\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=en_US.UTF-8\"" else ENCODING=$(locale charmap) [ "X$ENCODING" != X ] || ENCODING="UTF-8" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" sed -e \"s/^# \($LANG $ENCODING\)/\\1/\" -i /etc/locale.gen 2> /dev/null" miniade_evaler "echo \"$LANG $ENCODING\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"$LANG\" \"$ENCODING\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=$LANG\"" fi # Remove pointless services miniade_debug 10 "containerise_installation_bookworm: removing pointless services ..." #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f checkroot.sh disable" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f umountfs disable" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclock.sh disable 2>/dev/null" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclockfirst.sh disable" # Set up getty miniade_debug 10 "containerise_installation_bookworm: setting up getty ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/lib/systemd/system\"" miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants\"" # Fix getty-static-service as debootstrap does not install dbus if [ -e "$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service" ]; then miniade_debug 10 "containerise_installation_bookworm: fixing getty-static-service ..." TTY_SERVICES=$(for I in $(seq 2 $TTY_COUNT); do echo -n "getty@tty$I.service "; done) miniade_evaler "sed \"s/ getty@tty.*/ $TTY_SERVICES /g\" \"$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service\" | sed \"s/\\\\(tty2-tty\\\\)[5-9]/\\\\1$TTY_COUNT/g\" > \"$CONTAINER_ROOTDIR/etc/systemd/system/getty-static.service\"" fi # Setup getty service on the ttys we are going to allow in the # default config. Number should match lxc.tty.max for I in $(seq 1 $TTY_COUNT); do miniade_evaler "ln -sf ../getty@.service \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/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 for I in $(seq 0 $TTY_COUNT); do miniade_evaler "ln -sf /dev/null \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/container-getty@$I.service\"" done # This function has been copied and adapted from lxc-fedora miniade_debug 10 "containerise_installation_bookworm: fixing udev-related services ..." miniade_evaler "rm -f \"$CONTAINER_ROOTDIR/etc/systemd/system/default.target\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/udev.service" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/systemd-udevd.service" # Set default runlevel miniade_debug 10 "containerise_installation_bookworm: making multi-user target the default target ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target" # Avoid possible warning 'stdin not a tty'. Preserve single-commandedness of the thing # that gets substituted. miniade_evaler "[ ! -f \"$CONTAINER_ROOTDIR/root/.profile\" ] || sed -i -e \"s/^mesg n/{ [ ! -t 0 ] || mesg n; }/g\" \"$CONTAINER_ROOTDIR/root/.profile\"" # Disable more services not supported on containers. These don't crash # the container, but they do leave uply fail messages in startup. apt_and_systemctl_prologue miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-debug.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-tracing.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-config.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask systemd-journald-audit.socket" apt_and_systemctl_epilogue } containerise_installation_trixie() { local TTY_ID TTY_SERVICES I PROGNAME miniade_get_progname PROGNAME miniade_info "applying multiple small fixes ..." # Almost all of this function is cloned from the official template, with style changes. miniade_debug 10 "containerise_installation_trixie: mknod'ing ttys in container ..." for TTY_ID in $(seq 1 $TTY_COUNT); do [ -e $CONTAINER_ROOTDIR/dev/tty$TTY_ID ] || miniade_evaler "mknod \"$CONTAINER_ROOTDIR/dev/tty$TTY_ID\" c 4 $TTY_ID" done miniade_debug 10 "containerise_installation_trixie: writing /etc/inittab in container ..." { echo "id:3:initdefault:" echo "si::sysinit:/etc/init.d/rcS" for I in {0..6}; do echo "l$I:$I:wait:/etc/init.d/rc $I" done echo "z6:6:respawn:/sbin/sulogin" echo "1:2345:respawn:/sbin/getty 38400 console" for TTY_ID in $(seq 1 $TTY_COUNT); do echo "c$TTY_ID:12345:respawn:/sbin/getty 38400 tty$TTY_ID linux" done echo "p6::ctrlaltdel:/sbin/init 6" echo "p0::powerfail:/sbin/init 0" } > /tmp/$PROGNAME.$$.inittab miniade_evaler "cat /tmp/$PROGNAME.$$.inittab > $CONTAINER_ROOTDIR/etc/inittab" rm -f /tmp/$PROGNAME.$$.inittab # Symlink mtab miniade_debug 10 "containerise_installation_trixie: fixing /etc/mtab in container ..." [ ! -h "$CONTAINER_ROOTDIR/etc/mtab" -a ! -e "$CONTAINER_ROOTDIR/etc/mtab" ] || miniade_evaler "rm \"$CONTAINER_ROOTDIR/etc/mtab\"" miniade_evaler "ln -s /proc/self/mounts \"$CONTAINER_ROOTDIR/etc/mtab\"" # Disable SELinux miniade_debug 10 "containerise_installation_trixie: disabling SELinux in container ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/selinux\"" miniade_evaler "echo 0 > \"$CONTAINER_ROOTDIR/selinux/enforce\"" miniade_debug 10 "containerise_installation_trixie: writing network interface config file in container ..." # Configure the network using the dhcp { echo "auto lo" echo "iface lo inet loopback" echo echo "auto eth0" echo "iface eth0 inet dhcp" } > /tmp/$PROGNAME.$$.interfaces miniade_evaler "cat /tmp/$PROGNAME.$$.interfaces > \"$CONTAINER_ROOTDIR/etc/network/interfaces\"" # Set the hostname miniade_debug 10 "containerise_installation_trixie: setting hostname in container ..." miniade_evaler "echo \"$CONTAINER_NAME\" > \"$CONTAINER_ROOTDIR/etc/hostname\"" # Set up minimal /etc/hosts miniade_debug 10 "containerise_installation_trixie: setting up minimal /etc/hosts in container ..." { echo "127.0.0.1 localhost" echo "127.0.1.1 $CONTAINER_NAME" echo echo "::1 localhost ip6-localhost ip6-loopback" echo "ff02::1 ip6-allnodes" echo "ff02::2 ip6-allrouters" } > /tmp/$PROGNAME.$$.hosts miniade_evaler "cat /tmp/$PROGNAME.$$.hosts > \"$CONTAINER_ROOTDIR/etc/hosts\"" rm -f /tmp/$PROGNAME.$$.hosts # Before reconfiguring some services, ensure that locale # is set else reconfiguring services gets noisy. If LANG # not set or set to C or C.* then use en_US.UTF-8. # but first reconfigure locales - so we get no noisy perl-warnings miniade_debug 10 "containerise_installation_trixie: fixing locale in container ..." if [[ $LANG =~ ^(|C|C\..*)$ ]]; then miniade_evaler "echo \"en_US.UTF-8 UTF-8\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"en_US.UTF-8\" \"UTF-8\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=en_US.UTF-8\"" else ENCODING=$(locale charmap) [ "X$ENCODING" != X ] || ENCODING="UTF-8" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" sed -e \"s/^# \($LANG $ENCODING\)/\\1/\" -i /etc/locale.gen 2> /dev/null" miniade_evaler "echo \"$LANG $ENCODING\" >> \"$CONTAINER_ROOTDIR/etc/locale.gen\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" locale-gen \"$LANG\" \"$ENCODING\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" update-locale \"LANG=$LANG\"" fi # Remove pointless services #miniade_debug 10 "containerise_installation_trixie: removing pointless services ..." #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f checkroot.sh disable" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f umountfs disable" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclock.sh disable 2>/dev/null" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" /usr/sbin/update-rc.d -f hwclockfirst.sh disable" # Regenerate ssh host keys if [ -x "$CONTAINER_ROOTDIR/var/lib/dpkg/info/openssh-server.postinst" ]; then miniade_debug 10 "containerise_installation_trixie: regenerating ssh host keys ..." [ ! -f "$CONTAINER_ROOTDIR/etc/init/ssh.conf" ] || mv "$CONTAINER_ROOTDIR/etc/init/ssh.conf" "$CONTAINER_ROOTDIR/etc/init/ssh.conf.disabled" rm -f "$CONTAINER_ROOTDIR/etc/ssh/"ssh_host_*key* apt_and_systemctl_prologue DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot "$CONTAINER_ROOTDIR" /var/lib/dpkg/info/openssh-server.postinst configure 2> /dev/null apt_and_systemctl_epilogue sed -i "s/root@$(hostname)/root@$CONTAINER_NAME/g" "$CONTAINER_ROOTDIR/etc/ssh/"ssh_host_*.pub [ ! -f "$CONTAINER_ROOTDIR/etc/init/ssh.conf.disabled" ] || mv "$CONTAINER_ROOTDIR/etc/init/ssh.conf.disabled" "$CONTAINER_ROOTDIR/etc/init/ssh.conf" fi # Set up getty miniade_debug 10 "containerise_installation_trixie: setting up getty ..." miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/lib/systemd/system\"" miniade_evaler "mkdir -p \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants\"" # Fix getty-static-service as debootstrap does not install dbus if [ -e "$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service" ]; then miniade_debug 10 "containerise_installation_trixie: fixing getty-static-service ..." TTY_SERVICES=$(for I in $(seq 2 $TTY_COUNT); do echo -n "getty@tty$I.service "; done) miniade_evaler "sed \"s/ getty@tty.*/ $TTY_SERVICES /g\" \"$CONTAINER_ROOTDIR/lib/systemd/system/getty-static.service\" | sed \"s/\\\\(tty2-tty\\\\)[5-9]/\\\\1$TTY_COUNT/g\" > \"$CONTAINER_ROOTDIR/etc/systemd/system/getty-static.service\"" fi # Setup getty service on the ttys we are going to allow in the # default config. Number should match lxc.tty.max for I in $(seq 1 $TTY_COUNT); do miniade_debug 10 "containerise_installation_trixie: enabling getty#$I ..." miniade_evaler "ln -sf ../getty@.service \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/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 for I in $(seq 0 $TTY_COUNT); do miniade_debug 10 "containerise_installation_trixie: masking container-getty#$I ..." miniade_evaler "ln -sf /dev/null \"$CONTAINER_ROOTDIR/etc/systemd/system/getty.target.wants/container-getty@$I.service\"" done # This function has been copied and adapted from lxc-fedora miniade_debug 10 "containerise_installation_trixie: fixing udev-related services ..." miniade_evaler "rm -f \"$CONTAINER_ROOTDIR/etc/systemd/system/default.target\"" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/udev.service" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /dev/null /etc/systemd/system/systemd-udevd.service" # Set default runlevel miniade_debug 10 "containerise_installation_trixie: making multi-user target the default target ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target" # Avoid possible warning 'stdin not a tty'. Preserve single-commandedness of the thing # that gets substituted. miniade_debug 10 "containerise_installation_trixie: fixing up ~root/.profile ..." miniade_evaler "[ ! -f \"$CONTAINER_ROOTDIR/root/.profile\" ] || sed -i -e \"s/^mesg n/{ [ ! -t 0 ] || mesg n; }/g\" \"$CONTAINER_ROOTDIR/root/.profile\"" # Disable more services not supported on containers. These don't crash # the container, but they do leave uply fail messages in startup. miniade_debug 10 "containerise_installation_trixie: masking container-unsupported services ..." apt_and_systemctl_prologue miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-debug.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-tracing.mount" miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask sys-kernel-config.mount" #miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" systemctl --quiet mask systemd-journald-audit.socket" apt_and_systemctl_epilogue } install_packages() { # Particularly when installing packages for testing/unstable releases, # the existing package cache may be out of date. Hence we *always* # update the package cache. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get update" || true install_packages_$RELEASE "$@" } install_packages_bullseye() { apt_and_systemctl_prologue miniade_info "installing packages ..." # Install (sensible) things that a PM/VM gets that a container doesn't. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y --no-install-recommends install vim-tiny cron whiptail cron-daemon-common logrotate debconf-i18n less nano shared-mime-info xdg-user-dirs procps iputils-ping openssh-server" || true # Remove things that a PM/VM doesn't get. dialog was needed above # but no longer. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y --purge remove dialog" || true apt_and_systemctl_epilogue } install_packages_bookworm() { apt_and_systemctl_prologue miniade_info "installing packages ..." # Install (sensible) things that a PM/VM gets that a container doesn't. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y --no-install-recommends install vim-tiny cron whiptail cron-daemon-common logrotate debconf-i18n less nano shared-mime-info xdg-user-dirs procps iputils-ping openssh-server" || true # Remove things that a PM/VM doesn't get. dialog was needed above # but no longer. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y --purge remove dialog" || true apt_and_systemctl_epilogue } install_packages_trixie() { miniade_info "installing packages found on pristine PM/KVM-VMs ..." apt_and_systemctl_prologue # See https://www.pasta.freemyip.com/computing/projects/debian-trixie-rollout/ # for how this list was derived. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y install dosfstools usr-is-merged whiptail" miniade_info "removing packages not found on pristine PM/KVM-VMs ..." # We remove packages using debfoster to prune also dependencies. This is more tricky than one # might think! This list of packages to install/remove was constructed # by diffing debfoster's keepers file. But if we were to remove *only* the "leaf" dependencies # that that reports, then internal nodes in the dependency tree would become leaves and would # then be reporred by debfoster (if we were to run debfoster again). So we use debfoster itself # to remove the packages as that also removes/prunes the dependency tree of those internal # leaves. Of course, debfoster isn't actually installed at the moment, so we need to install it. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" apt-get -y install debfoster" # Next, we don't want debfoster to remove any more that we ask it to, so we need to run # debfoster responding "keep everything installed. This is what 'debfoster -q' does. miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" debfoster -q" # Next, we remove the first few packages that we want to remove (and their prunable dependencies). # These are the packages that are simple to remove. We tell debfoster to call apt-get in purge mode. miniade_debug 10 "install_packages_trixie: removing apt-transport-https and openssh-server ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" debfoster -f -o RemoveCmd=\"apt-get -y --purge remove\" apt-transport-https- openssh-server-" # Next, we remove dialog. We do that only after the ones above because the pre-rm scripts for # the above may consider asking remove-time questions, using dialog. (In fact there are no questions # that get asked, but nonetheless, if dialog is removed before (or as part of) the above packages, # then there a lot of warnings like: # # debconf: unable to initialize frontend: Dialog # debconf: unable to initialize frontend: Readline # debconf: falling back to frontend: Teletype miniade_debug 10 "install_packages_trixie: removing dialog ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" debfoster -f -o RemoveCmd=\"apt-get -y --purge remove\" dialog-" # Next, we remove locales. We don't do it earlier because it triggers later installs/iremovals to # report: # # perl: warning: Setting locale failed. # perl: warning: Please check that your locale settings: # LANGUAGE = (unset), # LC_ALL = (unset), # ... # are supported and installed on your system. # perl: warning: Falling back to the standard locale ("C"). # # Though these warnings do not appear during the removal of locales itself. miniade_debug 10 "install_packages_trixie: removing locales ..." miniade_evaler "chroot \"$CONTAINER_ROOTDIR\" debfoster -f -o RemoveCmd=\"apt-get -y --purge remove\" locales-" # Finally, since we installed debfoster to help us remove packages, we need to remove it again. Again # we use debfoster itself to do this. In order to avoid the perl/locale warnings, we run it under LC_ALL=C. miniade_debug 10 "install_packages_trixie: removing debfoster ..." miniade_evaler "LC_ALL=C chroot \"$CONTAINER_ROOTDIR\" debfoster -f -o RemoveCmd=\"apt-get -y --purge remove\" debfoster-" apt_and_systemctl_epilogue } # These two functions are also in create-basic-lxc-cnt. Keep them aligned! apt_and_systemctl_prologue() { miniade_evaler "mount devpts -t devpts $CONTAINER_ROOTDIR/dev/pts" miniade_evaler "mount proc -t proc $CONTAINER_ROOTDIR/proc" miniade_evaler "mount sysfs -t sysfs $CONTAINER_ROOTDIR/sys" # Disable install-time service startup. miniade_evaler "echo -e \"#!/bin/bash\\nexit 101\" > \"$CONTAINER_ROOTDIR/usr/sbin/policy-rc.d\"" miniade_evaler "chmod +x \"$CONTAINER_ROOTDIR/usr/sbin/policy-rc.d\"" } apt_and_systemctl_epilogue() { miniade_evaler "rm \"$CONTAINER_ROOTDIR/usr/sbin/policy-rc.d\"" miniade_evaler "umount $CONTAINER_ROOTDIR/sys" miniade_evaler "umount $CONTAINER_ROOTDIR/proc" miniade_evaler "umount $CONTAINER_ROOTDIR/dev/pts" } flush_cache_if_requested() { $FLUSH_CACHE_FLAG || return 0 miniade_evaler "rm -fr \"$CACHE_ROOTDIR/$RELEASE-$ARCH\"" } download_debian_to_cache_if_necessary() { download_debian_to_cache_if_necessary_$RELEASE "$@" } download_debian_to_cache_if_necessary_bullseye() { local PACKAGES FOUND KEYRING # This is the same as the OS-provided lxc-debian's preferred package list # except that it does not include openssh-server, apt-transport-https # net-tools or ca-certificates. None of these packages gets installed on # a "task-less" pristine PM/VM. apt-utils was added as, even with dialog # installed, there are still complaints from debconf. PACKAGES=init,ifupdown,locales,isc-dhcp-client,netbase,iproute2,dialog,apt-utils [ ! -d "$CACHE_ROOTDIR/$RELEASE-$ARCH" ] || return 0 miniade_evaler "rm -fr \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "mkdir -p \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" # That directory will become / in the container, so it is important that # (1) it itself has the right permissions and (2) it does not have any # permissions that will be propogated from directory to subdirectory # automatically. On Debian 11 and 12, /var/local is setgid! It's typically # owned by root:staff too, so there's quite some fixing to do! miniade_evaler "chmod g-s,u-s \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chown root:root \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chmod 755 \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "[ \"X\$(stat -c %u:%g:%a \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\")\" = X0:0:755 ]" || internal "chmod/chown failed" FOUND=false for KEYRING in /usr/share/keyrings/debian-archive-keyring.gpg /etc/apt/trusted.gpg.d/debian-archive-$RELEASE-stable.gpg; do [ ! -f $KEYRING ] || { FOUND=true; break; } done $FOUND || miniade_error "keyring not found" miniade_info "calling debootstrap to download debian to cache ..." miniade_evaler "debootstrap --verbose --variant=minbase --arch=$ARCH --include=$PACKAGES --keyring=$KEYRING $RELEASE \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" $MIRROR" || miniade_error "debootstrap failed" miniade_evaler "mv \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" \"$CACHE_ROOTDIR/$RELEASE-$ARCH\"" } download_debian_to_cache_if_necessary_bookworm() { local PACKAGES FOUND KEYRING # This is the same as the OS-provided lxc-debian's preferred package list # except that it does not include openssh-server, apt-transport-https # net-tools or ca-certificates. None of these packages gets installed on # a "task-less" pristine PM/VM. apt-utils was added as, even with dialog # installed, there are still complaints from debconf. PACKAGES=init,ifupdown,locales,isc-dhcp-client,netbase,iproute2,dialog,apt-utils [ ! -d "$CACHE_ROOTDIR/$RELEASE-$ARCH" ] || return 0 miniade_evaler "rm -fr \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "mkdir -p \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" # That directory will become / in the container, so it is important that # (1) it itself has the right permissions and (2) it does not have any # permissions that will be propogated from directory to subdirectory # automatically. On Debian 11 and 12, /var/local is setgid! It's typically # owned by root:staff too, so there's quite some fixing to do! miniade_evaler "chmod g-s,u-s \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chown root:root \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chmod 755 \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "[ \"X\$(stat -c %u:%g:%a \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\")\" = X0:0:755 ]" || internal "chmod/chown failed" FOUND=false for KEYRING in /usr/share/keyrings/debian-archive-keyring.gpg /etc/apt/trusted.gpg.d/debian-archive-$RELEASE-stable.gpg; do [ ! -f $KEYRING ] || { FOUND=true; break; } done $FOUND || miniade_error "keyring not found" miniade_info "calling debootstrap to download debian to cache ..." miniade_evaler "debootstrap --verbose --variant=minbase --arch=$ARCH --include=$PACKAGES --keyring=$KEYRING $RELEASE \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" $MIRROR" || miniade_error "debootstrap failed" miniade_evaler "mv \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" \"$CACHE_ROOTDIR/$RELEASE-$ARCH\"" } download_debian_to_cache_if_necessary_trixie() { local PACKAGES FOUND KEYRING # For earlier releases, I told debootstrap to install a slightly # modified list of packages. But I've since decided that that that # is not this function's job. This function should call debootstrap # in as similar way as possible to what the OS-provided template # does. So this package list is *identical* to the list in the # OS-provided template. PACKAGES=init,ifupdown,locales,ca-certificates,dialog,isc-dhcp-client,netbase,net-tools,iproute2,openssh-server,apt-transport-https # We download into $CACHE_ROOTDIR/$RELEASE-$ARCH.partial and then rename # that to $CACHE_ROOTDIR/$RELEASE-$ARCH, which means if $CACHE_ROOTDIR/$RELEASE-$ARCH # exists already then the cache has already been populated. [ ! -d "$CACHE_ROOTDIR/$RELEASE-$ARCH" ] || return 0 miniade_debug 10 "download_debian_to_cache_if_necessary_trixie: creating download directory ..." miniade_evaler "rm -fr \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "mkdir -p \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" # That directory will become / in the container, so it is important that # (1) it itself has the right permissions and (2) it does not have any # permissions that will be propogated from directory to subdirectory # automatically. On Debian 11 and 12, /var/local is setgid! It's typically # owned by root:staff too, so there's quite some fixing to do! miniade_evaler "chmod g-s,u-s \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chown root:root \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "chmod 755 \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\"" miniade_evaler "[ \"X\$(stat -c %u:%g:%a \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\")\" = X0:0:755 ]" || internal "chmod/chown failed" miniade_debug 10 "download_debian_to_cache_if_necessary_trixie: locating keyring that will allow debootstrap to successfully install things within the container using apt-get ..." FOUND=false for KEYRING in /usr/share/keyrings/debian-archive-keyring.gpg /etc/apt/trusted.gpg.d/debian-archive-$RELEASE-stable.gpg; do [ ! -f $KEYRING ] || { FOUND=true; break; } done $FOUND || miniade_error "keyring not found" miniade_info "calling debootstrap to download debian to cache ..." DEBOOTSTRAP_CMDLINE="debootstrap --verbose --variant=minbase --arch=$ARCH --include=$PACKAGES --keyring=$KEYRING $RELEASE \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" $MIRROR" miniade_debug 10 "download_debian_to_cache_if_necessary_trixie: calling [$DEBOOTSTRAP_CMDLINE] ..." miniade_evaler "$DEBOOTSTRAP_CMDLINE" # As mentioned above we now slide the .partial directory into place. miniade_evaler "mv \"$CACHE_ROOTDIR/$RELEASE-$ARCH.partial\" \"$CACHE_ROOTDIR/$RELEASE-$ARCH\"" } main "$@"