Initial commit

This commit is contained in:
Michael Stapelberg 2017-10-08 22:18:48 +02:00
commit 331f68931a
14 changed files with 429 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# suggested build result filename
raspi3.img
# after compress.sh
compr.img
compr.img.bz2
# log file names used for the published image
01-vmdb2.log
02-compress.log

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "vmdb2"]
path = vmdb2
url = https://github.com/larswirzenius/vmdb2.git

27
LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright © 2017, Michael Stapelberg and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Michael Stapelberg nor the
names of contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Michael Stapelberg ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Michael Stapelberg BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

41
README.md Normal file
View File

@ -0,0 +1,41 @@
# Raspberry Pi 3 image spec
This repository contains the files with which the image referenced at
https://wiki.debian.org/RaspberryPi3 has been built.
## Option 1: Downloading an image
See https://wiki.debian.org/RaspberryPi3#Preview_image for where to obtain the latest pre-built image.
## Option 2: Building your own image
If you prefer, you can build a Debian buster Raspberry Pi 3 image using:
```shell
git clone --recursive https://github.com/Debian/raspi3-image-spec
cd raspi3-image-spec
sudo ./vmdb2/vmdb2 --output raspi3.img raspi3.yaml --log raspi3.log
```
## Installing the image onto the Raspberry Pi 3
Plug an SD card which you would like to entirely overwrite into your SD card reader.
Assuming your SD card reader provides the device `/dev/sdb`, copy the image onto the SD card:
```shell
sudo dd if=raspi3.img of=/dev/sdb bs=5M
```
Then, plug the SD card into the Raspberry Pi 3 and power it up.
The image uses the hostname `rpi3`, so assuming your local network correctly resolves hostnames communicated via DHCP, you can log into your Raspberry Pi 3 once it booted:
```shell
ssh root@rpi3
# Enter password “raspberry”
```
## Reproducibility
The image currently uses http://snapshot.debian.org/archive/debian/20171007T213914Z/ for ensuring a reproducible build.

34
compress.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
# Copies raspi3.img into compr.img, resulting in many consecutive zero bytes
# which are nicely compressible.
set -e
qemu-img create -f raw compr.img 1000M
# copy partition table from raspi3.img
sfdisk --quiet --dump raspi3.img | sfdisk --quiet compr.img
readarray rmappings < <(sudo kpartx -asv raspi3.img)
readarray cmappings < <(sudo kpartx -asv compr.img)
# copy the vfat boot partition as-is
set -- ${rmappings[0]}
rboot="$3"
set -- ${cmappings[0]}
cboot="$3"
sudo dd if=/dev/mapper/${rboot?} of=/dev/mapper/${cboot?} bs=5M status=none
# copy the ext4 root partition in a space-saving way
set -- ${rmappings[1]}
rroot="$3"
set -- ${cmappings[1]}
croot="$3"
sudo e2fsck -y -f /dev/mapper/${rroot?}
sudo resize2fs /dev/mapper/${rroot?} 700M
sudo e2image -rap /dev/mapper/${rroot?} /dev/mapper/${croot?}
sudo kpartx -ds raspi3.img
sudo kpartx -ds compr.img
bzip2 -k -9 -f compr.img

6
eth0 Normal file
View File

@ -0,0 +1,6 @@
auto eth0
# TODO: switch back to iptables-persistent once it re-enters testing
iface eth0 inet dhcp
pre-up iptables-restore < /etc/iptables/rules.v4
pre-up ip6tables-restore < /etc/iptables/rules.v6

5
fstab Normal file
View File

@ -0,0 +1,5 @@
# The root file system has fs_passno=1 as per fstab(5) for automatic fsck.
/dev/mmcblk0p2 / ext4 rw 0 1
# All other file systems have fs_passno=2 as per fstab(5) for automatic fsck.
/dev/mmcblk0p1 /boot/firmware vfat rw 0 2
proc /proc proc defaults 0 0

134
raspi3.yaml Normal file
View File

