├── postinst └── collectd.conf ├── postinst.sh ├── install.sh ├── README.md └── preseed.cfg /postinst/collectd.conf: -------------------------------------------------------------------------------- 1 | LoadPlugin "cpu" 2 | LoadPlugin "memory" 3 | LoadPlugin "interface" 4 | LoadPlugin "disk" 5 | LoadPlugin "df" 6 | 7 | LoadPlugin "network" 8 | 9 | 10 | SecurityLevel "Encrypt" 11 | Username: "foo" 12 | Password: "bar" 13 | 14 | 15 | -------------------------------------------------------------------------------- /postinst.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script is run by debian installer using preseed/late_command 4 | # directive, see preseed.cfg 5 | 6 | # Setup console, remove timeout on boot. 7 | sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0"/g; s/TIMEOUT=5/TIMEOUT=0/g' /etc/default/grub 8 | update-grub 9 | 10 | # Members of `sudo` group are not asked for password. 11 | sed -i 's/%sudo\tALL=(ALL:ALL) ALL/%sudo\tALL=(ALL:ALL) NOPASSWD:ALL/g' /etc/sudoers 12 | 13 | # Empty message of the day. 14 | echo -n > /etc/motd 15 | 16 | # Unpack postinst tarball. 17 | tar -x -v -z -C/tmp -f /tmp/postinst.tar.gz 18 | 19 | # Install SSH key for pin. 20 | mkdir -m700 /home/pin/.ssh 21 | cat /tmp/postinst/authorized_keys > /home/pin/.ssh/authorized_keys 22 | chown -R pin:pin /home/pin/.ssh 23 | 24 | # Install collectd and config. 25 | #apt-get install -y collectd-core 26 | #cp /tmp/postinst/collectd.conf /etc/collectd/ 27 | 28 | # Remove some non-essential packages. 29 | DEBIAN_FRONTEND=noninteractive apt-get purge -y nano laptop-detect tasksel dictionaries-common emacsen-common iamerican ibritish ienglish-common ispell wamerican intel-microcode iucode-tool discover discover-data libdiscover2 libusb-1.0-0 30 | 31 | # Set domain name in hosts file 32 | #sed -i 's/127.0.1.1\t\([a-z]*\).*/127.0.1.1\t\1\.dp\-net\.com\t\1/' /etc/hosts 33 | 34 | # Avoid using DHCP-server provided domain name. 35 | #sed -i 's/#supersede.*/supersede domain-name "dp-net.com";/' /etc/dhcp/dhclient.conf 36 | 37 | # Do not install recommended packages by default. 38 | cat > /etc/apt/apt.conf.d/01norecommend << EOF 39 | APT::Install-Recommends "0"; 40 | APT::Install-Suggests "0"; 41 | EOF 42 | 43 | # Make "reboot" command work. 44 | apt-get install -y python3 dbus 45 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # Create debian VM as a KVM guest using virt-install in fully 4 | # automated way based on preseed.cfg 5 | 6 | # Domain is necessary to avoid debian installer to 7 | # require manual domain entry during the install. 8 | DOMAIN=`/bin/hostname -d` # Use domain of the host system 9 | #DOMAIN="dp-net.com" # Alternatively, hardcode domain 10 | # NB: See postinst.sh for ability to override domain received from 11 | # DHCP during the install. 12 | 13 | GITHUB_USERNAME='your-github-username' 14 | 15 | DIST_URL="http://ftp.debian.org/debian/dists/bookworm/main/installer-amd64/" 16 | LINUX_VARIANT="debiantesting" 17 | # NB: Also see preseed.cfg for debian mirror hostname. 18 | 19 | if [ $# -lt 1 ] 20 | then 21 | cat < [MAC_ADDRESS]" 23 | 24 | GUEST_NAME used as guest hostname, name of the VM and image file name 25 | MAC_ADDRESS allows to use specific MAC on the network, this is helpful 26 | when DHCP server expects your guest to have predefined MAC 27 | 28 | Examples: 29 | 30 | $0 backend 52:54:00:bf:b3:86 # create guest named "backend" with given MAC 31 | 32 | $0 wow # create guest named "wow" with random MAC 33 | EOF 34 | exit 1 35 | fi 36 | 37 | MAC="RANDOM" 38 | if [ $# -eq 2 ] 39 | then 40 | MAC=$2 41 | fi 42 | 43 | # Fetch SSH key from github. 44 | wget -q https://github.com/${GITHUB_USERNAME}.keys -O postinst/authorized_keys 45 | 46 | # Create tarball with some stuff we would like to install into the system. 47 | tar cvfz postinst.tar.gz postinst 48 | 49 | virt-install \ 50 | --connect=qemu:///system \ 51 | --name=${1} \ 52 | --ram=1024 \ 53 | --vcpus=2 \ 54 | --disk size=16,path=/var/lib/libvirt/images/${1}.img,bus=virtio,cache=none \ 55 | --initrd-inject=preseed.cfg \ 56 | --initrd-inject=postinst.sh \ 57 | --initrd-inject=postinst.tar.gz \ 58 | --location ${DIST_URL} \ 59 | --os-variant ${LINUX_VARIANT} \ 60 | --virt-type=kvm \ 61 | --controller usb,model=none \ 62 | --graphics none \ 63 | --noautoconsole \ 64 | --network bridge=br0,mac=${MAC},model=virtio \ 65 | --extra-args="auto=true hostname="${1}" domain="${DOMAIN}" console=tty0 console=ttyS0,115200n8 serial" 66 | 67 | rm postinst.tar.gz 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debian Bookworm unattended VM guest installer 2 | 3 | Simple script that uses **virt-install** and configures Debian installer 4 | for unattended installation and custom configuration using **preseed** 5 | config in order to create freshly installed Debian KVM guest. 6 | 7 | ``` 8 | Usage: ./install.sh [MAC_ADDRESS] 9 | 10 | GUEST_NAME used as guest hostname, name of the VM and image file name 11 | MAC_ADDRESS allows to use specific MAC on the network, this is helpful 12 | when DHCP server expects your guest to have predefined MAC 13 | ``` 14 | 15 | Guest OS is minimal no-GUI Debian installation configured with serial console 16 | for ability to `virsh console `, and OpenSSH server with your SSH 17 | key or/and password pre-configured. 18 | 19 | It is easy to change the script to add any extra packages and 20 | configuration files during unattended installation. The main point of 21 | sharing this script is to provide an example of unattended Debian VM 22 | creation or a base for your own script. 23 | 24 | Prerequisites 25 | ------------- 26 | ``` 27 | apt-get install wget virtinst libvirt-daemon-system qemu-system-x86 qemu-utils 28 | ``` 29 | 30 | Things to check before the first use 31 | ------------------------------------ 32 | * Set your login name and full name in `preseed.cfg`, update your GitHub name 33 | in `install.sh` in order to install your SSH key for authentication by guest. 34 | If you want to use different SSH key, not the one from GitHub, just put 35 | `authorized_keys` to `preseed` directory and remove `wget` command that 36 | fetches key from GitHub. 37 | Update your login name in `postinst.sh`, where SSH key is installed. 38 | * It's worth considering to enable password authentication in `preseed.cfg` 39 | at least during first run so you could `virsh console ` in case 40 | network connection in guest does not come up with DHCP or IP of the guest 41 | is unclear. 42 | * Check RAM size and disk size for the guest in arguments to `virst-install` in 43 | `install.sh` and modify them as needed. 44 | * Add `apt-get install -y ` or whatever you want to `postinst.sh` 45 | and any configuration files you want to add to the guest into `postinst` 46 | directory. 47 | 48 | Network configuration 49 | --------------------- 50 | Script works with bridged network, guests use DHCP and show up in local network. 51 | In case you want something else, replace `br0` in arguments to virt-install 52 | in `install.sh`. 53 | 54 | Before setting bridged network up: 55 | ``` 56 | apt-get install brigde-utils 57 | ``` 58 | 59 | Example of network configuration in `/etc/network/interfaces`: 60 | ``` 61 | auto lo 62 | iface lo inet loopback 63 | 64 | auto br0 65 | iface br0 inet dhcp 66 | bridge_hw eth0 67 | bridge_ports eth0 68 | bridge_stp off 69 | bridge_fd 1 70 | bridge_maxage 12 71 | ``` 72 | 73 | More Info 74 | --------- 75 | * https://www.debian.org/releases/stable/example-preseed.txt 76 | -------------------------------------------------------------------------------- /preseed.cfg: -------------------------------------------------------------------------------- 1 | # Preconfiguration file for jessie. 2 | # For more details see https://www.debian.org/releases/jessie/example-preseed.txt 3 | d-i debian-installer/locale string en_US 4 | d-i keyboard-configuration/xkb-keymap select us 5 | 6 | # Choose an network interface that has link if possible. 7 | d-i netcfg/choose_interface select auto 8 | 9 | # Disable that annoying WEP key dialog. 10 | d-i netcfg/wireless_wep string 11 | 12 | # Mirror settings. 13 | d-i mirror/country string manual 14 | d-i mirror/http/hostname string ftp.nl.debian.org 15 | d-i mirror/http/directory string /debian 16 | d-i mirror/http/proxy string 17 | 18 | # Root account setup. You can set password in plain-text or pre-encrypted. 19 | d-i passwd/root-login boolean false 20 | #d-i passwd/root-password password 98e1c23d2a5a2 21 | #d-i passwd/root-password-again password 98e1c23d2a5a2 22 | #d-i passwd/root-password-crypted password $6$1LCVFshS/kbYVg$M1QS1ZJ3.E7NkAD8sqkqhqExA2HWQ5/iDE.l23Xbr89Z7hTg/jUuBMyrYzANLmRybYcH8Smcy.yGDKMAX3okd0 # Use `mkpasswd -m sha-512` to generate password hash. 23 | 24 | # User account setup. 25 | #d-i passwd/make-user boolean false 26 | d-i passwd/user-fullname string Dmitri Popov 27 | d-i passwd/username string pin 28 | #d-i passwd/user-password password 236e95cd3901553 29 | #d-i passwd/user-password-again password 236e95cd3901553 30 | #d-i passwd/user-password-crypted password $6$dU9we2Mm$Btq1Tk1WkFx3/8YsXWbZr13m56uv0PabJKxk5teKAImLLQhtniOURXuOVLmbiBl0O3iS6xQBctNIc9Dn5b3vR. 31 | # Password login is disabled. 32 | d-i passwd/user-password-crypted password ! 33 | 34 | # Controls whether or not the hardware clock is set to UTC. 35 | d-i clock-setup/utc boolean true 36 | # See the contents of /usr/share/zoneinfo/ for valid values. 37 | d-i time/zone string Europe/Berlin 38 | # Controls whether to use NTP to set the clock during the install. 39 | d-i clock-setup/ntp boolean true 40 | 41 | # Simple non-LVM, all files in one partition. 42 | # For more examples see https://www.debian.org/releases/jessie/example-preseed.txt 43 | d-i partman-auto/method string regular 44 | d-i partman-auto/choose_recipe select atomic 45 | d-i partman-partitioning/confirm_write_new_label boolean true 46 | d-i partman/choose_partition select finish 47 | d-i partman/confirm boolean true 48 | d-i partman/confirm_nooverwrite boolean true 49 | 50 | # Do not install recommended packages by default. 51 | d-i base-installer/install-recommends boolean false 52 | tasksel tasksel/first multiselect 53 | 54 | # Individual additional packages to install. 55 | # ACPI packages are needed for `virsh shutdown ` to work. 56 | d-i pkgsel/include string openssh-server ca-certificates acpid acpi-support-base 57 | popularity-contest popularity-contest/participate boolean false 58 | 59 | # Bootloader installation. 60 | d-i grub-installer/bootdev string /dev/vda 61 | 62 | # Run postinst.sh in /target just before the install finishes. 63 | d-i preseed/late_command string cp postinst.sh postinst.tar.gz /target/tmp/ && chmod 755 /target/tmp/postinst.sh && in-target /tmp/postinst.sh 64 | 65 | # Avoid that last message about the install being complete. 66 | d-i finish-install/reboot_in_progress note 67 | --------------------------------------------------------------------------------