├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── Dockerfile ├── LICENSE ├── docker-entrypoint.sh ├── readme.md └── ubuntu ├── 16.04 ├── build-disk.sh ├── build-iso.sh └── custom │ ├── boot-menu.patch │ ├── preseed.cfg │ └── ssh-host-keygen.service ├── 18.04 ├── build-disk.sh ├── build-iso.sh └── custom │ ├── boot-menu.patch │ ├── isohdpfx.bin │ ├── preseed.cfg │ └── ssh-host-keygen.service └── 20.04 ├── build-disk.sh ├── build-iso.sh └── custom ├── boot-menu.patch ├── isohdpfx.bin ├── preseed.cfg └── ssh-host-keygen.service /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [coreprocess] 2 | 3 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #otechie: # Replace with a single Otechie username 12 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # This workflow runs tests on pull requests. 2 | name: tests 3 | 4 | on: 5 | pull_request: 6 | push: 7 | 8 | jobs: 9 | command-matrix: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | build-version: ["16.04", "18.04", "20.04"] 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Setup fake ssh key 17 | run: echo "dummyssh" > /tmp/dummy_id_rsa.pub 18 | - name: Install dependencies 19 | run: | 20 | sudo apt update 21 | sudo apt-get install dos2unix p7zip-full cpio gzip genisoimage whois pwgen wget fakeroot isolinux xorriso qemu-utils qemu-kvm 22 | - name: Build iso image 23 | run: ./ubuntu/${{ matrix.build-version }}/build-iso.sh /tmp/dummy_id_rsa.pub -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN export DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update 5 | RUN apt-get install -y cpio \ 6 | dos2unix \ 7 | fakeroot \ 8 | genisoimage \ 9 | git \ 10 | gzip \ 11 | isolinux \ 12 | p7zip-full \ 13 | pwgen \ 14 | wget \ 15 | whois \ 16 | xorriso 17 | 18 | COPY ubuntu ubuntu 19 | COPY docker-entrypoint.sh . 20 | 21 | ENTRYPOINT ["/docker-entrypoint.sh"] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Niklas Salmoukas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | readonly VERSION=${1:-18.04} 5 | exec /ubuntu/${VERSION}/build-iso.sh "$HOME/.ssh/id_rsa.pub" "/iso/ubuntu-${VERSION}-netboot-amd64-unattended.iso" 6 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Linux Unattended Installation 2 | 3 | This project provides all you need to create an unattended installation of a minimal setup of Linux, whereas *minimal* translates to the most lightweight setup - including an OpenSSH service and Python - which you can derive from the standard installer of a Linux distribution. The idea is, you will do all further deployment of your configurations and services with the help of Ansible or similar tools once you completed the minimal setup. 4 | 5 | ## Ubuntu 16.04 LTS, 18.04 LTS and 20.04 LTS 6 | 7 | Use the `build-iso.sh` script to create an ISO file based on the netsetup image of Ubuntu. 8 | 9 | Use the `build-disk.sh` script to create a cloneable preinstalled disk image based on the output of `build-iso.sh`. 10 | 11 | ### Features 12 | 13 | * Fully automated installation procedure. 14 | * Shutdown and power off when finished. We consider this a feature since it produces a defined and detectable state once the setup is complete. 15 | * Authentication based on SSH public key and not on a password. 16 | * Setup ensures about 25% of free disk space in the LVM group. We consider this a feature since it enables you to use LVM snapshots; e.g., for backup purposes. 17 | * Generates SSH server keys on first boot and not during setup stage. We consider this a feature since it enables you to use the installed image as a template for multiple machines. 18 | * Prints IPv4 and IPv6 address of the device on screen once booted. 19 | * USB bootable hybrid ISO image. 20 | * UEFI and BIOS mode supported. 21 | 22 | ### Prerequisites 23 | 24 | #### Linux 25 | 26 | Run `sudo apt-get install dos2unix p7zip-full cpio gzip genisoimage whois pwgen wget fakeroot isolinux xorriso` to install software tools required by the `build-iso.sh` script. 27 | 28 | Run `sudo apt-get install qemu-utils qemu-kvm` in addition to install software tools required by the `build-disk.sh` script. 29 | 30 | #### Mac (Ubuntu 18.04 LTS only) 31 | 32 | Run `brew install p7zip xorriso wget dos2unix fakeroot coreprocess/gnucpio/gnucpio` to install software tools required by the `build-iso.sh` script. 33 | 34 | The script `build-disk.sh` is not supported on Mac. 35 | 36 | #### Docker 37 | 38 | Run `docker build -t ubuntu-unattended .` to build the Docker image. 39 | 40 | When running the Docker container, add the public key you want to use and the ISO output directory as volume links and specify the desired Ubuntu version as parameter (defaults to 18.04), e.g: 41 | 42 | ```sh 43 | docker run \ 44 | --rm \ 45 | -t \ 46 | -v "$HOME/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub:ro" \ 47 | -v "$(pwd):/iso" \ 48 | ubuntu-unattended \ 49 | 16.04 50 | ``` 51 | 52 | Explanation of the command switches: 53 | ```sh 54 | --rm 55 | # Remove the Docker container when finished 56 | 57 | -t 58 | # Show terminal output 59 | 60 | -v "$HOME/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub:ro" 61 | # Mount "$HOME/.ssh/id_rsa.pub" from your machine to "/root/.ssh/id_rsa.pub" 62 | # in the container (read only). 63 | # This is the path, where the script expects your public key to be. 64 | 65 | -v "$(pwd):/iso" 66 | # Mount the current working directory from your machine to "/iso" 67 | # in the container. This is the path, where the ISO file is written to. 68 | ``` 69 | 70 | It is enough to build the container once. If you want to add a custom preseed config when executing `docker run`, mount your local copy of the file into the container, e.g: `-v "$(pwd)/my_preseed.cfg:/ubuntu//custom/preseed.cfg"`. 71 | 72 | The script `build-disk.sh` is not supported on Docker. 73 | 74 | ### Usage 75 | 76 | #### Build ISO images 77 | 78 | You can run the `build-iso.sh` script as regular user. No root permissions required. 79 | 80 | ```sh 81 | ./ubuntu//build-iso.sh 82 | ``` 83 | 84 | All parameters are optional. 85 | 86 | | Parameter | Description | Default Value | 87 | | :--- | :--- | :--- | 88 | | `` | The ssh public key to be placed in authorized_keys | `$HOME/.ssh/id_rsa.pub` | 89 | | `` | The path of the ISO image created by this script | `ubuntu--netboot-amd64-unattended.iso` | 90 | 91 | Boot the created ISO image on the target VM or physical machine. Be aware the setup will start within 10 seconds automatically and will reset the disk of the target device completely. The setup tries to eject the ISO/CD during its final stage. It usually works on physical machines, and it works on VirtualBox. It might not function in certain KVM environments in case the managing environment is not aware of the *eject event*. In that case, you have to detach the ISO image manually to prevent an unintended reinstall. 92 | 93 | Power-on the machine and log into it as root using your ssh key. The ssh host key will be generated on first boot. 94 | 95 | #### Build disk images 96 | 97 | You can run the `build-disk.sh` script as regular user. No root permissions required, if you are able to run `kvm` with your user account. 98 | 99 | ```sh 100 | ./ubuntu//build-disk.sh 101 | ``` 102 | 103 | All parameters are optional. 104 | 105 | | Parameter | Description | Default Value | 106 | | :--- | :--- | :--- | 107 | | `` | The RAM size used during setup routine in MB (might affect size of swap partition) | `2048` | 108 | | `` | The disk size of the disk image file to be created | `10G` | 109 | | `` | The format of the disk image file to be created (qcow2 or raw) | `qcow2` | 110 | | `` | The ssh public key to be placed in authorized_keys | `$HOME/.ssh/id_rsa.pub` | 111 | | `` | The path of the disk image created by this script | `ubuntu--amd64--.` | 112 | 113 | Use the generated disk image as template image and create copies of it to deploy virtual or physical machines. Do not boot the template itself, since the ssh host key will be generated on first boot. 114 | -------------------------------------------------------------------------------- /ubuntu/16.04/build-disk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_QEMU_IMG:=$(type -P qemu-img)}" 6 | : "${BIN_KVM:=$(type -P kvm)}" 7 | 8 | # get parameters 9 | RAM_SIZE=${1:-"2048"} 10 | DISK_SIZE=${2:-"10G"} 11 | DISK_FORMAT=${3:-"qcow2"} 12 | SSH_PUBLIC_KEY_FILE=${4:-"$HOME/.ssh/id_rsa.pub"} 13 | DISK_FILE=${5:-"`pwd`/ubuntu-16.04-amd64-$RAM_SIZE-$DISK_SIZE.$DISK_FORMAT"} 14 | 15 | # create iso 16 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 17 | TMP_ISO_DIR="`mktemp -d`" 18 | eval "$SCRIPT_DIR/build-iso.sh" "$SSH_PUBLIC_KEY_FILE" "$TMP_ISO_DIR/ubuntu-16.04-netboot-amd64-unattended.iso" 19 | 20 | # create image and run installer 21 | "$BIN_QEMU_IMG" create "$DISK_FILE" -f "$DISK_FORMAT" "$DISK_SIZE" 22 | "$BIN_KVM" -m "$RAM_SIZE" -cdrom "$TMP_ISO_DIR/ubuntu-16.04-netboot-amd64-unattended.iso" -boot once=d "$DISK_FILE" 23 | 24 | # remove tmp 25 | rm -r -f "$TMP_ISO_DIR" 26 | 27 | # done 28 | echo "Next steps: deploy image, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 29 | -------------------------------------------------------------------------------- /ubuntu/16.04/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_7Z:=$(type -P 7z)}" 6 | : "${BIN_XORRISO:=$(type -P xorriso)}" 7 | 8 | # get parameters 9 | SSH_PUBLIC_KEY_FILE=${1:-"$HOME/.ssh/id_rsa.pub"} 10 | TARGET_ISO=${2:-"`pwd`/ubuntu-16.04-netboot-amd64-unattended.iso"} 11 | 12 | # check if ssh key exists 13 | if [ ! -f "$SSH_PUBLIC_KEY_FILE" ]; 14 | then 15 | echo "Error: public SSH key $SSH_PUBLIC_KEY_FILE not found!" 16 | exit 1 17 | fi 18 | 19 | # get directories 20 | CURRENT_DIR="`pwd`" 21 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 22 | TMP_DOWNLOAD_DIR="`mktemp -d`" 23 | TMP_DISC_DIR="`mktemp -d`" 24 | TMP_INITRD_DIR="`mktemp -d`" 25 | 26 | # download and extract netboot iso 27 | SOURCE_ISO_URL="http://archive.ubuntu.com/ubuntu/dists/xenial-updates/main/installer-amd64/current/images/netboot/mini.iso" 28 | cd "$TMP_DOWNLOAD_DIR" 29 | wget -4 "$SOURCE_ISO_URL" -O "./netboot.iso" 30 | "$BIN_7Z" x "./netboot.iso" "-o$TMP_DISC_DIR" 31 | 32 | # patch boot menu 33 | cd "$TMP_DISC_DIR" 34 | dos2unix "./isolinux.cfg" 35 | patch -p1 -i "$SCRIPT_DIR/custom/boot-menu.patch" 36 | 37 | # prepare assets 38 | cd "$TMP_INITRD_DIR" 39 | mkdir "./custom" 40 | cp "$SCRIPT_DIR/custom/preseed.cfg" "./preseed.cfg" 41 | cp "$SSH_PUBLIC_KEY_FILE" "./custom/userkey.pub" 42 | cp "$SCRIPT_DIR/custom/ssh-host-keygen.service" "./custom/ssh-host-keygen.service" 43 | 44 | # append assets to initrd image 45 | cd "$TMP_INITRD_DIR" 46 | cat "$TMP_DISC_DIR/initrd.gz" | gzip -d > "./initrd" 47 | echo "./preseed.cfg" | fakeroot cpio -o -H newc -A -F "./initrd" 48 | find "./custom" | fakeroot cpio -o -H newc -A -F "./initrd" 49 | cat "./initrd" | gzip -9c > "$TMP_DISC_DIR/initrd.gz" 50 | 51 | # build iso 52 | cd "$TMP_DISC_DIR" 53 | rm -r '[BOOT]' 54 | "$BIN_XORRISO" -as mkisofs -r -V "ubuntu_1604_netboot_unattended" -J -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -input-charset utf-8 -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -o "$TARGET_ISO" ./ 55 | 56 | # go back to initial directory 57 | cd "$CURRENT_DIR" 58 | 59 | # delete all temporary directories 60 | rm -r "$TMP_DOWNLOAD_DIR" 61 | rm -r "$TMP_DISC_DIR" 62 | rm -r "$TMP_INITRD_DIR" 63 | 64 | # done 65 | echo "Next steps: install system, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 66 | -------------------------------------------------------------------------------- /ubuntu/16.04/custom/boot-menu.patch: -------------------------------------------------------------------------------- 1 | *** orig/isolinux.cfg 2016-10-12 04:45:19.000000000 +0200 2 | --- custom/isolinux.cfg 2017-02-12 00:36:09.201190417 +0100 3 | *************** 4 | *** 4,7 **** 5 | include menu.cfg 6 | default vesamenu.c32 7 | prompt 0 8 | ! timeout 0 9 | --- 4,7 ---- 10 | include menu.cfg 11 | default vesamenu.c32 12 | prompt 0 13 | ! timeout 100 14 | *** orig/boot/grub/grub.cfg 2018-04-25 22:11:06.000000000 +0200 15 | --- custom/boot/grub/grub.cfg 2018-06-01 22:47:30.953007353 +0200 16 | *************** 17 | *** 9,14 **** 18 | --- 9,15 ---- 19 | 20 | set menu_color_normal=white/black 21 | set menu_color_highlight=black/light-gray 22 | + set timeout=10 23 | 24 | menuentry "Install" { 25 | set gfxpayload=keep 26 | -------------------------------------------------------------------------------- /ubuntu/16.04/custom/preseed.cfg: -------------------------------------------------------------------------------- 1 | ### Preseed for Ubuntu 16.04 2 | # Derived from: https://help.ubuntu.com/lts/installation-guide/example-preseed.txt 3 | 4 | ### Compatibility 5 | # Tested with the netboot image: 6 | # http://archive.ubuntu.com/ubuntu/dists/xenial/main/installer-amd64/current/images/netboot/mini.iso 7 | # Might work with the regular image (not tested): 8 | # http://releases.ubuntu.com/16.04/ubuntu-16.04.4-server-amd64.iso 9 | 10 | ### Authentication 11 | # We recommend to use public key authentication and therefore disabled the root 12 | # password. The build-iso.sh script will do all the hard work for you. 13 | # 14 | # In case you still want to use password authentication, you need to set the 15 | # passwd/root-password-crypted variable. See below for further instructions. 16 | 17 | ### Usage 18 | # We recommend to use the build-iso.sh script to build an image with embedded 19 | # preseed and other required files. In that case the preseed file gets loaded 20 | # automatically and all additional files are available to the installer. 21 | # 22 | # In case you want to use a vanilla ISO image, please use the following boot 23 | # instructions to load the preseed file (press ESC and enter in boot prompt): 24 | # 25 | # install auto=true priority=critical url=https://raw.githubusercontent.com/coreprocess/linux-unattended-installation/master/ubuntu/16.04/custom/preseed.cfg 26 | # 27 | # Note: you will need to adjust the "preseed/late_command" variable to match 28 | # your environment in case you do not use the build-iso.sh script. 29 | 30 | 31 | ### Unattended Installation 32 | d-i auto-install/enable boolean true 33 | d-i debconf/priority select critical 34 | 35 | ### Localization 36 | d-i debian-installer/locale string en_US.UTF-8 37 | d-i localechooser/supported-locales multiselect en_US.UTF-8, de_DE.UTF-8 38 | d-i console-setup/ask_detect boolean false 39 | d-i keyboard-configuration/xkb-keymap select us 40 | 41 | ### Network configuration 42 | d-i netcfg/choose_interface select auto 43 | d-i netcfg/hostname string device 44 | d-i netcfg/get_hostname string device 45 | d-i netcfg/get_domain string unnamed 46 | d-i hw-detect/load_firmware boolean true 47 | 48 | ### Mirror settings 49 | d-i mirror/country string manual 50 | d-i mirror/http/hostname string archive.ubuntu.com 51 | d-i mirror/http/directory string /ubuntu 52 | d-i mirror/http/proxy string 53 | 54 | ### Account setup 55 | d-i passwd/root-login boolean true 56 | d-i passwd/root-password-crypted password !! 57 | d-i passwd/make-user boolean false 58 | 59 | # The root password is disabled by default. In case you want to use a root 60 | # password, please generate a password with the following command 61 | # printf "please-change-password" | mkpasswd -s -m sha-512 62 | # and update the passwd/root-password-crypted variable accordingly. Furthermore 63 | # you need to remove the call to "usermod" below. 64 | 65 | ### Clock and time zone setup 66 | d-i clock-setup/utc boolean true 67 | d-i time/zone string Etc/UTC 68 | d-i clock-setup/ntp boolean true 69 | d-i clock-setup/ntp-server string ntp.ubuntu.com 70 | 71 | ### Do NOT install on the USB stick(!) 72 | # 73 | # The Debian installer will install on the first disk it finds which can 74 | # sometimes be the USB stick itself. Work around this by rolling our own auto 75 | # detect logic which disallows installing on USB devices. 76 | d-i partman/early_command string \ 77 | USBDEV_LIST="$(mktemp)"; \ 78 | list-devices usb-partition | sed "s/\(.*\)./\1/" > "$USBDEV_LIST"; \ 79 | BOOTDEV="$(list-devices disk | grep -vf "$USBDEV_LIST" | head -n 1)"; \ 80 | debconf-set partman-auto/disk "$BOOTDEV"; \ 81 | debconf-set grub-installer/bootdev "$BOOTDEV"; 82 | 83 | ### Partitioning 84 | d-i preseed/early_command string umount /media || true 85 | d-i partman-auto/method string lvm 86 | d-i partman-auto-lvm/guided_size string max 87 | d-i partman-lvm/device_remove_lvm boolean true 88 | d-i partman-lvm/confirm boolean true 89 | d-i partman-lvm/confirm_nooverwrite boolean true 90 | d-i partman-auto-lvm/new_vg_name string main 91 | d-i partman-md/device_remove_md boolean true 92 | d-i partman-md/confirm boolean true 93 | d-i partman-partitioning/confirm_write_new_label boolean true 94 | d-i partman/choose_partition select finish 95 | d-i partman/confirm boolean true 96 | d-i partman/confirm_nooverwrite boolean true 97 | d-i partman-basicmethods/method_only boolean false 98 | 99 | ### GPT 100 | d-i partman-basicfilesystems/choose_label string gpt 101 | d-i partman-basicfilesystems/default_label string gpt 102 | d-i partman-partitioning/choose_label string gpt 103 | d-i partman-partitioning/default_label string gpt 104 | d-i partman/choose_label string gpt 105 | d-i partman/default_label string gpt 106 | 107 | ### EFI 108 | d-i partman-efi/non_efi_system boolean true 109 | 110 | ### Grub 111 | d-i grub-installer/only_debian boolean true 112 | d-i grub-installer/with_other_os boolean true 113 | 114 | ### Disk layout 115 | 116 | # Keep 25% free space 117 | d-i partman-auto/expert_recipe string \ 118 | boot-root :: \ 119 | 1 1 1 free \ 120 | $bios_boot{ } \ 121 | method{ biosgrub } \ 122 | . \ 123 | 256 256 256 fat32 \ 124 | $primary{ } \ 125 | $iflabel{ gpt } \ 126 | $reusemethod{ } \ 127 | method{ efi } format{ } \ 128 | mountpoint{ /boot/efi } \ 129 | . \ 130 | 512 512 512 ext4 \ 131 | $primary{ } \ 132 | $bootable{ } \ 133 | method{ format } format{ } \ 134 | use_filesystem{ } filesystem{ ext4 } \ 135 | mountpoint{ /boot } \ 136 | . \ 137 | 1024 102400000 1000000000 ext4 \ 138 | $lvmok{ } \ 139 | method{ format } format{ } \ 140 | use_filesystem{ } filesystem{ ext4 } \ 141 | mountpoint{ / } \ 142 | lv_name{ root } \ 143 | . \ 144 | 256 25600000 1000000000 ext4 \ 145 | $lvmok{ } \ 146 | method{ keep } \ 147 | lv_name{ placeholder } \ 148 | . \ 149 | 200% 200% 200% linux-swap \ 150 | $lvmok{ } \ 151 | method{ swap } format{ } \ 152 | lv_name{ swap } \ 153 | . 154 | 155 | # Use entire disk 156 | #d-i partman-auto/expert_recipe string \ 157 | # boot-root :: \ 158 | # 1 1 1 free \ 159 | # $bios_boot{ } \ 160 | # method{ biosgrub } \ 161 | # . \ 162 | # 256 256 256 fat32 \ 163 | # $primary{ } \ 164 | # $iflabel{ gpt } \ 165 | # $reusemethod{ } \ 166 | # method{ efi } format{ } \ 167 | # mountpoint{ /boot/efi } \ 168 | # . \ 169 | # 512 512 512 ext4 \ 170 | # $primary{ } \ 171 | # $bootable{ } \ 172 | # method{ format } format{ } \ 173 | # use_filesystem{ } filesystem{ ext4 } \ 174 | # mountpoint{ /boot } \ 175 | # . \ 176 | # 1024 102400000 1000000000 ext4 \ 177 | # $lvmok{ } \ 178 | # method{ format } format{ } \ 179 | # use_filesystem{ } filesystem{ ext4 } \ 180 | # mountpoint{ / } \ 181 | # lv_name{ root } \ 182 | # . \ 183 | # 200% 200% 200% linux-swap \ 184 | # $lvmok{ } \ 185 | # method{ swap } format{ } \ 186 | # lv_name{ swap } \ 187 | # . 188 | 189 | ### Base system installation 190 | d-i base-installer/install-recommends boolean true 191 | d-i base-installer/kernel/image string linux-generic 192 | 193 | ### Apt setup 194 | d-i apt-setup/restricted boolean true 195 | d-i apt-setup/universe boolean true 196 | d-i apt-setup/backports boolean true 197 | d-i apt-setup/use_mirror boolean false 198 | d-i apt-setup/services-select multiselect security, updates 199 | d-i apt-setup/security_host string security.ubuntu.com 200 | d-i apt-setup/security_path string /ubuntu 201 | 202 | ### Package selection 203 | d-i tasksel/first multiselect none 204 | d-i pkgsel/include string openssh-server python 205 | d-i pkgsel/upgrade select full-upgrade 206 | d-i pkgsel/update-policy select unattended-upgrades 207 | 208 | ### Finishing up the installation 209 | d-i preseed/late_command string \ 210 | cp -r /custom /target/custom; \ 211 | in-target sh -c 'lvremove -f --noudevsync main/placeholder || true'; \ 212 | in-target sh -c 'usermod -p "!" root'; \ 213 | in-target sh -c 'mkdir -p --mode=0700 /root/.ssh && cat /custom/userkey.pub > /root/.ssh/authorized_keys && chmod 0600 /root/.ssh/authorized_keys'; \ 214 | in-target sh -c 'sed -i "s/^#PermitRootLogin.*\$/PermitRootLogin prohibit-password/g" /etc/ssh/sshd_config'; \ 215 | in-target sh -c 'rm -f /etc/ssh/ssh_host_*_key* && mkdir -p /usr/lib/systemd/system && cp /custom/ssh-host-keygen.service /usr/lib/systemd/system/ssh-host-keygen.service && systemctl enable ssh-host-keygen.service'; \ 216 | in-target sh -c 'echo "IPv4: \\\4" >> /etc/issue && echo "IPv6: \\\6" >> /etc/issue && echo "" >> /etc/issue'; \ 217 | in-target sh -c 'eject || true'; \ 218 | rm -r /target/custom; 219 | d-i debian-installer/splash boolean false 220 | d-i cdrom-detect/eject boolean true 221 | 222 | ### Shutdown machine 223 | d-i finish-install/reboot_in_progress note 224 | d-i debian-installer/exit/poweroff boolean true 225 | -------------------------------------------------------------------------------- /ubuntu/16.04/custom/ssh-host-keygen.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenSSH Server Key Generation 3 | Before=ssh.service 4 | 5 | [Service] 6 | ExecStart=/usr/bin/ssh-keygen -A 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /ubuntu/18.04/build-disk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_QEMU_IMG:=$(type -P qemu-img)}" 6 | : "${BIN_KVM:=$(type -P kvm)}" 7 | 8 | # get parameters 9 | RAM_SIZE=${1:-"2048"} 10 | DISK_SIZE=${2:-"10G"} 11 | DISK_FORMAT=${3:-"qcow2"} 12 | SSH_PUBLIC_KEY_FILE=${4:-"$HOME/.ssh/id_rsa.pub"} 13 | DISK_FILE=${5:-"`pwd`/ubuntu-18.04-amd64-$RAM_SIZE-$DISK_SIZE.$DISK_FORMAT"} 14 | 15 | # create iso 16 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 17 | TMP_ISO_DIR="`mktemp -d`" 18 | eval "$SCRIPT_DIR/build-iso.sh" "$SSH_PUBLIC_KEY_FILE" "$TMP_ISO_DIR/ubuntu-18.04-netboot-amd64-unattended.iso" 19 | 20 | # create image and run installer 21 | "$BIN_QEMU_IMG" create "$DISK_FILE" -f "$DISK_FORMAT" "$DISK_SIZE" 22 | "$BIN_KVM" -m "$RAM_SIZE" -cdrom "$TMP_ISO_DIR/ubuntu-18.04-netboot-amd64-unattended.iso" -boot once=d "$DISK_FILE" 23 | 24 | # remove tmp 25 | rm -r -f "$TMP_ISO_DIR" 26 | 27 | # done 28 | echo "Next steps: deploy image, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 29 | -------------------------------------------------------------------------------- /ubuntu/18.04/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_7Z:=$(type -P 7z)}" 6 | : "${BIN_XORRISO:=$(type -P xorriso)}" 7 | : "${BIN_CPIO:=$(type -P gnucpio || type -P cpio)}" 8 | 9 | # get parameters 10 | SSH_PUBLIC_KEY_FILE=${1:-"$HOME/.ssh/id_rsa.pub"} 11 | TARGET_ISO=${2:-"`pwd`/ubuntu-18.04-netboot-amd64-unattended.iso"} 12 | 13 | # check if ssh key exists 14 | if [ ! -f "$SSH_PUBLIC_KEY_FILE" ]; 15 | then 16 | echo "Error: public SSH key $SSH_PUBLIC_KEY_FILE not found!" 17 | exit 1 18 | fi 19 | 20 | # get directories 21 | CURRENT_DIR="`pwd`" 22 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 23 | TMP_DOWNLOAD_DIR="`mktemp -d`" 24 | TMP_DISC_DIR="`mktemp -d`" 25 | TMP_INITRD_DIR="`mktemp -d`" 26 | 27 | # download and extract netboot iso 28 | SOURCE_ISO_URL="http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/current/images/netboot/mini.iso" 29 | cd "$TMP_DOWNLOAD_DIR" 30 | wget -4 "$SOURCE_ISO_URL" -O "./netboot.iso" 31 | "$BIN_7Z" x "./netboot.iso" "-o$TMP_DISC_DIR" 32 | 33 | # patch boot menu 34 | cd "$TMP_DISC_DIR" 35 | dos2unix "./isolinux.cfg" 36 | patch -p1 -i "$SCRIPT_DIR/custom/boot-menu.patch" 37 | 38 | # prepare assets 39 | cd "$TMP_INITRD_DIR" 40 | mkdir "./custom" 41 | cp "$SCRIPT_DIR/custom/preseed.cfg" "./preseed.cfg" 42 | cp "$SSH_PUBLIC_KEY_FILE" "./custom/userkey.pub" 43 | cp "$SCRIPT_DIR/custom/ssh-host-keygen.service" "./custom/ssh-host-keygen.service" 44 | 45 | # append assets to initrd image 46 | cd "$TMP_INITRD_DIR" 47 | cat "$TMP_DISC_DIR/initrd.gz" | gzip -d > "./initrd" 48 | echo "./preseed.cfg" | fakeroot "$BIN_CPIO" -o -H newc -A -F "./initrd" 49 | find "./custom" | fakeroot "$BIN_CPIO" -o -H newc -A -F "./initrd" 50 | cat "./initrd" | gzip -9c > "$TMP_DISC_DIR/initrd.gz" 51 | 52 | # build iso 53 | cd "$TMP_DISC_DIR" 54 | rm -r '[BOOT]' 55 | "$BIN_XORRISO" -as mkisofs -r -V "ubuntu_1804_netboot_unattended" -J -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -input-charset utf-8 -isohybrid-mbr "$SCRIPT_DIR/custom/isohdpfx.bin" -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -o "$TARGET_ISO" ./ 56 | 57 | # go back to initial directory 58 | cd "$CURRENT_DIR" 59 | 60 | # delete all temporary directories 61 | rm -r "$TMP_DOWNLOAD_DIR" 62 | rm -r "$TMP_DISC_DIR" 63 | rm -r "$TMP_INITRD_DIR" 64 | 65 | # done 66 | echo "Next steps: install system, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 67 | -------------------------------------------------------------------------------- /ubuntu/18.04/custom/boot-menu.patch: -------------------------------------------------------------------------------- 1 | *** orig/isolinux.cfg 2016-10-12 04:45:19.000000000 +0200 2 | --- custom/isolinux.cfg 2017-02-12 00:36:09.201190417 +0100 3 | *************** 4 | *** 4,7 **** 5 | include menu.cfg 6 | default vesamenu.c32 7 | prompt 0 8 | ! timeout 0 9 | --- 4,7 ---- 10 | include menu.cfg 11 | default vesamenu.c32 12 | prompt 0 13 | ! timeout 100 14 | *** orig/boot/grub/grub.cfg 2018-04-25 22:11:06.000000000 +0200 15 | --- custom/boot/grub/grub.cfg 2018-06-01 22:47:30.953007353 +0200 16 | *************** 17 | *** 9,14 **** 18 | --- 9,15 ---- 19 | 20 | set menu_color_normal=white/black 21 | set menu_color_highlight=black/light-gray 22 | + set timeout=10 23 | 24 | menuentry "Install" { 25 | set gfxpayload=keep 26 | -------------------------------------------------------------------------------- /ubuntu/18.04/custom/isohdpfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreprocess/linux-unattended-installation/68ebc5474fb13320a3ed07b4145ee9f287682d83/ubuntu/18.04/custom/isohdpfx.bin -------------------------------------------------------------------------------- /ubuntu/18.04/custom/preseed.cfg: -------------------------------------------------------------------------------- 1 | ### Preseed for Ubuntu 18.04 2 | # Derived from: https://help.ubuntu.com/lts/installation-guide/example-preseed.txt 3 | 4 | ### Compatibility 5 | # Tested with the netboot image: 6 | # http://archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/current/images/netboot/mini.iso 7 | # Might work with the regular image (not tested): 8 | # http://cdimage.ubuntu.com/releases/18.04/release/ubuntu-18.04-server-amd64.iso 9 | 10 | ### Authentication 11 | # We recommend to use public key authentication and therefore disabled the root 12 | # password. The build-iso.sh script will do all the hard work for you. 13 | # 14 | # In case you still want to use password authentication, you need to set the 15 | # passwd/root-password-crypted variable. See below for further instructions. 16 | 17 | ### Usage 18 | # We recommend to use the build-iso.sh script to build an image with embedded 19 | # preseed and other required files. In that case the preseed file gets loaded 20 | # automatically and all additional files are available to the installer. 21 | # 22 | # In case you want to use a vanilla ISO image, please use the following boot 23 | # instructions to load the preseed file (press ESC and enter in boot prompt): 24 | # 25 | # install auto=true priority=critical url=https://raw.githubusercontent.com/coreprocess/linux-unattended-installation/master/ubuntu/18.04/custom/preseed.cfg 26 | # 27 | # Note: you will need to adjust the "preseed/late_command" variable to match 28 | # your environment in case you do not use the build-iso.sh script. 29 | 30 | 31 | ### Unattended Installation 32 | d-i auto-install/enable boolean true 33 | d-i debconf/priority select critical 34 | 35 | ### Localization 36 | d-i debian-installer/locale string en_US.UTF-8 37 | d-i localechooser/supported-locales multiselect en_US.UTF-8, de_DE.UTF-8 38 | d-i console-setup/ask_detect boolean false 39 | d-i keyboard-configuration/xkb-keymap select us 40 | 41 | ### Network configuration 42 | d-i netcfg/choose_interface select auto 43 | d-i netcfg/hostname string device 44 | d-i netcfg/get_hostname string device 45 | d-i netcfg/get_domain string unnamed 46 | d-i hw-detect/load_firmware boolean true 47 | 48 | ### Mirror settings 49 | d-i mirror/country string manual 50 | d-i mirror/http/hostname string archive.ubuntu.com 51 | d-i mirror/http/directory string /ubuntu 52 | d-i mirror/http/proxy string 53 | 54 | ### Account setup 55 | d-i passwd/root-login boolean true 56 | d-i passwd/root-password-crypted password !! 57 | d-i passwd/make-user boolean false 58 | 59 | # The root password is disabled by default. In case you want to use a root 60 | # password, please generate a password with the following command 61 | # printf "please-change-password" | mkpasswd -s -m sha-512 62 | # and update the passwd/root-password-crypted variable accordingly. Furthermore 63 | # you need to remove the call to "usermod" below. 64 | 65 | ### Clock and time zone setup 66 | d-i clock-setup/utc boolean true 67 | d-i time/zone string Etc/UTC 68 | d-i clock-setup/ntp boolean true 69 | d-i clock-setup/ntp-server string ntp.ubuntu.com 70 | 71 | ### Do NOT install on the USB stick(!) 72 | # 73 | # The Debian installer will install on the first disk it finds which can 74 | # sometimes be the USB stick itself. Work around this by rolling our own auto 75 | # detect logic which disallows installing on USB devices. 76 | d-i partman/early_command string \ 77 | USBDEV_LIST="$(mktemp)"; \ 78 | list-devices usb-partition | sed "s/\(.*\)./\1/" > "$USBDEV_LIST"; \ 79 | BOOTDEV="$(list-devices disk | grep -vf "$USBDEV_LIST" | head -n 1)"; \ 80 | debconf-set partman-auto/disk "$BOOTDEV"; \ 81 | debconf-set grub-installer/bootdev "$BOOTDEV"; \ 82 | while /bin/true; do sleep 0.01; rm -f /target/etc/grub.d/30_os-prober; done & 83 | 84 | ### Partitioning 85 | d-i preseed/early_command string umount /media || true 86 | d-i partman-auto/method string lvm 87 | d-i partman-auto-lvm/guided_size string max 88 | d-i partman-lvm/device_remove_lvm boolean true 89 | d-i partman-lvm/confirm boolean true 90 | d-i partman-lvm/confirm_nooverwrite boolean true 91 | d-i partman-auto-lvm/new_vg_name string main 92 | d-i partman-md/device_remove_md boolean true 93 | d-i partman-md/confirm boolean true 94 | d-i partman-partitioning/confirm_write_new_label boolean true 95 | d-i partman/choose_partition select finish 96 | d-i partman/confirm boolean true 97 | d-i partman/confirm_nooverwrite boolean true 98 | d-i partman-basicmethods/method_only boolean false 99 | 100 | ### GPT 101 | d-i partman-basicfilesystems/choose_label string gpt 102 | d-i partman-basicfilesystems/default_label string gpt 103 | d-i partman-partitioning/choose_label string gpt 104 | d-i partman-partitioning/default_label string gpt 105 | d-i partman/choose_label string gpt 106 | d-i partman/default_label string gpt 107 | 108 | ### EFI 109 | d-i partman-efi/non_efi_system boolean true 110 | 111 | ### Grub 112 | d-i grub-installer/only_debian boolean true 113 | d-i grub-installer/with_other_os boolean true 114 | 115 | ### Disk layout 116 | 117 | # Keep 25% free space 118 | d-i partman-auto/expert_recipe string \ 119 | boot-root :: \ 120 | 1 1 1 free \ 121 | $bios_boot{ } \ 122 | method{ biosgrub } \ 123 | . \ 124 | 256 256 256 fat32 \ 125 | $primary{ } \ 126 | $iflabel{ gpt } \ 127 | $reusemethod{ } \ 128 | method{ efi } format{ } \ 129 | mountpoint{ /boot/efi } \ 130 | . \ 131 | 512 512 512 ext4 \ 132 | $primary{ } \ 133 | $bootable{ } \ 134 | method{ format } format{ } \ 135 | use_filesystem{ } filesystem{ ext4 } \ 136 | mountpoint{ /boot } \ 137 | . \ 138 | 1024 102400000 1000000000 ext4 \ 139 | $lvmok{ } \ 140 | method{ format } format{ } \ 141 | use_filesystem{ } filesystem{ ext4 } \ 142 | mountpoint{ / } \ 143 | lv_name{ root } \ 144 | . \ 145 | 256 25600000 1000000000 ext4 \ 146 | $lvmok{ } \ 147 | method{ keep } \ 148 | lv_name{ placeholder } \ 149 | . \ 150 | 200% 200% 200% linux-swap \ 151 | $lvmok{ } \ 152 | method{ swap } format{ } \ 153 | lv_name{ swap } \ 154 | . 155 | 156 | # Use entire disk 157 | #d-i partman-auto/expert_recipe string \ 158 | # boot-root :: \ 159 | # 1 1 1 free \ 160 | # $bios_boot{ } \ 161 | # method{ biosgrub } \ 162 | # . \ 163 | # 256 256 256 fat32 \ 164 | # $primary{ } \ 165 | # $iflabel{ gpt } \ 166 | # $reusemethod{ } \ 167 | # method{ efi } format{ } \ 168 | # mountpoint{ /boot/efi } \ 169 | # . \ 170 | # 512 512 512 ext4 \ 171 | # $primary{ } \ 172 | # $bootable{ } \ 173 | # method{ format } format{ } \ 174 | # use_filesystem{ } filesystem{ ext4 } \ 175 | # mountpoint{ /boot } \ 176 | # . \ 177 | # 1024 102400000 1000000000 ext4 \ 178 | # $lvmok{ } \ 179 | # method{ format } format{ } \ 180 | # use_filesystem{ } filesystem{ ext4 } \ 181 | # mountpoint{ / } \ 182 | # lv_name{ root } \ 183 | # . \ 184 | # 200% 200% 200% linux-swap \ 185 | # $lvmok{ } \ 186 | # method{ swap } format{ } \ 187 | # lv_name{ swap } \ 188 | # . 189 | 190 | ### Base system installation 191 | d-i base-installer/install-recommends boolean true 192 | d-i base-installer/kernel/image string linux-generic 193 | 194 | ### Apt setup 195 | d-i apt-setup/restricted boolean true 196 | d-i apt-setup/universe boolean true 197 | d-i apt-setup/backports boolean true 198 | d-i apt-setup/use_mirror boolean false 199 | d-i apt-setup/services-select multiselect security, updates 200 | d-i apt-setup/security_host string security.ubuntu.com 201 | d-i apt-setup/security_path string /ubuntu 202 | 203 | ### Package selection 204 | d-i tasksel/first multiselect none 205 | d-i pkgsel/include string openssh-server python 206 | d-i pkgsel/upgrade select full-upgrade 207 | d-i pkgsel/update-policy select unattended-upgrades 208 | 209 | ### Finishing up the installation 210 | d-i preseed/late_command string \ 211 | cp -r /custom /target/custom; \ 212 | in-target sh -c 'lvremove -f --noudevsync main/placeholder || true'; \ 213 | in-target sh -c 'usermod -p "!" root'; \ 214 | in-target sh -c 'mkdir -p --mode=0700 /root/.ssh && cat /custom/userkey.pub > /root/.ssh/authorized_keys && chmod 0600 /root/.ssh/authorized_keys'; \ 215 | in-target sh -c 'sed -i "s/^#PermitRootLogin.*\$/PermitRootLogin prohibit-password/g" /etc/ssh/sshd_config'; \ 216 | in-target sh -c 'rm -f /etc/ssh/ssh_host_*_key* && mkdir -p /usr/lib/systemd/system && cp /custom/ssh-host-keygen.service /usr/lib/systemd/system/ssh-host-keygen.service && systemctl enable ssh-host-keygen.service'; \ 217 | in-target sh -c 'echo "IPv4: \\\4" >> /etc/issue && echo "IPv6: \\\6" >> /etc/issue && echo "" >> /etc/issue'; \ 218 | in-target sh -c 'eject || true'; \ 219 | rm -r /target/custom; 220 | d-i debian-installer/splash boolean false 221 | d-i cdrom-detect/eject boolean true 222 | 223 | ### Shutdown machine 224 | d-i finish-install/reboot_in_progress note 225 | d-i debian-installer/exit/poweroff boolean true 226 | -------------------------------------------------------------------------------- /ubuntu/18.04/custom/ssh-host-keygen.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenSSH Server Key Generation 3 | Before=ssh.service 4 | 5 | [Service] 6 | ExecStart=/usr/bin/ssh-keygen -A 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /ubuntu/20.04/build-disk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_QEMU_IMG:=$(type -P qemu-img)}" 6 | : "${BIN_KVM:=$(type -P kvm)}" 7 | 8 | # get parameters 9 | RAM_SIZE=${1:-"2048"} 10 | DISK_SIZE=${2:-"10G"} 11 | DISK_FORMAT=${3:-"qcow2"} 12 | SSH_PUBLIC_KEY_FILE=${4:-"$HOME/.ssh/id_rsa.pub"} 13 | DISK_FILE=${5:-"`pwd`/ubuntu-20.04-amd64-$RAM_SIZE-$DISK_SIZE.$DISK_FORMAT"} 14 | 15 | # create iso 16 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 17 | TMP_ISO_DIR="`mktemp -d`" 18 | eval "$SCRIPT_DIR/build-iso.sh" "$SSH_PUBLIC_KEY_FILE" "$TMP_ISO_DIR/ubuntu-20.04-netboot-amd64-unattended.iso" 19 | 20 | # create image and run installer 21 | "$BIN_QEMU_IMG" create "$DISK_FILE" -f "$DISK_FORMAT" "$DISK_SIZE" 22 | "$BIN_KVM" -m "$RAM_SIZE" -cdrom "$TMP_ISO_DIR/ubuntu-20.04-netboot-amd64-unattended.iso" -boot once=d "$DISK_FILE" 23 | 24 | # remove tmp 25 | rm -r -f "$TMP_ISO_DIR" 26 | 27 | # done 28 | echo "Next steps: deploy image, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 29 | -------------------------------------------------------------------------------- /ubuntu/20.04/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # lookup specific binaries 5 | : "${BIN_7Z:=$(type -P 7z)}" 6 | : "${BIN_XORRISO:=$(type -P xorriso)}" 7 | : "${BIN_CPIO:=$(type -P gnucpio || type -P cpio)}" 8 | 9 | # get parameters 10 | SSH_PUBLIC_KEY_FILE=${1:-"$HOME/.ssh/id_rsa.pub"} 11 | TARGET_ISO=${2:-"`pwd`/ubuntu-20.04-netboot-amd64-unattended.iso"} 12 | 13 | # check if ssh key exists 14 | if [ ! -f "$SSH_PUBLIC_KEY_FILE" ]; 15 | then 16 | echo "Error: public SSH key $SSH_PUBLIC_KEY_FILE not found!" 17 | exit 1 18 | fi 19 | 20 | # get directories 21 | CURRENT_DIR="`pwd`" 22 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 23 | TMP_DOWNLOAD_DIR="`mktemp -d`" 24 | TMP_DISC_DIR="`mktemp -d`" 25 | TMP_INITRD_DIR="`mktemp -d`" 26 | 27 | # download and extract netboot iso 28 | SOURCE_ISO_URL="http://archive.ubuntu.com/ubuntu/dists/focal/main/installer-amd64/current/legacy-images/netboot/mini.iso" 29 | cd "$TMP_DOWNLOAD_DIR" 30 | wget -4 "$SOURCE_ISO_URL" -O "./netboot.iso" 31 | "$BIN_7Z" x "./netboot.iso" "-o$TMP_DISC_DIR" 32 | 33 | # patch boot menu 34 | cd "$TMP_DISC_DIR" 35 | dos2unix "./isolinux.cfg" 36 | patch -p1 -i "$SCRIPT_DIR/custom/boot-menu.patch" 37 | 38 | # prepare assets 39 | cd "$TMP_INITRD_DIR" 40 | mkdir "./custom" 41 | cp "$SCRIPT_DIR/custom/preseed.cfg" "./preseed.cfg" 42 | cp "$SSH_PUBLIC_KEY_FILE" "./custom/userkey.pub" 43 | cp "$SCRIPT_DIR/custom/ssh-host-keygen.service" "./custom/ssh-host-keygen.service" 44 | 45 | # append assets to initrd image 46 | cd "$TMP_INITRD_DIR" 47 | cat "$TMP_DISC_DIR/initrd.gz" | gzip -d > "./initrd" 48 | echo "./preseed.cfg" | fakeroot "$BIN_CPIO" -o -H newc -A -F "./initrd" 49 | find "./custom" | fakeroot "$BIN_CPIO" -o -H newc -A -F "./initrd" 50 | cat "./initrd" | gzip -9c > "$TMP_DISC_DIR/initrd.gz" 51 | 52 | # build iso 53 | cd "$TMP_DISC_DIR" 54 | rm -r '[BOOT]' 55 | "$BIN_XORRISO" -as mkisofs -r -V "ubuntu_2004_netboot_unattended" -J -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -input-charset utf-8 -isohybrid-mbr "$SCRIPT_DIR/custom/isohdpfx.bin" -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -o "$TARGET_ISO" ./ 56 | 57 | # go back to initial directory 58 | cd "$CURRENT_DIR" 59 | 60 | # delete all temporary directories 61 | rm -r "$TMP_DOWNLOAD_DIR" 62 | rm -r "$TMP_DISC_DIR" 63 | rm -r "$TMP_INITRD_DIR" 64 | 65 | # done 66 | echo "Next steps: install system, login via root, adjust the authorized keys, set a root password (if you want to), deploy via ansible (if applicable), enjoy!" 67 | -------------------------------------------------------------------------------- /ubuntu/20.04/custom/boot-menu.patch: -------------------------------------------------------------------------------- 1 | *** orig/isolinux.cfg 2016-10-12 04:45:19.000000000 +0200 2 | --- custom/isolinux.cfg 2017-02-12 00:36:09.201190417 +0100 3 | *************** 4 | *** 4,7 **** 5 | include menu.cfg 6 | default vesamenu.c32 7 | prompt 0 8 | ! timeout 0 9 | --- 4,7 ---- 10 | include menu.cfg 11 | default vesamenu.c32 12 | prompt 0 13 | ! timeout 100 14 | *** orig/boot/grub/grub.cfg 2018-04-25 22:11:06.000000000 +0200 15 | --- custom/boot/grub/grub.cfg 2018-06-01 22:47:30.953007353 +0200 16 | *************** 17 | *** 9,14 **** 18 | --- 9,15 ---- 19 | 20 | set menu_color_normal=white/black 21 | set menu_color_highlight=black/light-gray 22 | + set timeout=10 23 | 24 | menuentry "Install" { 25 | set gfxpayload=keep 26 | -------------------------------------------------------------------------------- /ubuntu/20.04/custom/isohdpfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreprocess/linux-unattended-installation/68ebc5474fb13320a3ed07b4145ee9f287682d83/ubuntu/20.04/custom/isohdpfx.bin -------------------------------------------------------------------------------- /ubuntu/20.04/custom/preseed.cfg: -------------------------------------------------------------------------------- 1 | ### Preseed for Ubuntu 20.04 2 | # Derived from: https://help.ubuntu.com/lts/installation-guide/example-preseed.txt 3 | 4 | ### Compatibility 5 | # Tested with the netboot image: 6 | # http://archive.ubuntu.com/ubuntu/dists/focal/main/installer-amd64/current/legacy-images/netboot/mini.iso 7 | 8 | ### Authentication 9 | # We recommend to use public key authentication and therefore disabled the root 10 | # password. The build-iso.sh script will do all the hard work for you. 11 | # 12 | # In case you still want to use password authentication, you need to set the 13 | # passwd/root-password-crypted variable. See below for further instructions. 14 | 15 | ### Usage 16 | # We recommend to use the build-iso.sh script to build an image with embedded 17 | # preseed and other required files. In that case the preseed file gets loaded 18 | # automatically and all additional files are available to the installer. 19 | # 20 | # In case you want to use a vanilla ISO image, please use the following boot 21 | # instructions to load the preseed file (press ESC and enter in boot prompt): 22 | # 23 | # install auto=true priority=critical url=https://raw.githubusercontent.com/coreprocess/linux-unattended-installation/master/ubuntu/20.04/custom/preseed.cfg 24 | # 25 | # Note: you will need to adjust the "preseed/late_command" variable to match 26 | # your environment in case you do not use the build-iso.sh script. 27 | 28 | 29 | ### Unattended Installation 30 | d-i auto-install/enable boolean true 31 | d-i debconf/priority select critical 32 | 33 | ### Localization 34 | d-i debian-installer/locale string en_US.UTF-8 35 | d-i localechooser/supported-locales multiselect en_US.UTF-8, de_DE.UTF-8 36 | d-i console-setup/ask_detect boolean false 37 | d-i keyboard-configuration/xkb-keymap select us 38 | 39 | ### Network configuration 40 | d-i netcfg/choose_interface select auto 41 | d-i netcfg/hostname string device 42 | d-i netcfg/get_hostname string device 43 | d-i netcfg/get_domain string unnamed 44 | d-i hw-detect/load_firmware boolean true 45 | 46 | ### Mirror settings 47 | d-i mirror/country string manual 48 | d-i mirror/http/hostname string archive.ubuntu.com 49 | d-i mirror/http/directory string /ubuntu 50 | d-i mirror/http/proxy string 51 | 52 | ### Account setup 53 | d-i passwd/root-login boolean true 54 | d-i passwd/root-password-crypted password !! 55 | d-i passwd/make-user boolean false 56 | 57 | # The root password is disabled by default. In case you want to use a root 58 | # password, please generate a password with the following command 59 | # printf "please-change-password" | mkpasswd -s -m sha-512 60 | # and update the passwd/root-password-crypted variable accordingly. Furthermore 61 | # you need to remove the call to "usermod" below. 62 | 63 | ### Clock and time zone setup 64 | d-i clock-setup/utc boolean true 65 | d-i time/zone string Etc/UTC 66 | d-i clock-setup/ntp boolean true 67 | d-i clock-setup/ntp-server string ntp.ubuntu.com 68 | 69 | ### Do NOT install on the USB stick(!) 70 | # 71 | # The Debian installer will install on the first disk it finds which can 72 | # sometimes be the USB stick itself. Work around this by rolling our own auto 73 | # detect logic which disallows installing on USB devices. 74 | d-i partman/early_command string \ 75 | USBDEV_LIST="$(mktemp)"; \ 76 | list-devices usb-partition | sed "s/\(.*\)./\1/" > "$USBDEV_LIST"; \ 77 | BOOTDEV="$(list-devices disk | grep -vf "$USBDEV_LIST" | head -n 1)"; \ 78 | debconf-set partman-auto/disk "$BOOTDEV"; \ 79 | debconf-set grub-installer/bootdev "$BOOTDEV"; \ 80 | while /bin/true; do sleep 0.01; rm -f /target/etc/grub.d/30_os-prober; done & 81 | 82 | ### Partitioning 83 | d-i preseed/early_command string umount /media || true 84 | d-i partman-auto/method string lvm 85 | d-i partman-auto-lvm/guided_size string max 86 | d-i partman-lvm/device_remove_lvm boolean true 87 | d-i partman-lvm/confirm boolean true 88 | d-i partman-lvm/confirm_nooverwrite boolean true 89 | d-i partman-auto-lvm/new_vg_name string main 90 | d-i partman-md/device_remove_md boolean true 91 | d-i partman-md/confirm boolean true 92 | d-i partman-partitioning/confirm_write_new_label boolean true 93 | d-i partman/choose_partition select finish 94 | d-i partman/confirm boolean true 95 | d-i partman/confirm_nooverwrite boolean true 96 | d-i partman-basicmethods/method_only boolean false 97 | 98 | ### GPT 99 | d-i partman-basicfilesystems/choose_label string gpt 100 | d-i partman-basicfilesystems/default_label string gpt 101 | d-i partman-partitioning/choose_label string gpt 102 | d-i partman-partitioning/default_label string gpt 103 | d-i partman/choose_label string gpt 104 | d-i partman/default_label string gpt 105 | 106 | ### EFI 107 | d-i partman-efi/non_efi_system boolean true 108 | 109 | ### Grub 110 | d-i grub-installer/only_debian boolean true 111 | d-i grub-installer/with_other_os boolean true 112 | 113 | ### Disk layout 114 | 115 | # Keep 25% free space 116 | d-i partman-auto/expert_recipe string \ 117 | boot-root :: \ 118 | 1 1 1 free \ 119 | $bios_boot{ } \ 120 | method{ biosgrub } \ 121 | . \ 122 | 256 256 256 fat32 \ 123 | $primary{ } \ 124 | $iflabel{ gpt } \ 125 | $reusemethod{ } \ 126 | method{ efi } format{ } \ 127 | mountpoint{ /boot/efi } \ 128 | . \ 129 | 512 512 512 ext4 \ 130 | $primary{ } \ 131 | $bootable{ } \ 132 | method{ format } format{ } \ 133 | use_filesystem{ } filesystem{ ext4 } \ 134 | mountpoint{ /boot } \ 135 | . \ 136 | 1024 102400000 1000000000 ext4 \ 137 | $lvmok{ } \ 138 | method{ format } format{ } \ 139 | use_filesystem{ } filesystem{ ext4 } \ 140 | mountpoint{ / } \ 141 | lv_name{ root } \ 142 | . \ 143 | 256 25600000 1000000000 ext4 \ 144 | $lvmok{ } \ 145 | method{ keep } \ 146 | lv_name{ placeholder } \ 147 | . \ 148 | 200% 200% 200% linux-swap \ 149 | $lvmok{ } \ 150 | method{ swap } format{ } \ 151 | lv_name{ swap } \ 152 | . 153 | 154 | # Use entire disk 155 | #d-i partman-auto/expert_recipe string \ 156 | # boot-root :: \ 157 | # 1 1 1 free \ 158 | # $bios_boot{ } \ 159 | # method{ biosgrub } \ 160 | # . \ 161 | # 256 256 256 fat32 \ 162 | # $primary{ } \ 163 | # $iflabel{ gpt } \ 164 | # $reusemethod{ } \ 165 | # method{ efi } format{ } \ 166 | # mountpoint{ /boot/efi } \ 167 | # . \ 168 | # 512 512 512 ext4 \ 169 | # $primary{ } \ 170 | # $bootable{ } \ 171 | # method{ format } format{ } \ 172 | # use_filesystem{ } filesystem{ ext4 } \ 173 | # mountpoint{ /boot } \ 174 | # . \ 175 | # 1024 102400000 1000000000 ext4 \ 176 | # $lvmok{ } \ 177 | # method{ format } format{ } \ 178 | # use_filesystem{ } filesystem{ ext4 } \ 179 | # mountpoint{ / } \ 180 | # lv_name{ root } \ 181 | # . \ 182 | # 200% 200% 200% linux-swap \ 183 | # $lvmok{ } \ 184 | # method{ swap } format{ } \ 185 | # lv_name{ swap } \ 186 | # . 187 | 188 | ### Base system installation 189 | d-i base-installer/install-recommends boolean true 190 | d-i base-installer/kernel/image string linux-generic 191 | 192 | ### Apt setup 193 | d-i apt-setup/restricted boolean true 194 | d-i apt-setup/universe boolean true 195 | d-i apt-setup/backports boolean true 196 | d-i apt-setup/use_mirror boolean false 197 | d-i apt-setup/services-select multiselect security, updates 198 | d-i apt-setup/security_host string security.ubuntu.com 199 | d-i apt-setup/security_path string /ubuntu 200 | 201 | ### Package selection 202 | d-i tasksel/first multiselect none 203 | d-i pkgsel/include string openssh-server python 204 | d-i pkgsel/upgrade select full-upgrade 205 | d-i pkgsel/update-policy select unattended-upgrades 206 | 207 | ### Finishing up the installation 208 | d-i preseed/late_command string \ 209 | cp -r /custom /target/custom; \ 210 | in-target sh -c 'lvremove -f --noudevsync main/placeholder || true'; \ 211 | in-target sh -c 'usermod -p "!" root'; \ 212 | in-target sh -c 'mkdir -p --mode=0700 /root/.ssh && cat /custom/userkey.pub > /root/.ssh/authorized_keys && chmod 0600 /root/.ssh/authorized_keys'; \ 213 | in-target sh -c 'sed -i "s/^#PermitRootLogin.*\$/PermitRootLogin prohibit-password/g" /etc/ssh/sshd_config'; \ 214 | in-target sh -c 'rm -f /etc/ssh/ssh_host_*_key* && mkdir -p /usr/lib/systemd/system && cp /custom/ssh-host-keygen.service /usr/lib/systemd/system/ssh-host-keygen.service && systemctl enable ssh-host-keygen.service'; \ 215 | in-target sh -c 'echo "IPv4: \\\4" >> /etc/issue && echo "IPv6: \\\6" >> /etc/issue && echo "" >> /etc/issue'; \ 216 | in-target sh -c 'eject || true'; \ 217 | rm -r /target/custom; 218 | d-i debian-installer/splash boolean false 219 | d-i cdrom-detect/eject boolean true 220 | 221 | ### Shutdown machine 222 | d-i finish-install/reboot_in_progress note 223 | d-i debian-installer/exit/poweroff boolean true 224 | -------------------------------------------------------------------------------- /ubuntu/20.04/custom/ssh-host-keygen.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=OpenSSH Server Key Generation 3 | Before=ssh.service 4 | 5 | [Service] 6 | ExecStart=/usr/bin/ssh-keygen -A 7 | Type=oneshot 8 | RemainAfterExit=yes 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | --------------------------------------------------------------------------------