diff --git a/.gitignore b/.gitignore index f3b6c46..782d329 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ raspi*.img raspi*.log raspi*.tar.gz +raspi_0w.yaml +raspi_2.yaml +raspi_3.yaml diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3cb8e3e --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +yaml: raspi_0w.yaml raspi_2.yaml raspi_3.yaml +images: raspi_0w.img raspi_2.img raspi_3.img + +raspi_0w.yaml: raspi_master.yaml + cat raspi_master.yaml | sed "s/__ARCH__/armel/" | \ + sed "s/__LINUX_IMAGE__/linux-image-rpi/" | \ + sed "s/__EXTRA_PKGS__/- firmware-brcm80211/" | \ + sed "s/__DTB__/\\/usr\\/lib\\/linux-image-*-rpi\\/bcm*rpi-*.dtb/" |\ + sed "s/__HOST__/rpi0/" > $@ + +raspi_2.yaml: raspi_master.yaml + cat raspi_master.yaml | sed "s/__ARCH__/armhf/" | \ + sed "s/__LINUX_IMAGE__/linux-image-armmp/" | \ + grep -v "__EXTRA_PKGS__" | \ + sed "s/__DTB__/\\/usr\\/lib\\/linux-image-*-armmp\\/bcm*rpi*.dtb/" |\ + sed "s/__HOST__/rpi2/" > $@ + +raspi_3.yaml: raspi_master.yaml + cat raspi_master.yaml | sed "s/__ARCH__/arm64/" | \ + sed "s/__LINUX_IMAGE__/linux-image-arm64/" | \ + sed "s/__EXTRA_PKGS__/- firmware-brcm80211/" | \ + sed "s/__DTB__/\\/usr\\/lib\\/linux-image-*-arm64\\/broadcom\\/bcm*rpi*.dtb/" |\ + sed "s/__HOST__/rpi3/" > $@ + +raspi_0w.img : IMAGE=raspi_0w +raspi_0w.img: raspi_0w.yaml _ck_root _build_img + +raspi_2.img : IMAGE=raspi_2 +raspi_2.img: raspi_2.yaml _ck_root _build_img + +raspi_3.img : IMAGE=raspi_3 +raspi_3.img: raspi_3.yaml _ck_root _build_img + +_build_img: + [ ! -z "$(IMAGE)" ] # This target is not to be called directly + touch $(IMAGE).log + chmod 0644 $(IMAGE).log # Allow for non-root users to follow the build log + time nice vmdb2 --verbose --rootfs-tarball=$(IMAGE).tar.gz --output=$(IMAGE).img $(IMAGE).yaml --log $(IMAGE).log + +_ck_root: + [ `whoami` = 'root' ] # Only root can summon vmdb2 ☹ + +_clean_yaml: + rm -f raspi_0w.yaml raspi_2.yaml raspi_3.yaml +_clean_images: + rm -f raspi_0w.img raspi_2.img raspi_3.img +_clean_logs: + rm -f raspi_0w.log raspi_2.log raspi_3.log +_clean_tarballs: + rm -f raspi_0w.tar.gz raspi_2.tar.gz raspi_3.tar.gz +clean: _clean_images _clean_yaml _clean_tarballs _clean_logs + +.PHONY: _ck_root _build_img clean _clean_images _clean_yaml _clean_tarballs _clean_logs diff --git a/README.md b/README.md index e069fa7..e740943 100644 --- a/README.md +++ b/README.md @@ -22,27 +22,42 @@ cd image-specs For this you will first need to install the `vmdb2` package, on a Debian Buster or higher system. -The recipes for building the images are: +This repository includes a master YAML recipe (which is basically a +configuration file) for all of the generated images, diverting as +little as possible in a parametrized way. The master recipe is +[raspi_master.yaml](raspi_master.yaml). -- [raspi0w.yaml](raspi0w.yaml) for Raspberry Pi 0 and 0w. We believe - (but have not tested) it should also work on the 1 models. -- [raspi2.yaml](raspi2.yaml) for Raspberry Pi 2. -- [raspi3.yaml](raspi3.yaml) for all of the Raspberry Pi 3 models. - -You can edit them to customize the built image. Although it could -(should!) be better documented, -[vmdb2](http://git.liw.fi/vmdb2/tree/README)'s format is very easy to -understand. - -Once you have edited the recipe for your hardware, you can generate -the image by issuing the following (as root): +A Makefile is supplied to drive the build of the recipes into images — +`raspi_0w` (for the Raspberry Pi 0, 0w and 1, models A and B), +`raspi_2` (for the Raspberry Pi 2, models A and B) and `raspi_3` +(models A, B, B+). That is, if you want to build the default image for +a Raspberry Pi 3B+, you can just issue: ```shell - vmdb2 --rootfs-tarball=raspi3.tar.gz --output \ - raspi3.img raspi3.yaml --log raspi3.log + make raspi_3.img ``` -Of course, substituting `raspi3` with the actual flavor you need. +You might also want to edit them to customize the built image. If you +want to start from the platform-specific recipe, you can issue: + +```shell + make raspi_3.yaml +``` +The recipe drives [vmdb2](https://vmdb2.liw.fi/), the successor to +`vmdebootstrap`. Please refer to [its +documentation](https://vmdb2.liw.fi/documentation/) for further +details; it is quite an easy format to understand. + +Copy the generated file to a name descriptive enough for you (say, +`my_raspi.yaml`). Once you have edited the recipe for your specific +needs, you can generate the image by issuing the following (as root): + +```shell + vmdb2 --rootfs-tarball=my_raspi.tar.gz --output \ + my_raspi.img my_raspi.yaml --log my_raspi.log +``` + +This is, just follow what is done by the `_build_img` target of the Makefile. ## Installing the image onto the Raspberry Pi @@ -60,26 +75,6 @@ sudo dd if=raspi3.img of=/dev/mmcblk0 bs=64k oflag=dsync status=progress Then, plug the SD card into the Raspberry Pi, and power it up. The image uses the hostname `rpi0w`, `rpi2` or `rpi3` depending on the -target build, so assuming your local network correctly resolves -hostnames communicated via DHCP, you can log into your Raspberry Pi -once it booted: - -```shell -ssh root@rpi3 -# Enter password “raspberry” -``` - -Note that the default firewall rules only allow SSH access from the local -network. If you wish to enable SSH access globally, first change your root -password using `passwd`. Next, issue the following commands as root to remove -the corresponding firewall rules: - -```shell -iptables -F INPUT -ip6tables -F INPUT -``` - -This will allow SSH connections globally until the next reboot. To make this -persistent, remove the lines containing "REJECT" in `/etc/iptables/rules.v4` and -`/etc/iptables/rules.v6`. - +target build. The provided image will allow you to log in with the +`root` account with no password set, but only logging in at the +physical console (be it serial or by USB keyboard and HDMI monitor). diff --git a/raspi_master.yaml b/raspi_master.yaml new file mode 100644 index 0000000..25595ac --- /dev/null +++ b/raspi_master.yaml @@ -0,0 +1,129 @@ +# See https://wiki.debian.org/RaspberryPi3 for known issues and more details. + +steps: + - mkimg: "{{ output }}" + size: 1500M + + - mklabel: msdos + device: "{{ output }}" + + - mkpart: primary + fs-type: 'fat32' + device: "{{ output }}" + start: 0% + end: 20% + tag: /boot + + - mkpart: primary + device: "{{ output }}" + start: 20% + end: 100% + tag: / + + - kpartx: "{{ output }}" + + - mkfs: vfat + partition: /boot + label: RASPIFIRM + + - mkfs: ext4 + partition: / + label: RASPIROOT + + - mount: / + + - mount: /boot + mount-on: / + dirname: '/boot/firmware' + + - unpack-rootfs: / + + - qemu-debootstrap: buster + mirror: http://deb.debian.org/debian + target: / + arch: __ARCH__ + components: + - main + - contrib + - non-free + unless: rootfs_unpacked + + # TODO(https://bugs.debian.org/877855): remove this workaround once + # debootstrap is fixed + - chroot: / + shell: | + echo 'deb http://deb.debian.org/debian buster main contrib non-free' > /etc/apt/sources.list + echo 'deb http://deb.debian.org/debian-security buster/updates main contrib non-free' >> /etc/apt/sources.list + echo '# Backports are _not_ enabled by default. ' >> /etc/apt/sources.list + echo '# Enable them by uncommenting the following line:' >> /etc/apt/sources.list + echo '# deb http://deb.debian.org/debian buster-backports main contrib non-free' >> /etc/apt/sources.list + apt-get update + unless: rootfs_unpacked + + - apt: install + packages: + - ssh + - parted + - dosfstools + - wireless-tools + - wpasupplicant + - raspi3-firmware + - __LINUX_IMAGE__ + __EXTRA_PKGS__ + tag: / + unless: rootfs_unpacked + + - cache-rootfs: / + unless: rootfs_unpacked + + - shell: | + echo "__HOST__-$(date +%Y%m%d)" > "${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 fstab "${ROOT?}/etc/fstab" + + install -m 644 -o root -g root eth0 "${ROOT?}/etc/network/interfaces.d/eth0" + + install -m 755 -o root -g root rpi-set-sysconf "${ROOT?}/usr/local/sbin/rpi-set-sysconf" + install -m 644 -o root -g root rpi-set-sysconf.service "${ROOT?}/etc/systemd/system" + install -m 644 -o root -g root 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" + + install -m 755 -o root -g root rpi-resizerootfs "${ROOT?}/usr/sbin/rpi-resizerootfs" + install -m 644 -o root -g root rpi-resizerootfs.service "${ROOT?}/etc/systemd/system" + mkdir -p "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/" + ln -s /etc/systemd/system/rpi-resizerootfs.service "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/rpi-resizerootfs.service" + + install -m 644 -o root -g root rpi-generate-ssh-host-keys.service "${ROOT?}/etc/systemd/system" + mkdir -p "${ROOT?}/etc/systemd/system/multi-user.target.requires/" + 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: / + + # Copy the relevant device tree files to the boot partition + - chroot: / + shell: | + install -m 644 -o root -g root __DTB__ /boot/firmware/ + + # Clean up archive cache (likely not useful) and lists (likely outdated) to + # reduce image size by several hundred megabytes. + - chroot: / + 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 + - chroot: / + shell: | + ls -aR /boot + sed -i 's/.dev.mmcblk0p2/LABEL=RASPIROOT/' /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: /