@ -0,0 +1,134 @@
# 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%
part-tag: boot-part
- mkpart: primary
device: "{{ output }}"
start: 20%
end: 100%
part-tag: root-part
- mkfs: vfat
partition: boot-part
- mkfs: ext4
partition: root-part
- mount: root-part
fs-tag: root-fs
- mount: boot-part
mount-on: root-fs
dirname: '/boot/firmware'
fs-tag: boot-fs
# Without copying the archive keyring into the chroot, debootstraps second
# stage will revert to a known-working HTTPS mirror. As snapshot.d.o does
# not provide HTTPS at this point, we need to avert that.
- shell: |
mkdir -p "${ROOT?}/usr/share/keyrings"
cp /usr/share/keyrings/debian-archive-keyring.gpg "${ROOT?}/usr/share/keyrings/debian-archive-keyring.gpg"
root-fs: root-fs
# We need to use Debian buster (currently testing) instead of Debian stretch
# (currently stable) for:
#
# linux ≥ 4.13.4-1
# Which includes the sdhost driver for faster SD card access and making the
# WiFi chip available.
#
# raspi3-firmware ≥ 1.20171006-1
# Which includes a recent enough firmware version to correctly pass the MAC
# address to the kernel. This is a regression with Linux ≥ 4.12, see
# https://github.com/raspberrypi/firmware/issues/846
- qemu-debootstrap: buster
mirror: http://snapshot.debian.org/archive/debian/20171007T213914Z
target: root-fs
arch: arm64
components:
- main
- contrib
- non-free
# TODO(https://bugs.debian.org/877855): remove this workaround once
# debootstrap is fixed
- chroot: root-fs
shell: |
echo 'deb http://snapshot.debian.org/archive/debian/20171007T213914Z buster main contrib non-free' > /etc/apt/sources.list
apt-get update
- apt: install
packages:
- ssh
- dosfstools
# Contains /lib/firmware/brcm/brcmfmac43430-sdio.bin (required for WiFi).
- firmware-brcm80211
- wireless-tools
- wpasupplicant
fs-tag: root-fs
# TODO: install raspi3-firmware and linux-image-arm64 from buster once they
# migrated in sufficiently recent versions.
- chroot: root-fs
shell: |
echo 'deb http://snapshot.debian.org/archive/debian/20171007T213914Z unstable main contrib non-free' >> /etc/apt/sources.list
echo 'APT::Default-Release "buster";' > /etc/apt/apt.conf.d/08default-release
apt-get update
apt-get -y --no-show-progress -t unstable install raspi3-firmware linux-image-arm64
- shell: |
echo "rpi3" > "${ROOT?}/etc/hostname"
# '..VyaTFxP8kT6' is crypt.crypt('raspberry', '..')
sed -i 's,root:[^:]*,root:..VyaTFxP8kT6,' "${ROOT?}/etc/shadow"
sed -i 's,#PermitRootLogin prohibit-password,PermitRootLogin yes,g' "${ROOT?}/etc/ssh/sshd_config"
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"
mkdir -p "${ROOT?}/etc/iptables"
install -m 644 -o root -g root rules.v4 "${ROOT?}/etc/iptables/rules.v4"
install -m 644 -o root -g root rules.v6 "${ROOT?}/etc/iptables/rules.v6"
install -m 755 -o root -g root rpi3-resizerootfs "${ROOT?}/usr/bin/rpi3-resizerootfs"
install -m 644 -o root -g root rpi3-resizerootfs.service "${ROOT?}/etc/systemd/system"
mkdir -p "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/"
ln -s /etc/systemd/system/rpi3-resizerootfs.service "${ROOT?}/etc/systemd/system/systemd-remount-fs.service.requires/rpi3-resizerootfs.service"
install -m 644 -o root -g root rpi3-generate-ssh-host-keys.service "${ROOT?}/etc/systemd/system"
mkdir -p "${ROOT?}/etc/systemd/system/multi-user.target.requires/"
ln -s /etc/systemd/system/rpi3-generate-ssh-host-keys.service "${ROOT?}/etc/systemd/system/multi-user.target.requires/rpi3-generate-ssh-host-keys.service"
rm -f ${ROOT?}/etc/ssh/ssh_host_*_key*
cat >> "${ROOT?}/etc/motd" <<'EOT'
Please change the root password by running passwd
EOT
root-fs: root-fs
# Clean up archive cache (likely not useful) and lists (likely outdated) to
# reduce image size by several hundred megabytes.
- chroot: root-fs
shell: |
apt-get clean
rm -rf /var/lib/apt/lists
# 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: root-fs

View File

@ -0,0 +1,10 @@
[Unit]
Description=generate SSH host keys
ConditionPathExistsGlob=!/etc/ssh/ssh_host_*_key
[Service]
Type=oneshot
ExecStart=/usr/sbin/dpkg-reconfigure -fnoninteractive openssh-server
[Install]
RequiredBy=multi-user.target

