diff --git a/README.md b/README.md index 6c0e9d53..f282b89d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Read more about how [custom images](https://maas.io/docs/how-to-customise-images | CentOS 7 | Stable | >= 2.3 | | CentOS 8 | EOL | >= 2.7 | | CentOS 8 Stream | Beta | >= 3.2 | +| Debian 10 | Beta | >= 3.3 | +| Debian 11 | Beta | >= 3.3 | +| Debian 12 | Beta | >= 3.3 | | OL8 | Beta | >= 3.5 | | OL9 | Beta | >= 3.5 | | RHEL 7 | EOL | >= 2.3 | diff --git a/debian/Makefile b/debian/Makefile new file mode 100644 index 00000000..f0c378dd --- /dev/null +++ b/debian/Makefile @@ -0,0 +1,67 @@ +#!/usr/bin/make -f + +include ../scripts/check.mk + +PACKER ?= packer +PACKER_LOG ?= 0 +export PACKER_LOG + +SERIES ?= bullseye +BOOT ?= uefi +ARCH ?= amd64 + +ifeq ($(strip $(SERIES)),buster) + VERSION = 10 +else ifeq ($(strip $(SERIES)),bullseye) + VERSION = 11 +else ifeq ($(strip $(SERIES)),bookworm) + VERSION = 12 +else + VERSION = 11 +endif + +# Safeguard +ifeq ($(strip $(ARCH)),arm64) + boot = uefi +endif + +.PHONY: all clean + +all: debian + +$(eval $(call check_packages_deps,cloud-image-utils ovmf,cloud-image-utils ovmf)) + +lint: + packer validate . + packer fmt -check -diff . + +format: + packer fmt . + +OVMF_VARS.fd: /usr/share/OVMF/OVMF_VARS.fd + cp -v $< $@ + +debian: check-deps clean + ${PACKER} init . && ${PACKER} build \ + -var debian_series=${SERIES} \ + -var debian_version=${VERSION} \ + -var architecture=${ARCH} \ + -var boot_mode=${BOOT} . + +clean: + ${RM} -rf output-* debian-custom-*.gz \ + seeds-cloudimg.iso \ + OVMF_VARS.fd \ + AAVMF_VARS.fd + +CUSTOM_PKGS:=${wildcard packages/*.deb} + +packages/custom-packages.tar.gz: ${CUSTOM_PKGS} +ifeq ($(strip $(CUSTOM_PKGS)),) + tar czf $@ -C packages -T /dev/null +else + tar czf $@ -C packages ${notdir $^} +endif + +.INTERMEDIATE: OVMF_VARS.fd packages/custom-packages.tar.gz \ + seeds-cloudimg.iso diff --git a/debian/README.md b/debian/README.md new file mode 100644 index 00000000..b28569ee --- /dev/null +++ b/debian/README.md @@ -0,0 +1,147 @@ +# Debian Packer Templates for MAAS + +## Introduction + +The Packer templates in this directory creates Debian images for use with MAAS. + +## Prerequisites (to create the image) + +* A machine running Ubuntu 18.04+ with the ability to run KVM virtual machines. +* qemu-utils, libnbd-bin, nbdkit and fuse2fs +* qemu-system +* ovmf +* cloud-image-utils +* [Packer](https://www.packer.io/intro/getting-started/install.html), v1.7.0 or newer + +## Requirements (to deploy the image) + +* [MAAS](https://maas.io) 3.2+ +* [Curtin](https://launchpad.net/curtin) 21.0+ +* [A Custom Preseed for Debian (Important - See below)] + +## Supported Debian Versions + +The builds and deployment has been tested on MAAS 3.3.5 with Jammy ephemeral images, +in BIOS and UEFI modes. The process currently works with the following Debian series: + +* Debian 10 (Buster) +* Debian 11 (Bullseye) +* Debian 12 (Bookworm) + +## Supported Architectures + +Currently amd64 (x86_64) and arm64 (aarch64) architectures are supported with aemd64 +being the default. + +## Known Issues + +* UEFI images fro Debian 10 (Buster) and 11 (Bullseye) are usable on both BIOS and +UEFI systems. However for Debian 12 (Bookworm) explicit images are required to +support BIOS and UEFI modes. See BOOT make parameter for more details. + + +## debian-cloudimg.pkr.hcl + +This template builds a tgz image from the official Debian cloud images. This +results in an image that is very close to the ones that are on +. + +### Building the image + +The build the image you give the template a script which has all the +customizations: + +```shell +packer init . +packer build -var customize_script=my-changes.sh -var debian_series=bullseye \ + -var debian_version=11 . +``` + +`my-changes.sh` is a script you write which customizes the image from within +the VM. For example, you can install packages using `apt-get`, call out to +ansible, or whatever you want. + +Using make: + +```shell +make debian SERIES=bullseye +``` + +#### Accessing external files from you script + +If you want to put or use some files in the image, you can put those in the `http` directory. + +Whatever file you put there, you can access from within your script like this: + +```shell +wget http://${PACKER_HTTP_IP}:${PACKER_HTTP_PORT}:/my-file +``` + +### Installing a kernel + +If you do want to force an image to always use a specific kernel, you can +include it in the image. + +The easiest way of doing this is to use the `kernel` parameter: + +```shell +packer init . +packer build -var kernel=linux-image-amd64 -var customize_script=my-changes.sh . +``` + +You can also install the kernel manually in your `my-changes.sh` script. + +### Custom Preseed for Debian + +As mentioned above, Debian images require a custom preseed file to be present in the +preseeds directory of MAAS region controllers. + +When used snaps, the path is /var/snap/maas/current/preseeds/curtin_userdata_custom + +Example ready to use preesed files has been included with this repository. Please +see curtin_userdata_custom_amd64 and curtin_userdata_custom_arm64. + +Please be aware that this could potentially create a conflict with the rest of custom +images present in your setup, hence a through investigation and testing might be +required prior to deployment. + +To work around a conflict, it is possible to name the preseed file something similar to +curtin_userdata_custom_amd64_generic_debian-10 assuming the architecture was set to +amd64/generic and the uploaded name was set to custom/debian-10. + +### Makefile Parameters + +#### PACKER_LOG + +Enable (1) or Disable (0) verbose packer logs. The default value is set to 0. + +#### SERIES + +Specify the Debian Series to build. The default value is set to bullseye. + +#### BOOT + +Supported boot mode baked into the image. The default is set to uefi. Please +see the Known Issues section for more details. This parameter is only valid +for amd64 architecture. + +#### ARCH + +Target image architecture. Supported values are amd64 (default) and arm64. + +### Default Username + +The default username is ```debian``` + +## Uploading images to MAAS + +TGZ image + +```shell +maas admin boot-resources create \ + name='custom/debian-12' \ + title='Debian 12 Custom' \ + architecture='amd64/generic' \ + filetype='tgz' \ + content@=debian-custom-cloudimg.tar.gz +``` diff --git a/debian/curtin_userdata_custom_amd64 b/debian/curtin_userdata_custom_amd64 new file mode 100644 index 00000000..bc605986 --- /dev/null +++ b/debian/curtin_userdata_custom_amd64 @@ -0,0 +1,24 @@ +#cloud-config +kernel: + fallback-package: linux-image-amd64 + package: linux-image-amd64 + +apt: + preserve_sources_list: true + +debconf_selections: + maas: | + {{for line in str(curtin_preseed).splitlines()}} + {{line}} + {{endfor}} + +late_commands: + maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] + late_1: mount --bind $TARGET_MOUNT_POINT /mnt + late_2: grep -A2 datasource /etc/cloud/cloud.cfg.d/91_kernel_cmdline_url.cfg | sed 's/curtin//' | tee /mnt/etc/cloud/cloud.cfg.d/debian.cfg + late_3: sed -i 's@ubuntu.com/ubuntu@debian.org/debian@g;s@archive@deb@g;s@ubuntu@debian@g;s@Ubuntu@Debian@g;' /mnt/etc/cloud/cloud.cfg + late_4: debver=$(cat /mnt/etc/debian_version | awk -F. '{print $1}'); if [ ${debver} -eq 10 ]; then rel="buster"; elif [ ${debver} -eq 11 ]; then rel="bullseye"; elif [ ${debver} -eq 12 ]; then rel="bookworm"; fi; sed -i s/stable/${rel}/g /mnt/etc/apt/sources.list; + late_5: sed -i '/^set -e/{n;N;d}' /mnt/etc/kernel/postinst.d/zz-update-grub + late_6: rm -f /usr/local/bin/dpkg-query + late_7: rm -f /usr/local/bin/netplan + diff --git a/debian/curtin_userdata_custom_arm64 b/debian/curtin_userdata_custom_arm64 new file mode 100644 index 00000000..3a475ec6 --- /dev/null +++ b/debian/curtin_userdata_custom_arm64 @@ -0,0 +1,24 @@ +#cloud-config +kernel: + fallback-package: linux-image-arm64 + package: linux-image-arm64 + +apt: + preserve_sources_list: true + +debconf_selections: + maas: | + {{for line in str(curtin_preseed).splitlines()}} + {{line}} + {{endfor}} + +late_commands: + maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] + late_1: mount --bind $TARGET_MOUNT_POINT /mnt + late_2: grep -A2 datasource /etc/cloud/cloud.cfg.d/91_kernel_cmdline_url.cfg | sed 's/curtin//' | tee /mnt/etc/cloud/cloud.cfg.d/debian.cfg + late_3: sed -i 's@ubuntu.com/ubuntu@debian.org/debian@g;s@archive@deb@g;s@ubuntu@debian@g;s@Ubuntu@Debian@g;' /mnt/etc/cloud/cloud.cfg + late_4: debver=$(cat /mnt/etc/debian_version | awk -F. '{print $1}'); if [ ${debver} -eq 10 ]; then rel="buster"; elif [ ${debver} -eq 11 ]; then rel="bullseye"; elif [ ${debver} -eq 12 ]; then rel="bookworm"; fi; sed -i s/stable/${rel}/g /mnt/etc/apt/sources.list; + late_5: sed -i '/^set -e/{n;N;d}' /mnt/etc/kernel/postinst.d/zz-update-grub + late_6: rm -f /usr/local/bin/dpkg-query + late_7: rm -f /usr/local/bin/netplan + diff --git a/debian/debian-cloudimg.pkr.hcl b/debian/debian-cloudimg.pkr.hcl new file mode 100644 index 00000000..61e7ea35 --- /dev/null +++ b/debian/debian-cloudimg.pkr.hcl @@ -0,0 +1,125 @@ +locals { + qemu_arch = { + "amd64" = "x86_64" + "arm64" = "aarch64" + } + uefi_imp = { + "amd64" = "OVMF" + "arm64" = "AAVMF" + } + qemu_machine = { + "amd64" = "accel=kvm" + "arm64" = "virt" + } + qemu_cpu = { + "amd64" = "host" + "arm64" = "cortex-a57" + } + + proxy_env = [ + "http_proxy=${var.http_proxy}", + "https_proxy=${var.https_proxy}", + "no_proxy=${var.https_proxy}", + ] +} + +source "null" "dependencies" { + communicator = "none" +} + +source "qemu" "cloudimg" { + boot_wait = "2s" + cpus = 2 + disk_image = true + disk_size = "4G" + format = "qcow2" + headless = var.headless + http_directory = var.http_directory + iso_checksum = "file:https://cloud.debian.org/images/cloud/${var.debian_series}/latest/SHA512SUMS" + iso_url = "https://cloud.debian.org/images/cloud/${var.debian_series}/latest/debian-${var.debian_version}-generic-${var.architecture}.qcow2" + memory = 2048 + qemu_binary = "qemu-system-${lookup(local.qemu_arch, var.architecture, "")}" + qemu_img_args { + create = ["-F", "qcow2"] + } + qemuargs = [ + ["-machine", "${lookup(local.qemu_machine, var.architecture, "")}"], + ["-cpu", "${lookup(local.qemu_cpu, var.architecture, "")}"], + ["-device", "virtio-gpu-pci"], + ["-drive", "if=pflash,format=raw,id=ovmf_code,readonly=on,file=/usr/share/${lookup(local.uefi_imp, var.architecture, "")}/${lookup(local.uefi_imp, var.architecture, "")}_CODE.fd"], + ["-drive", "if=pflash,format=raw,id=ovmf_vars,file=${lookup(local.uefi_imp, var.architecture, "")}_VARS.fd"], + ["-drive", "file=output-cloudimg/packer-cloudimg,format=qcow2"], + ["-drive", "file=seeds-cloudimg.iso,format=raw"] + ] + shutdown_command = "sudo -S shutdown -P now" + ssh_handshake_attempts = 50 + ssh_password = var.ssh_password + ssh_timeout = "15m" + ssh_username = var.ssh_username + ssh_wait_timeout = "15m" + use_backing_file = true +} + +build { + name = "cloudimg.deps" + sources = ["source.null.dependencies"] + + provisioner "shell-local" { + inline = [ + "cp /usr/share/${lookup(local.uefi_imp, var.architecture, "")}/${lookup(local.uefi_imp, var.architecture, "")}_VARS.fd ${lookup(local.uefi_imp, var.architecture, "")}_VARS.fd", + "cloud-localds seeds-cloudimg.iso user-data-cloudimg meta-data" + ] + inline_shebang = "/bin/bash -e" + } +} + +build { + name = "cloudimg.image" + sources = ["source.qemu.cloudimg"] + + provisioner "shell" { + environment_vars = concat(local.proxy_env, ["DEBIAN_FRONTEND=noninteractive", "DEBIAN_VERSION=${var.debian_version}", "BOOT_MODE=${var.boot_mode}"]) + scripts = ["${path.root}/scripts/essential-packages.sh", "${path.root}/scripts/setup-boot.sh", "${path.root}/scripts/networking.sh"] + } + + provisioner "shell" { + environment_vars = concat(local.proxy_env, ["DEBIAN_FRONTEND=noninteractive"]) + expect_disconnect = true + scripts = [var.customize_script] + } + + provisioner "shell" { + environment_vars = [ + "CLOUDIMG_CUSTOM_KERNEL=${var.kernel}", + "DEBIAN_FRONTEND=noninteractive" + ] + scripts = ["${path.root}/scripts/install-custom-kernel.sh"] + } + + provisioner "file" { + destination = "/tmp/" + sources = ["${path.root}/scripts/curtin-hooks"] + } + + provisioner "shell" { + environment_vars = ["CLOUDIMG_CUSTOM_KERNEL=${var.kernel}"] + scripts = ["${path.root}/scripts/setup-curtin.sh"] + } + + provisioner "shell" { + environment_vars = ["DEBIAN_FRONTEND=noninteractive"] + scripts = ["${path.root}/scripts/cleanup.sh"] + } + + post-processor "shell-local" { + inline = [ + "IMG_FMT=qcow2", + "SOURCE=cloudimg", + "ROOT_PARTITION=1", + "OUTPUT=${var.filename}", + "source ../scripts/fuse-nbd", + "source ../scripts/fuse-tar-root" + ] + inline_shebang = "/bin/bash -e" + } +} diff --git a/debian/debian-cloudimg.variables.pkr.hcl b/debian/debian-cloudimg.variables.pkr.hcl new file mode 100644 index 00000000..4f5a79fd --- /dev/null +++ b/debian/debian-cloudimg.variables.pkr.hcl @@ -0,0 +1,41 @@ +variable "debian_series" { + type = string + default = "bullseye" + description = "The codename of the debian series to build." +} + +variable "debian_version" { + type = string + default = "11" + description = "The version number of the debian series to build." +} + +variable "boot_mode" { + type = string + default = "uefi" + description = "The default boot mode support baked into the image." +} + +variable "filename" { + type = string + default = "debian-custom-cloudimg.tar.gz" + description = "The filename of the tarball to produce" +} + +variable "kernel" { + type = string + default = "" + description = "The package name of the kernel to install. May include version string, e.g linux-image-amd64=5.10.127-2~bpo10+1" +} + +variable "customize_script" { + type = string + default = "/dev/null" + description = "The filename of the script that will run in the VM to customize the image." +} + +variable "architecture" { + type = string + default = "amd64" + description = "The architecture to build the image for (amd64 or arm64)" +} diff --git a/debian/http/.gitkeep b/debian/http/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/debian/meta-data b/debian/meta-data new file mode 100644 index 00000000..27e68d8a --- /dev/null +++ b/debian/meta-data @@ -0,0 +1,2 @@ +instance-id: iid-local01 +local-hostname: packer-debian diff --git a/debian/scripts/cleanup.sh b/debian/scripts/cleanup.sh new file mode 100644 index 00000000..a6f0b5f4 --- /dev/null +++ b/debian/scripts/cleanup.sh @@ -0,0 +1,29 @@ +#!/bin/bash -ex +# +# cleanup.sh - Clean up what we did to be able to build the image. +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +# Everything in /run/packer_backup should be restored. +find /run/packer_backup +cp --preserve -r /run/packer_backup/ / +rm -rf /run/packer_backup + +# We had to allow root to ssh for the image setup. Let's try to revert that. +sed -i s/^root:[^:]*/root:*/ /etc/shadow +rm -r /root/.ssh +rm -r /etc/ssh/ssh_host_* diff --git a/debian/scripts/curtin-hooks b/debian/scripts/curtin-hooks new file mode 100644 index 00000000..e103bb36 --- /dev/null +++ b/debian/scripts/curtin-hooks @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# curtin-hooks - Curtin installation hooks for Ubuntu +# +# Copyright (C) 2022 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os +import shutil + +from curtin.commands.curthooks import builtin_curthooks +from curtin.config import load_command_config +from curtin.util import load_command_environment + + +def configure_custom_kernel(config): + """Amend the curtin config to explicity specify the kernel to install. + + The name of the kernel to install should already have been written to the + CUSTOM_KERNEL file in the same directory as this file. + """ + custom_kernel_path = os.path.join( + os.path.dirname(__file__), "CUSTOM_KERNEL") + with open(custom_kernel_path, "r") as custom_kernel_file: + custom_kernel_package = custom_kernel_file.read().strip() + kernel_config = config.setdefault("kernel", {}) + kernel_config["package"] = custom_kernel_package + return config + + +def cleanup(): + """Remove curtin-hooks so its as if we were never here.""" + curtin_dir = os.path.dirname(__file__) + shutil.rmtree(curtin_dir) + + +def main(): + state = load_command_environment() + config = load_command_config(None, state) + target = state['target'] + + config = configure_custom_kernel(config) + builtin_curthooks(config, target, state) + cleanup() + + +if __name__ == "__main__": + main() diff --git a/debian/scripts/curtin.sh b/debian/scripts/curtin.sh new file mode 100644 index 00000000..c3355ad0 --- /dev/null +++ b/debian/scripts/curtin.sh @@ -0,0 +1,38 @@ +#!/bin/bash -ex +# +# curtin.sh - Move curtin scripts to final destination +# +# Author: Alexsander de Souza +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +export DEBIAN_FRONTEND=noninteractive + +apt-get install -y jq +mkdir -p /curtin + +# install scripts +for s in curtin-hooks install-custom-packages setup-bootloader; do + if [ -f "/tmp/$s" ]; then + mv "/tmp/$s" /curtin/ + chmod 750 "/curtin/$s" + fi +done + +# copy custom packages +if [ -f /tmp/custom-packages.tar.gz ]; then + mv /tmp/custom-packages.tar.gz /curtin/ +fi diff --git a/debian/scripts/essential-packages.sh b/debian/scripts/essential-packages.sh new file mode 100644 index 00000000..4e0ae8bc --- /dev/null +++ b/debian/scripts/essential-packages.sh @@ -0,0 +1,34 @@ +#!/bin/bash -ex +# +# setup-boot.sh - Set up the image after initial boot +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +export DEBIAN_FRONTEND=noninteractive + +# Configure apt proxy if needed. +packer_apt_proxy_config="/etc/apt/apt.conf.d/packer-proxy.conf" +if [ ! -z "${http_proxy}" ]; then + echo "Acquire::http::Proxy \"${http_proxy}\";" >> ${packer_apt_proxy_config} +fi +if [ ! -z "${https_proxy}" ]; then + echo "Acquire::https::Proxy \"${https_proxy}\";" >> ${packer_apt_proxy_config} +fi + +ARCH=$(dpkg --print-architecture) + +apt-get update +apt-get -y install lvm2 xfsprogs diff --git a/debian/scripts/install-custom-kernel.sh b/debian/scripts/install-custom-kernel.sh new file mode 100755 index 00000000..a1926011 --- /dev/null +++ b/debian/scripts/install-custom-kernel.sh @@ -0,0 +1,31 @@ +#!/bin/bash -ex +# +# install-custom-kernel.sh - Install custom kernel, if specified +# +# Copyright (C) 2021 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +export DEBIAN_FRONTEND=noninteractive + +if [ -z "${CLOUDIMG_CUSTOM_KERNEL}" ]; then + echo "Not installing custom kernel, since none was specified." + exit 0 +fi + +echo "Installing custom kernel ${CLOUDIMG_CUSTOM_KERNEL}" +apt-get install -y ${CLOUDIMG_CUSTOM_KERNEL} + +# Record the installed kernel version, so that the curtin hook knows about it. +mkdir -p /curtin +echo -n "${CLOUDIMG_CUSTOM_KERNEL}" > /curtin/CUSTOM_KERNEL diff --git a/debian/scripts/install-custom-packages b/debian/scripts/install-custom-packages new file mode 100644 index 00000000..ade9a941 --- /dev/null +++ b/debian/scripts/install-custom-packages @@ -0,0 +1,46 @@ +#!/bin/bash -ex +# +# install-custom-packages - Install custom packages +# +# Author: Alexsander de Souza +# +# Copyright (C) 2021 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +export DEBIAN_FRONTEND=noninteractive + +PKG_TGZ="/curtin/custom-packages.tar.gz" + +if [ ! -f "${PKG_TGZ}" ]; then + exit 0 +fi + +WORKDIR=$(mktemp -d) + +cleanup() { + rm -rf "${WORKDIR}" +} +trap cleanup EXIT + +echo "remove existing kernels" +dpkg -l 'linux-image-*' 'linux-headers-*' | awk '/^ii/{print $2}' | xargs apt-get -y purge + +echo "install new kernel" +tar xzvf "${PKG_TGZ}" -C "${WORKDIR}" +DEBS=$(find "${WORKDIR}" -name '*.deb') +apt-get install -y --no-install-recommends ${DEBS} +apt-get install --fix-broken + +echo "purge unused packages" +apt-get autoremove -y diff --git a/debian/scripts/networking.sh b/debian/scripts/networking.sh new file mode 100644 index 00000000..3ca4ee0c --- /dev/null +++ b/debian/scripts/networking.sh @@ -0,0 +1,88 @@ +#!/bin/bash -ex +# +# networking.sh - Prepare image to boot with cloud-init +# +# Author: Alexsander de Souza +# Author: Alan Baghumian +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +export DEBIAN_FRONTEND=noninteractive + +# Configure apt proxy if needed. +packer_apt_proxy_config="/etc/apt/apt.conf.d/packer-proxy.conf" +if [ ! -z "${http_proxy}" ]; then + echo "Acquire::http::Proxy \"${http_proxy}\";" >> ${packer_apt_proxy_config} +fi +if [ ! -z "${https_proxy}" ]; then + echo "Acquire::https::Proxy \"${https_proxy}\";" >> ${packer_apt_proxy_config} +fi + +apt-get install -qy cloud-init netplan.io python3-serial + +cat > /etc/sysctl.d/99-cloudimg-ipv6.conf < /usr/local/bin/dpkg-query < /usr/local/bin/netplan < /etc/default/netplan +#systemctl disable networking; systemctl mask networking +#mv /etc/network/{interfaces,interfaces.save} +#systemctl enable systemd-networkd diff --git a/debian/scripts/setup-boot.sh b/debian/scripts/setup-boot.sh new file mode 100644 index 00000000..0fcdcbaa --- /dev/null +++ b/debian/scripts/setup-boot.sh @@ -0,0 +1,52 @@ +#!/bin/bash -ex +# +# setup-boot.sh - Set up the image after initial boot +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +export DEBIAN_FRONTEND=noninteractive + +# Configure apt proxy if needed. +packer_apt_proxy_config="/etc/apt/apt.conf.d/packer-proxy.conf" +if [ ! -z "${http_proxy}" ]; then + echo "Acquire::http::Proxy \"${http_proxy}\";" >> ${packer_apt_proxy_config} +fi +if [ ! -z "${https_proxy}" ]; then + echo "Acquire::https::Proxy \"${https_proxy}\";" >> ${packer_apt_proxy_config} +fi + +ARCH=$(dpkg --print-architecture) + +# Reset cloud-init, so that it can run again when MAAS deploy the image. +cloud-init clean --logs + +apt-get update +if [ ${BOOT_MODE} == "uefi" ]; then + if [ ${ARCH} == "amd64" ]; then + apt-get install -y grub-cloud-${ARCH} grub-efi-${ARCH} + else + apt-get install -y grub-efi-${ARCH}-signed shim-signed grub-efi-${ARCH} + fi +else + apt-get install -y grub-cloud-${ARCH} grub-pc +fi + +# Bookworm does not include this, but curtin requires this during the installation. +if [ ${DEBIAN_VERSION} == '12' ]; then + wget http://ftp.us.debian.org/debian/pool/main/e/efibootmgr/efibootmgr_15-1_${ARCH}.deb + dpkg -i efibootmgr_15-1_${ARCH}.deb + rm efibootmgr_15-1_${ARCH}.deb +fi diff --git a/debian/scripts/setup-bootloader b/debian/scripts/setup-bootloader new file mode 100644 index 00000000..bce05f6b --- /dev/null +++ b/debian/scripts/setup-bootloader @@ -0,0 +1,50 @@ +#!/bin/bash -ex +# +# setup-bootloader - Install bootloader in the boot disk +# +# Author: Alexsander de Souza +# +# Copyright (C) 2023 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +export DEBIAN_FRONTEND=noninteractive + +# Configure apt proxy if needed. +packer_apt_proxy_config="/etc/apt/apt.conf.d/packer-proxy.conf" +if [ ! -z "${http_proxy}" ]; then + echo "Acquire::http::Proxy \"${http_proxy}\";" >> ${packer_apt_proxy_config} +fi +if [ ! -z "${https_proxy}" ]; then + echo "Acquire::https::Proxy \"${https_proxy}\";" >> ${packer_apt_proxy_config} +fi + +ARCH=$(dpkg --print-architecture) + +# Clean up remnants from packer-maas vm install +rm /var/cache/debconf/config.dat +dpkg --configure -a + +apt-get update + +if [ -f /sys/firmware/efi/runtime ]; then + if [ ${ARCH} == "amd64" ]; then + apt-get install -y grub-cloud-${ARCH} grub-efi-${ARCH} + else + apt-get install -y grub-efi-${ARCH}-signed shim-signed grub-efi-${ARCH} + fi +else + apt-get install -y grub-cloud-${ARCH} grub-pc +fi + diff --git a/debian/scripts/setup-curtin.sh b/debian/scripts/setup-curtin.sh new file mode 100755 index 00000000..145d8d35 --- /dev/null +++ b/debian/scripts/setup-curtin.sh @@ -0,0 +1,31 @@ +#!/bin/bash -ex +# +# cloud-img-setup-curtin.sh - Set up curtin curthooks, if needed. +# +# Copyright (C) 2022 Canonical +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +if [[ ! -f "/curtin/CUSTOM_KERNEL" ]]; then + echo "Skipping curtin setup, since no custom kernel is used." + exit 0 +fi + +echo "Configuring curtin to install custom kernel" + +mkdir -p /curtin + +FILENAME=curtin-hooks +mv "/tmp/${FILENAME}" /curtin/ +chmod 750 "/curtin/${FILENAME}" diff --git a/debian/user-data-cloudimg b/debian/user-data-cloudimg new file mode 100644 index 00000000..dc0e2621 --- /dev/null +++ b/debian/user-data-cloudimg @@ -0,0 +1,19 @@ +#cloud-config +users: + - name: root + lock_passwd: false + plain_text_passwd: "debian" + ssh_redirect_user: false +ssh_pwauth: True +disable_root: false +preserve_hostname: true +runcmd: + - sed -i -e '/^[#]*PermitRootLogin/s/^.*$/PermitRootLogin yes/' /etc/ssh/sshd_config + - systemctl restart ssh +bootcmd: + - mkdir /run/packer_backup + - mkdir /run/packer_backup/etc + - mkdir /run/packer_backup/etc/apt + - mkdir /run/packer_backup/etc/ssh + - cp --preserve /etc/apt/sources.list /run/packer_backup/etc/apt/ + - cp --preserve /etc/ssh/sshd_config /run/packer_backup/etc/ssh/ diff --git a/debian/variables.pkr.hcl b/debian/variables.pkr.hcl new file mode 100644 index 00000000..3b3dfc76 --- /dev/null +++ b/debian/variables.pkr.hcl @@ -0,0 +1,50 @@ +packer { + required_version = ">= 1.7.0" + required_plugins { + qemu = { + version = "~> 1.0" + source = "github.com/hashicorp/qemu" + } + } +} + +variable "headless" { + type = bool + default = true + description = "Whether VNC viewer should not be launched." +} + +variable "http_directory" { + type = string + default = "http" +} + +variable "http_proxy" { + type = string + default = "${env("http_proxy")}" +} + +variable "https_proxy" { + type = string + default = "${env("https_proxy")}" +} + +variable "no_proxy" { + type = string + default = "${env("no_proxy")}" +} + +variable "ssh_password" { + type = string + default = "debian" +} + +variable "ssh_username" { + type = string + default = "root" +} + +variable "ssh_debian_password" { + type = string + default = "debian" +}