#!/bin/sh # Copyright 2022 Helmut Grohne # SPDX-License-Identifier: MIT # shellcheck disable=SC2016 # Intentional quoting technique set -u ARCHITECTURE=$(dpkg --print-architecture) IMAGE=rootfs.ext2 INCLUDE_PACKAGES=init MIRROR="http://deb.debian.org/debian" SIZE=$((1024*1024*1024)) SSHKEY= SUITE=unstable VMNAME=testvm die() { echo "$*" 1>&2 exit 1 } usage() { die "usage: $0 [-a architecture] [-h hostname] [-k sshkey] [-m mirror] [-o output] [-p packages] [-r release] [-s size_in_GB] [-- mmdebstrap options]" } while test "$#" -gt 0; do case "$1" in -a) test "$#" -eq 1 && usage ARCHITECTURE=$2 shift 2 ;; -h) test "$#" -eq 1 && usage VMNAME=$2 shift 2 ;; -k) test "$#" -eq 1 && usage SSHKEY=$2 shift 2 ;; -m) test "$#" -eq 1 && usage MIRROR=$2 shift 2 ;; -o) test "$#" -eq 1 && usage IMAGE=$2 shift 2 ;; -p) test "$#" -eq 1 && usage INCLUDE_PACKAGES="$INCLUDE_PACKAGES,$2" shift 2 ;; -r) test "$#" -eq 1 && usage SUITE=$2 shift 2 ;; -s) test "$#" -eq 1 && usage SIZE=$(($2*1024*1024*1024)) shift 2 ;; --) shift break ;; *) usage ;; esac done if test -n "$SSHKEY" && ! test -f "$SSHKEY"; then die "error: ssh keyfile '$SSHKEY' not found" fi case "$SUITE" in jessie) DEBVER=8 ;; stretch) DEBVER=9 ;; buster) DEBVER=10 ;; bullseye|stable) DEBVER=11 ;; bookworm|testing) DEBVER=12 ;; trixie) DEBVER=13 ;; forky) DEBVER=14 ;; sid|unstable) DEBVER=999 ;; *) die "unrecognized Debian release: $SUITE" ;; esac KERNEL_SUFFIX=-$ARCHITECTURE case "$ARCHITECTURE" in amd64|arm64) KERNEL_SUFFIX="-cloud-$ARCHITECTURE" if test "$DEBVER" -le 9; then KERNEL_SUFFIX="-$ARCHITECTURE" fi ;; armhf) KERNEL_SUFFIX=-armmp ;; i386) KERNEL_SUFFIX=-686-pae ;; mips64el) KERNEL_SUFFIX=-5kc-malta ;; mipsel) KERNEL_SUFFIX=-4kc-malta ;; ppc64el) KERNEL_SUFFIX=-powerpc64le ;; esac INCLUDE_PACKAGES="$INCLUDE_PACKAGES,linux-image$KERNEL_SUFFIX" if test -n "$SSHKEY"; then INCLUDE_PACKAGES="$INCLUDE_PACKAGES,openssh-server" fi # add a DNS resolver if test "$DEBVER" -ge 9; then INCLUDE_PACKAGES="$INCLUDE_PACKAGES,libnss-resolve" fi if test "$DEBVER" -le 11; then set -- '--customize-hook=chroot "$1" systemctl enable systemd-resolved.service' "$@" fi if test "$DEBVER" -le 9; then set -- '--customize-hook=ln -fs ../run/systemd/resolve/resolv.conf "$1/etc/resolv.conf"' "$@" elif test "$DEBVER" -le 11; then set -- '--customize-hook=ln -fs ../run/systemd/resolve/stub-resolv.conf "$1/etc/resolv.conf"' "$@" fi # construct mmdebstrap options as $@: set -- \ --verbose \ --variant=apt \ --format=ext2 \ "--architecture=$ARCHITECTURE" \ "--include=$INCLUDE_PACKAGES" \ '--customize-hook=echo "LABEL=debvm / ext4 defaults 0 1" >"$1/etc/fstab"' \ "$@" # set up a hostname set -- \ "--customize-hook=echo $VMNAME >"'"$1/etc/hostname"' \ "--customize-hook=echo 127.0.0.1 localhost $VMNAME >"'"$1/etc/hosts"' \ "$@" # allow password-less root login set -- '--customize-hook=chroot "$1" passwd --delete root' "$@" # dhcp on all network interfaces SYSD_NET_MATCH='Name=en*\n' test "$DEBVER" -le 8 && SYSD_NET_MATCH="${SYSD_NET_MATCH}Name=eth*\\n" SYSD_NET_NET='DHCP=yes\n' # This anchor is included by default since bullseye. Fails DNSSEC validation when missing. test "$DEBVER" -le 11 && SYSD_NET_NET="${SYSD_NET_NET}DNSSECNegativeTrustAnchors=home.arpa\\n" set -- \ '--customize-hook=chroot "$1" systemctl enable systemd-networkd.service' \ "--customize-hook=printf \"[Match]\\n$SYSD_NET_MATCH\\n[Network]\\n$SYSD_NET_NET"'\n[DHCP]\nUseDomains=yes\n" > "$1/etc/systemd/network/20-wired.network"' \ "$@" # add ssh key for root if test -n "$SSHKEY"; then set -- \ '--customize-hook=mkdir -p "$1/root/.ssh"' \ "--customize-hook=upload $SSHKEY /root/.ssh/authorized_keys" \ "$@" fi set -- --skip=cleanup/apt "$@" # We need /var/lib/dpkg/available for dpkg --set-selections to work. set -- '--customize-hook=chroot "$1" apt-cache dumpavail | chroot "$1" dpkg --update-avail' "$@" if test "$DEBVER" -le 8; then # Use obsolete and expired keys. set -- '--keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg' "$@" set -- --aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"' "$@" # chfn does not work, because libpam-runtime.postinst is late setting up /etc/pam.d/common-auth et al, see #1026765 set -- --extract-hook='chroot "$1" pam-auth-update --package --force' "$@" fi if test "$DEBVER" -ge 12; then # Avoid the usrmerge package set -- --hook-dir=/usr/share/mmdebstrap/hooks/merged-usr "$@" fi # suite target mirror set -- "$@" "$SUITE" "$IMAGE" "deb $MIRROR $SUITE main" set -ex mmdebstrap "$@" truncate -s "$SIZE" "$IMAGE" /sbin/resize2fs "$IMAGE" /sbin/tune2fs -L debvm -i 0 -O extents,uninit_bg,dir_index,has_journal "$IMAGE" # Must fsck after tune2fs: https://ext4.wiki.kernel.org/index.php/UpgradeToExt4 /sbin/fsck.ext4 -fDp rootfs.ext2