125
rpi3-resizerootfs Executable file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env perl
# vim:ts=4:sw=4:et
#
# © 2017 Michael Stapelberg <stapelberg@debian.org>
#
# Extends the root partition and root file system to cover the entire remainder
# of the device. Requires the root file system to be a block device matching
# /.+p[0-9]$/, e.g. /dev/mmcblk0p2.
#
# Requires only perl-base (present on all Debian installations)
use strict;
use Fcntl qw(:DEFAULT :seek);
################################################################################
# Find the root device by looking at what is mounted on /
################################################################################
sub slurp {
my ($fn) = @_;
open(my $fh, '<', $fn)
or die qq|open($fn): $!|;
local $/;
<$fh>;
}
my ($rootpart) = grep { $_ ne '' } map { /([^ ]+) \/ / && $1 } split("\n", slurp(</proc/mounts>));
my $rootdev;
my $pno;
if ($rootpart =~ /^(.+)p([0-9])$/) {
$rootdev = $1;
$pno = int($2);
} else {
die qq|root partition "$rootpart" unexpectedly does not end in p[0-9]|;
}
################################################################################
# Get the size of the root block device in bytes
################################################################################
sub SYS_ioctl { 29; } # aarch64-specific
sub BLKGETSIZE64 { 2148012658; }
sysopen(my $root, $rootdev, O_RDWR)
or die qq|sysopen($rootdev): $!|;
my $devsizep = pack("Q", ());
$! = 0;
syscall(SYS_ioctl(), fileno($root), BLKGETSIZE64(), $devsizep) >= 0
or die qq|ioctl(BLKGETSIZE64): $!|;
my ($devsize) = unpack("Q", $devsizep);
################################################################################
# Read the partition table entry
################################################################################
sub boot_code { 446; }
sub partition_table_entry { 16; }
sub partition_table_start_offset { 8; }
sub partition_table_length_offset { 12; }
sub read_uint32_le {
my ($offset) = @_;
sysseek($root, $offset, SEEK_SET)
or die qq|sysseek($offset): $!|;
my $buf;
sysread($root, $buf, 4)
or die qq|sysread(): $!|;
my ($val) = unpack("V", $buf);
return $val;
}
my $entry_offset = boot_code() + (partition_table_entry() * ($pno - 1));
my $start = 512 * read_uint32_le($entry_offset + partition_table_start_offset());
my $oldlength = 512 * read_uint32_le($entry_offset + partition_table_length_offset());
my $newlength = ($devsize - $start);
if ($oldlength == $newlength) {
print "Partition $rootpart already at maximum size $newlength\n";
exit 0;
}
################################################################################
# Change the partition length
################################################################################
sub write_uint32_le {
my ($offset, $val) = @_;
sysseek($root, $offset, SEEK_SET)
or die qq|sysseek($offset): $!|;
my $buf = pack("V", $val);
syswrite($root, $buf, 4)
or die qq|syswrite: $!|;
}
print "Resizing partition $rootpart from $oldlength to $newlength bytes\n";
write_uint32_le($entry_offset + partition_table_length_offset(), $newlength / 512);
################################################################################
# Tell linux about the new partition size using the BLKPG ioctl (BLKRRPART
# cannot be used when the device is mounted, even read-only).
################################################################################
sub BLKPG { 0x1269; }
sub BLKPG_RESIZE_PARTITION { 3; }
my $part = pack("q q i Z64 Z64", $start, $newlength, $pno, "devname", "volname");
my $partb = "\x00" x length($part);
my $op = BLKPG_RESIZE_PARTITION();
my $ioctl_arg = pack("i i i x4 p", $op, 0, length($part), $part);
$! = 0;
syscall(SYS_ioctl(), fileno($root), BLKPG(), $ioctl_arg) >= 0
or die "ioctl(BLKPG): $!";
################################################################################
# Run resize2fs to enlarge the file system
################################################################################
# resize2fs requires the file system to be writeable.
my @remountcmd = ('mount', '-o', 'remount,rw', $rootpart);
system(@remountcmd) == 0
or die qq|system(| . join(" ", @remountcmd) . qq|): $!|;
my @resizecmd = ('resize2fs', "${rootpart}");
exec { $resizecmd[0] } @resizecmd
or die qq|exec(| . join(" ", @resizecmd) . qq|): $!|;

11
rpi3-resizerootfs.service Normal file
View File

@ -0,0 +1,11 @@
[Unit]
Description=resize root file system
Before=systemd-remount-fs.service
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usr/bin/rpi3-resizerootfs
[Install]
RequiredBy=systemd-remount-fs.service

13
rules.v4 Normal file
View File

@ -0,0 +1,13 @@
# Generated by iptables-save v1.6.0 on Wed Mar 22 14:31:11 2017
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -s 127.0.0.0/8 -m comment --comment "RFC3330 loopback" -j ACCEPT
-A INPUT -s 10.0.0.0/8 -m comment --comment "RFC1918 reserved" -j ACCEPT
-A INPUT -s 172.16.0.0/12 -m comment --comment "RFC1918 reserved" -j ACCEPT
-A INPUT -s 192.168.0.0/16 -m comment --comment "RFC1918 reserved" -j ACCEPT
-A INPUT -s 169.254.0.0/16 -m comment --comment "RFC3927 link-local" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -m comment --comment SSH -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Wed Mar 22 14:31:11 2017

11
rules.v6 Normal file
View File

@ -0,0 +1,11 @@
# Generated by ip6tables-save v1.6.0 on Wed Mar 22 14:31:11 2017
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -s ::1/128 -m comment --comment "RFC3513 loopback" -j ACCEPT
-A INPUT -s fc00::/7 -m comment --comment "RFC4193 reserved" -j ACCEPT
-A INPUT -s fe80::/10 -m comment --comment "RFC4291 link-local" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -m comment --comment SSH -j REJECT --reject-with icmp6-port-unreachable
COMMIT
# Completed on Wed Mar 22 14:31:11 2017

1
vmdb2 Submodule

@ -0,0 +1 @@
Subproject commit 67709480586340533d23be85987080469f2eebb5