#!/bin/bash # SPDX-License-Identifier: MIT LXC_SELF_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" LXC_BIN=/usr/local/bin : ${LXC_SUDO:=} : ${LXC_CONTAINER_RELEASE:=bookworm} : ${LXC_HOME:=/home} : ${LXC_VERBOSE:=false} source /etc/os-release function lxc_release() { echo $VERSION_CODENAME } function lxc_template_release() { echo lxc-helpers-$LXC_CONTAINER_RELEASE } function lxc_root() { local name="$1" echo /var/lib/lxc/$name/rootfs } function lxc_config() { local name="$1" echo /var/lib/lxc/$name/config } function lxc_container_run() { local name="$1" shift $LXC_SUDO lxc-attach --clear-env --name $name -- "$@" } function lxc_container_run_script_as() { local name="$1" local user="$2" local script="$3" $LXC_SUDO chmod +x $(lxc_root $name)$script $LXC_SUDO lxc-attach --name $name -- sudo --user $user $script } function lxc_container_run_script() { local name="$1" local script="$2" $LXC_SUDO chmod +x $(lxc_root $name)$script lxc_container_run $name $script } function lxc_container_inside() { local name="$1" shift lxc_container_run $name $LXC_BIN/lxc-helpers.sh "$@" } function lxc_container_user_install() { local name="$1" local user_id="$2" local user="$3" if test "$user" = root ; then return fi local root=$(lxc_root $name) if ! $LXC_SUDO grep --quiet "^$user " $root/etc/sudoers ; then $LXC_SUDO tee $root/usr/local/bin/lxc-helpers-create-user.sh > /dev/null <> /etc/sudoers sudo --user $user ssh-keygen -b 2048 -N '' -f $LXC_HOME/$user/.ssh/id_rsa EOF lxc_container_run_script $name /usr/local/bin/lxc-helpers-create-user.sh fi } function lxc_maybe_sudo() { if test $(id -u) != 0 ; then LXC_SUDO=sudo fi } function lxc_prepare_environment() { lxc_maybe_sudo if ! $(which lxc-create > /dev/null) ; then $LXC_SUDO apt-get install -y -qq make git libvirt0 libpam-cgfs bridge-utils uidmap dnsmasq-base dnsmasq dnsmasq-utils qemu-user-static fi } function lxc_container_configure() { local name="$1" $LXC_SUDO tee -a $(lxc_config $name) > /dev/null <<'EOF' security.nesting = true lxc.cap.drop = lxc.apparmor.profile = unconfined # # /dev/net (docker won't work without /dev/net/tun) # lxc.cgroup2.devices.allow = c 10:200 rwm lxc.mount.entry = /dev/net dev/net none bind,create=dir 0 0 # # /dev/kvm (libvirt / kvm won't work without /dev/kvm) # lxc.cgroup2.devices.allow = c 10:232 rwm lxc.mount.entry = /dev/kvm dev/kvm none bind,create=file 0 0 # # /dev/loop # lxc.cgroup2.devices.allow = c 10:237 rwm lxc.cgroup2.devices.allow = b 7:* rwm lxc.mount.entry = /dev/loop-control dev/loop-control none bind,create=file 0 0 # # /dev/mapper # lxc.cgroup2.devices.allow = c 10:236 rwm lxc.mount.entry = /dev/mapper dev/mapper none bind,create=dir 0 0 # # /dev/fuse # lxc.cgroup2.devices.allow = b 10:229 rwm lxc.mount.entry = /dev/fuse dev/fuse none bind,create=file 0 0 EOF # # Wait for the network to come up # local wait_networking=$(lxc_root $name)/usr/local/bin/lxc-helpers-wait-networking.sh $LXC_SUDO tee $wait_networking > /dev/null <<'EOF' #!/bin/sh -e for d in $(seq 60); do getent hosts wikipedia.org > /dev/null && break sleep 1 done getent hosts wikipedia.org > /dev/null || getent hosts wikipedia.org EOF $LXC_SUDO chmod +x $wait_networking } function lxc_container_mount() { local name="$1" local dir="$2" local config=$(lxc_config $name) if ! $LXC_SUDO grep --quiet "lxc.mount.entry = $dir" $config ; then local relative_dir=${dir##/} $LXC_SUDO tee -a $config > /dev/null <<< "lxc.mount.entry = $dir $relative_dir none bind,create=dir 0 0" fi } function lxc_container_start() { local name="$1" if lxc_running $name ; then return fi local logs if $LXC_VERBOSE; then logs="--logfile=/dev/tty" fi $LXC_SUDO lxc-start $logs $name $LXC_SUDO lxc-wait --name $name --state RUNNING lxc_container_run $name /usr/local/bin/lxc-helpers-wait-networking.sh } function lxc_container_stop() { local name="$1" $LXC_SUDO lxc-ls -1 --running --filter="^$name" | while read container ; do $LXC_SUDO lxc-stop --kill --name="$container" done } function lxc_container_destroy() { local name="$1" local root="$2" if lxc_exists "$name" ; then lxc_container_stop $name $root $LXC_SUDO lxc-destroy --force --name="$name" fi } function lxc_exists() { local name="$1" test "$($LXC_SUDO lxc-ls --filter=^$name)" } function lxc_running() { local name="$1" test "$($LXC_SUDO lxc-ls --running --filter=^$name)" } function lxc_build_template_release() { local name="$(lxc_template_release)" if lxc_exists $name ; then return fi local root=$(lxc_root $name) local packages="sudo,git,python3" $LXC_SUDO lxc-create --name $name --template debian -- --release=$LXC_CONTAINER_RELEASE --packages="$packages" $LXC_SUDO cp -a $LXC_SELF_DIR/lxc-helpers*.sh $root/$LXC_BIN lxc_container_configure $name } function lxc_build_template() { local name="$1" local newname="$2" if lxc_exists $newname ; then return fi if test "$name" = "$(lxc_template_release)" ; then lxc_build_template_release fi $LXC_SUDO lxc-copy --name=$name --newname=$newname } function lxc_apt_install() { local name="$1" shift lxc_container_inside $name lxc_apt_install_inside "$@" } function lxc_apt_install_inside() { DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$@" } function lxc_install_lxc() { local name="$1" local prefix="$2" lxc_container_inside $name lxc_install_lxc_inside $prefix } function lxc_install_lxc_inside() { local prefix="$1" local packages="make git libvirt0 libpam-cgfs bridge-utils uidmap dnsmasq-base dnsmasq dnsmasq-utils qemu-user-static" if test "$(lxc_release)" = bookworm ; then packages="$packages lxc-templates debootstrap distro-info" fi lxc_apt_install_inside $packages if ! systemctl is-active --quiet lxc-net; then systemctl disable --now dnsmasq apt-get install -y -qq lxc systemctl stop lxc-net sed -i -e '/ConditionVirtualization/d' $root/usr/lib/systemd/system/lxc-net.service systemctl daemon-reload cat >> /etc/default/lxc-net <