From 0c0e770fca782e0b12c1275644e36cb8b09e1cc6 Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Thu, 5 Mar 2026 09:24:59 +0000 Subject: [PATCH 1/4] Ensure hard drives are not mounted as storage media, add recipe for kanglam --- ansible/install-usb-viewer.yml | 2 +- ansible/templates/99-usb-butter.rules | 11 ++ vmdb2-recipes/kanglam_pi4.yaml | 197 ++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 ansible/templates/99-usb-butter.rules create mode 100644 vmdb2-recipes/kanglam_pi4.yaml diff --git a/ansible/install-usb-viewer.yml b/ansible/install-usb-viewer.yml index 820dc5f..8f84caa 100644 --- a/ansible/install-usb-viewer.yml +++ b/ansible/install-usb-viewer.yml @@ -36,7 +36,7 @@ - name: Install udev rule copy: - src: "{{ vmdb2_config_base_dir }}/99-usb-butter.rules" + src: "templates/99-usb-butter.rules" dest: "/etc/udev/rules.d/99-usb-butter.rules" - name: Install udev trigger script diff --git a/ansible/templates/99-usb-butter.rules b/ansible/templates/99-usb-butter.rules new file mode 100644 index 0000000..4f378a1 --- /dev/null +++ b/ansible/templates/99-usb-butter.rules @@ -0,0 +1,11 @@ +# Using udev to mount newly attached usb drives doesn't work. +# https://unix.stackexchange.com/a/507150/223286 +# So, we depend on udisks to mount the disk. *Then* we want to +# to setup the symlink and lighttpd config with our script. +# We can run the script immediately because it waits for the disk +# to be mounted. + +# Mount newly inserted drives, creating the directory if it doesn't exist +ACTION=="add", KERNEL=="sd[a-z][1-9]", ENV{ID_BUS}=="usb", TAG+="systemd", ENV{SYSTEMD_WANTS}+="udisks2-mount@%k.service", ENV{SYSTEMD_WANTS}+="serve-usb@%k.service" + +# When the disk is `unmounted, the symlink will just point to a now-empty directory. diff --git a/vmdb2-recipes/kanglam_pi4.yaml b/vmdb2-recipes/kanglam_pi4.yaml new file mode 100644 index 0000000..4cc244e --- /dev/null +++ b/vmdb2-recipes/kanglam_pi4.yaml @@ -0,0 +1,197 @@ +--- +# See https://wiki.debian.org/RaspberryPi3 for known issues and more details. +# image.yml based on revision: ff7fdbf (Switch from qemu-debootstrap to debootstrap., 2024-01-01) + +steps: + - mkimg: "{{ output }}" + size: 3100M + + - mklabel: msdos + device: "{{ output }}" + + - mkpart: primary + fs-type: 'fat32' + device: "{{ output }}" + start: 4MiB + end: 512MiB + tag: tag-firmware + + - mkpart: primary + device: "{{ output }}" + start: 512MiB + end: 100% + tag: tag-root + + - kpartx: "{{ output }}" + + - mkfs: vfat + partition: tag-firmware + label: RASPIFIRM + + - mkfs: ext4 + partition: tag-root + label: RASPIROOT + + - mount: tag-root + + - mount: tag-firmware + mount-on: tag-root + dirname: '/boot/firmware' + + - unpack-rootfs: tag-root + + - debootstrap: trixie + require_empty_target: false + mirror: http://deb.debian.org/debian + target: tag-root + arch: arm64 + components: + - main + - non-free-firmware + - non-free + unless: rootfs_unpacked + + - create-file: /etc/apt/sources.list + contents: |+ + deb http://deb.debian.org/debian trixie main non-free-firmware non-free + deb http://deb.debian.org/debian trixie-updates main non-free-firmware non-free + deb http://security.debian.org/debian-security trixie-security main non-free-firmware non-free + # Backports are _not_ enabled by default. + # Enable them by uncommenting the following line: + # deb http://deb.debian.org/debian trixie-backports main non-free-firmware + + unless: rootfs_unpacked + + - copy-file: /etc/initramfs-tools/hooks/rpi-resizerootfs + src: image-specs/rootfs/etc/initramfs-tools/hooks/rpi-resizerootfs + perm: 0755 + unless: rootfs_unpacked + + - copy-file: /etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs + src: image-specs/rootfs/etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs + perm: 0755 + unless: rootfs_unpacked + + - apt: install + packages: + - avahi-daemon + - curl + - udisks2 + - wget + - dhcpcd + - dnsmasq + - python3 + - lighttpd + - unzip + - sudo + - systemd-timesyncd + - ca-certificates + - dosfstools + - iw + - parted + - ssh + - wpasupplicant + - systemd-timesyncd + - linux-image-arm64 + - raspi-firmware + - firmware-brcm80211 + - bluez-firmware + tag: tag-root + unless: rootfs_unpacked + + - cache-rootfs: tag-root + unless: rootfs_unpacked + + - shell: | + echo "kanglam" > "${ROOT?}/etc/hostname" + sed -i "s,root:[^:]*:,root::," "${ROOT?}/etc/shadow" + + + install -m 644 -o root -g root image-specs/rootfs/etc/fstab "${ROOT?}/etc/fstab" + + install -m 644 -o root -g root image-specs/rootfs/etc/network/interfaces.d/eth0 "${ROOT?}/etc/network/interfaces.d/eth0" + install -m 600 -o root -g root image-specs/rootfs/etc/network/interfaces.d/wlan0 "${ROOT?}/etc/network/interfaces.d/wlan0" + + install -m 755 -o root -g root image-specs/rootfs/usr/local/sbin/rpi-set-sysconf "${ROOT?}/usr/local/sbin/rpi-set-sysconf" + install -m 644 -o root -g root image-specs/rootfs/etc/systemd/system/rpi-set-sysconf.service "${ROOT?}/etc/systemd/system/" + install -m 644 -o root -g root image-specs/rootfs/boot/firmware/sysconf.txt "${ROOT?}/boot/firmware/sysconf.txt" + mkdir -p "${ROOT?}/etc/systemd/system/basic.target.requires/" + ln -s /etc/systemd/system/rpi-set-sysconf.service "${ROOT?}/etc/systemd/system/basic.target.requires/rpi-set-sysconf.service" + + # Resize script is now in the initrd for first boot; no need to ship it. + rm -f "${ROOT?}/etc/initramfs-tools/hooks/rpi-resizerootfs" + rm -f "${ROOT?}/etc/initramfs-tools/scripts/local-bottom/rpi-resizerootfs" + + install -m 644 -o root -g root image-specs/rootfs/etc/systemd/system/rpi-reconfigure-raspi-firmware.service "${ROOT?}/etc/systemd/system/" + mkdir -p "${ROOT?}/etc/systemd/system/multi-user.target.requires/" + ln -s /etc/systemd/system/rpi-reconfigure-raspi-firmware.service "${ROOT?}/etc/systemd/system/multi-user.target.requires/rpi-reconfigure-raspi-firmware.service" + + install -m 644 -o root -g root image-specs/rootfs/etc/systemd/system/rpi-generate-ssh-host-keys.service "${ROOT?}/etc/systemd/system/" + ln -s /etc/systemd/system/rpi-generate-ssh-host-keys.service "${ROOT?}/etc/systemd/system/multi-user.target.requires/rpi-generate-ssh-host-keys.service" + rm -f "${ROOT?}"/etc/ssh/ssh_host_*_key* + + root-fs: tag-root + + # Copy the relevant device tree files to the boot partition + - chroot: tag-root + shell: | + install -m 644 -o root -g root /usr/lib/linux-image-*-arm64/broadcom/bcm*rpi*.dtb /boot/firmware/ + + # Clean up archive cache (likely not useful) and lists (likely outdated) to + # reduce image size by several hundred megabytes. + - chroot: tag-root + shell: | + apt-get clean + rm -rf /var/lib/apt/lists + + # Modify the kernel commandline we take from the firmware to boot from + # the partition labeled raspiroot instead of forcing it to mmcblk0p2. + # Also insert the serial console right before the root= parameter. + # + # These changes will be overwritten after the hardware is probed + # after dpkg reconfigures raspi-firmware (upon first boot), so make + # sure we don't lose label-based booting. + - chroot: tag-root + shell: | + sed -i 's/root=/console=ttyS1,115200 root=/' /boot/firmware/cmdline.txt + sed -i 's#root=/dev/mmcblk0p2#root=LABEL=RASPIROOT#' /boot/firmware/cmdline.txt + sed -i 's/^#ROOTPART=.*/ROOTPART=LABEL=RASPIROOT/' /etc/default/raspi*-firmware + + sed -i 's/cma=64M //' /boot/firmware/cmdline.txt + + # TODO(https://github.com/larswirzenius/vmdb2/issues/24): remove once vmdb + # clears /etc/resolv.conf on its own. + - shell: | + rm "${ROOT?}/etc/resolv.conf" + root-fs: tag-root + + # Clear /etc/machine-id and /var/lib/dbus/machine-id, as both should + # be auto-generated upon first boot. From the manpage + # (machine-id(5)): + # + # For normal operating system installations, where a custom image is + # created for a specific machine, /etc/machine-id should be + # populated during installation. + # + # Note this will also trigger ConditionFirstBoot=yes for systemd. + # On Buster, /etc/machine-id should be an emtpy file, not an absent file + # On Bullseye, /etc/machine-id should not exist in an image + - chroot: tag-root + shell: | + rm -f /etc/machine-id /var/lib/dbus/machine-id + echo "uninitialized" > /etc/machine-id + + # Create /etc/raspi-image-id to know, from what commit the image was built + - chroot: tag-root + shell: | + echo "image based on revision: ff7fdbf (Switch from qemu-debootstrap to debootstrap., 2024-01-01) and build on 2025-10-27 20:22 (UTC)" > "/etc/raspi-image-id" + + - virtual-filesystems: tag-root + + - ansible: tag-root + playbook: ../ansible/main.yml + config_file: ../ansible/ansible.cfg + extra_vars: + butter_language: bo + butter_name: kanglam + From 398685d9c2ddd8df42dd0146711a0fbf3f63dbe6 Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Thu, 5 Mar 2026 09:28:23 +0000 Subject: [PATCH 2/4] Add some scripts for building and compressing images --- vmdb2-recipes/run_build_amd64.sh | 10 ++++++++++ vmdb2-recipes/run_build_amd64_kanglam.sh | 10 ++++++++++ vmdb2-recipes/run_build_raspi4.sh | 10 ++++++++++ vmdb2-recipes/run_build_raspi4_kanglam.sh | 10 ++++++++++ 4 files changed, 40 insertions(+) create mode 100755 vmdb2-recipes/run_build_amd64.sh create mode 100755 vmdb2-recipes/run_build_amd64_kanglam.sh create mode 100755 vmdb2-recipes/run_build_raspi4.sh create mode 100755 vmdb2-recipes/run_build_raspi4_kanglam.sh diff --git a/vmdb2-recipes/run_build_amd64.sh b/vmdb2-recipes/run_build_amd64.sh new file mode 100755 index 0000000..7b4d103 --- /dev/null +++ b/vmdb2-recipes/run_build_amd64.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +GIT_BRANCH=$(git branch --show-current 2>/dev/null) +GIT_TAG=$(git tag 2>/dev/null | head -n1) +BUILD_DATE=$(date +"%d%m%y") +SUFFIX="${GIT_BRANCH}_${GIT_TAG}_${BUILD_DATE}" +time vmdb2 --rootfs-tarball=64_$SUFFIX.tar.gz --output 64_butter_$SUFFIX.img --log 64_butter_$SUFFIX.log amd64_trixie.yaml +tar cvfz 64_butter_$SUFFIX.img.tar.gz 64_butter_$SUFFIX.img +#curl -H "Authorization: token" $CHURN_SECRET -X PUT --upload-file raspi4_butter_$SUFFIX.img.tar.gz https://guardianproject.dev/api/packages/butter/generic/churn/latest/raspi4_butter_$SUFFIX.img.tar.gz +#rm *img *tar.gz diff --git a/vmdb2-recipes/run_build_amd64_kanglam.sh b/vmdb2-recipes/run_build_amd64_kanglam.sh new file mode 100755 index 0000000..14c468f --- /dev/null +++ b/vmdb2-recipes/run_build_amd64_kanglam.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +GIT_BRANCH=$(git branch --show-current 2>/dev/null) +GIT_TAG=$(git tag 2>/dev/null | head -n1) +BUILD_DATE=$(date +"%d%m%y") +SUFFIX="${GIT_BRANCH}_${GIT_TAG}_${BUILD_DATE}" +time vmdb2 --rootfs-tarball=64_$SUFFIX.tar.gz --output 64_butter_$SUFFIX.img --log 64_butter_$SUFFIX.log amd64_trixie_kanglam.yaml +tar cvfz 64_butter_$SUFFIX.img.tar.gz 64_butter_$SUFFIX.img +#curl -H "Authorization: token" $CHURN_SECRET -X PUT --upload-file raspi4_butter_$SUFFIX.img.tar.gz https://guardianproject.dev/api/packages/butter/generic/churn/latest/raspi4_butter_$SUFFIX.img.tar.gz +#rm *img *tar.gz diff --git a/vmdb2-recipes/run_build_raspi4.sh b/vmdb2-recipes/run_build_raspi4.sh new file mode 100755 index 0000000..e0a9a07 --- /dev/null +++ b/vmdb2-recipes/run_build_raspi4.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +GIT_BRANCH=$(git branch --show-current 2>/dev/null) +GIT_TAG=$(git tag 2>/dev/null | head -n1) +BUILD_DATE=$(date +"%d%m%y") +SUFFIX="${GIT_BRANCH}_${GIT_TAG}_${BUILD_DATE}" +time vmdb2 --rootfs-tarball=raspi4_$SUFFIX.tar.gz --output raspi4_butter_NOAP_$SUFFIX.img --log raspi4_butter_$SUFFIX.log raspi_4_trixie.yaml +tar cvfz raspi4_butter_NOAP_$SUFFIX.img.tar.gz raspi4_butter_NOAP_$SUFFIX.img +#curl -H "Authorization: token" $CHURN_SECRET -X PUT --upload-file raspi4_butter_$SUFFIX.img.tar.gz https://guardianproject.dev/api/packages/butter/generic/churn/latest/raspi4_butter_$SUFFIX.img.tar.gz +#rm *img *tar.gz diff --git a/vmdb2-recipes/run_build_raspi4_kanglam.sh b/vmdb2-recipes/run_build_raspi4_kanglam.sh new file mode 100755 index 0000000..f27ff92 --- /dev/null +++ b/vmdb2-recipes/run_build_raspi4_kanglam.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +GIT_BRANCH=$(git branch --show-current 2>/dev/null) +GIT_TAG=$(git tag 2>/dev/null | head -n1) +BUILD_DATE=$(date +"%d%m%y") +SUFFIX="${GIT_BRANCH}_${GIT_TAG}_${BUILD_DATE}" +time vmdb2 --rootfs-tarball=raspi4_$SUFFIX.tar.gz --output raspi4_butter_$SUFFIX.img --log raspi4_butter_$SUFFIX.log kanglam_pi4.yaml +tar cvfz raspi4_butter_$SUFFIX.img.tar.gz raspi4_butter_$SUFFIX.img +#curl -H "Authorization: token" $CHURN_SECRET -X PUT --upload-file raspi4_butter_$SUFFIX.img.tar.gz https://guardianproject.dev/api/packages/butter/generic/churn/latest/raspi4_butter_$SUFFIX.img.tar.gz +#rm *img *tar.gz From dea506b562eca6afe570b3e37a914e2471d281ce Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Thu, 5 Mar 2026 09:31:14 +0000 Subject: [PATCH 3/4] Add amd64 recipe for kanglam --- vmdb2-recipes/amd64_trixie_kanglam.yaml | 178 ++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 vmdb2-recipes/amd64_trixie_kanglam.yaml diff --git a/vmdb2-recipes/amd64_trixie_kanglam.yaml b/vmdb2-recipes/amd64_trixie_kanglam.yaml new file mode 100644 index 0000000..f13c328 --- /dev/null +++ b/vmdb2-recipes/amd64_trixie_kanglam.yaml @@ -0,0 +1,178 @@ +--- +# See https://wiki.debian.org/RaspberryPi3 for known issues and more details. +# image.yml based on revision: ff7fdbf (Switch from qemu-debootstrap to debootstrap., 2024-01-01) + +steps: + - mkimg: "{{ output }}" + size: 3100M + + - mklabel: gpt + device: "{{ output }}" + +############ efi + - mkpart: primary + fs-type: 'fat32' + device: "{{ output }}" + start: 1MiB + end: 132MiB + tag: efi + + - set_part_flag: "{{ output }}" + tag: efi + flag: boot + state: enabled + + - set_part_flag: "{{ output }}" + tag: efi + flag: esp + state: enabled + +############ bios grub + - mkpart: primary + device: "{{ output }}" + start: 132MiB + end: 133MiB + tag: bios_grub + + - set_part_flag: "{{ output }}" + tag: bios_grub + flag: bios_grub + state: enabled +############ live + - mkpart: primary + device: "{{ output }}" + start: 133MiB + end: 100% + tag: tag-root + + - set_part_flag: "{{ output }}" + tag: tag-root + flag: legacy_boot + state: enabled + + - kpartx: "{{ output }}" + + - mkfs: vfat + partition: efi + label: EFI + options: -F32 + + - mkfs: ext4 + partition: tag-root + label: boot + + - mount: tag-root + + - shell: | + dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/mbr/gptmbr.bin of="{{ output }}" + root-fs: tag-root + + - unpack-rootfs: tag-root + + - debootstrap: trixie + require_empty_target: false + mirror: http://deb.debian.org/debian + target: tag-root + components: + - main + - non-free-firmware + - non-free + unless: rootfs_unpacked + + - create-file: /etc/apt/sources.list + contents: |+ + deb http://deb.debian.org/debian trixie main non-free-firmware non-free + deb http://deb.debian.org/debian trixie-updates main non-free-firmware non-free + deb http://security.debian.org/debian-security trixie-security main non-free-firmware non-free + + unless: rootfs_unpacked + + - apt: install + packages: + - avahi-daemon + - curl + - udisks2 + - wget + - dhcpcd + - python3 + - lighttpd + - unzip + - sudo + - systemd-timesyncd + - ca-certificates + - dosfstools + - iw + - parted + - ssh + - wpasupplicant + - systemd + - systemd-sysv + - init-system-helpers + - syslinux + - linux-image-amd64 + tag: tag-root + unless: rootfs_unpacked + + - cache-rootfs: tag-root + unless: rootfs_unpacked + + - shell: | + echo "kanglam" > "${ROOT?}/etc/hostname" + + # Allow root logins locally with no password + sed -i 's,root:[^:]*:,root::,' "${ROOT?}/etc/shadow" + + install -m 644 -o root -g root image-specs/rootfs/etc/fstab "${ROOT?}/etc/fstab" + + install -m 644 -o root -g root image-specs/rootfs/etc/network/interfaces.d/eth0 "${ROOT?}/etc/network/interfaces.d/eth0" + install -m 600 -o root -g root image-specs/rootfs/etc/network/interfaces.d/wlan0 "${ROOT?}/etc/network/interfaces.d/wlan0" + root-fs: tag-root + + # Clean up archive cache (likely not useful) and lists (likely outdated) to + # reduce image size by several hundred megabytes. + - chroot: tag-root + shell: | + apt-get clean + rm -rf /var/lib/apt/lists + + - grub: bios + tag: tag-root + console: serial +# + - grub: uefi + tag: tag-root + efi: efi + console: serial + + - shell: | + rm "${ROOT?}/etc/resolv.conf" + root-fs: tag-root + + # Clear /etc/machine-id and /var/lib/dbus/machine-id, as both should + # be auto-generated upon first boot. From the manpage + # (machine-id(5)): + # + # For normal operating system installations, where a custom image is + # created for a specific machine, /etc/machine-id should be + # populated during installation. + # + # Note this will also trigger ConditionFirstBoot=yes for systemd. + # On Buster, /etc/machine-id should be an emtpy file, not an absent file + # On Bullseye, /etc/machine-id should not exist in an image + - chroot: tag-root + shell: | + rm -f /etc/machine-id /var/lib/dbus/machine-id + echo "uninitialized" > /etc/machine-id + echo "LABEL=BOOT / ext4 rw 0 1" > /etc/fstab + + - virtual-filesystems: tag-root + + - ansible: tag-root + playbook: ../ansible/main.yml + config_file: ../ansible/ansible.cfg + extra_vars: + butter_language: en + butter_name: kanglam + tags: base,usb,matrix,keanu,website + butter_user: "amd" + ap_mode_supported: "false" From c4ffbb00e22211a2e34e7800a6cc660b05b1e0fc Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Thu, 5 Mar 2026 09:33:02 +0000 Subject: [PATCH 4/4] Add delta chat ansible playbook --- ansible/delta-chat.yml | 31 +++++++++++++++++++++++++++++++ ansible/main.yml | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 ansible/delta-chat.yml diff --git a/ansible/delta-chat.yml b/ansible/delta-chat.yml new file mode 100644 index 0000000..5f17f0b --- /dev/null +++ b/ansible/delta-chat.yml @@ -0,0 +1,31 @@ +--- +- name: Install madmail/deltachat + hosts: all + become: true + tasks: + - name: Create madmail directory + file: + path: "/home/{{ butter_user }}/madmail" + state: directory + owner: "{{ butter_user }}" + group: "{{ butter_user }}" + mode: "0755" + + - name: Download pre-built madmail archive + get_url: + url: "https://github.com/themadorg/madmail/releases/download/v0.12.7/madmail-linux-{{ go_arch_map[ansible_architecture] }}.tar.gz" + dest: "/tmp/madmail-linux-{{ go_arch_map[ansible_architecture] }}.tar.gz" + mode: '0644' + + - name: Untar madmail + unarchive: + src: "/tmp/madmail-linux-{{ go_arch_map[ansible_architecture] }}.tar.gz" + dest: "/home/{{ butter_user }}/madmail" + remote_src: yes + #extra_opts: [--strip-components=1] + + - name: Ensure butter_user owns madmail directory + file: + path: "/home/{{ butter_user }}/madmail" + state: directory + recurse: yes diff --git a/ansible/main.yml b/ansible/main.yml index 770412c..7687838 100644 --- a/ansible/main.yml +++ b/ansible/main.yml @@ -4,6 +4,8 @@ - "base" - "ap" - "matrix" +- import_playbook: delta-chat.yml + tags: "delta-chat" - import_playbook: install-rasp-ap.yml tags: "ap" when: ap_mode_supported | bool