aboutsummaryrefslogtreecommitdiff
path: root/mkrootfs
blob: 09047cb45d813bccc8a57890b13f5d900caa199b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#!/bin/bash
set -e

hostname=pi
ssh_key="$HOME/.ssh/id_rsa.pub"
release=stretch
rootfs=

print_usage() {
    cat <<EOF
Usage: $0 [options] <rootfs_directory>

Create a bare debian root filesystem for the Raspberry Pi 2 B. The
password for the root account will be set to 'guest' by default.

Options:
  --hostname <hostname>  Use a given hostname for the device. Defaults to '$hostname'.
  --ssh-key <key file>	 Add the given SSH key to root's authorized_keys file. Defaults to '$ssh_key'.
  --release <release>    Create a root filesystem for the given debian release. Defaults to '$release'.
  --no-debootstrap       Don't create an initial file system with debootstrap. This may be useful for debugging purposes
EOF
}

fail() {
    echo "$1" >&2
    exit 1
}

log() {
    echo "mkrootfs: $1" >&2
}

# Process arguments
while [ $# -gt 0 ]; do
    case "$1" in
	--help|-h)
	    print_usage
	    exit 0
	    ;;	
	--hostname)
	    shift
	    [[ -z "$1" ]] && fail "no hostname specified"
	    hostname="$1"
	    ;;
	--release)
	    shift
	    [[ -z "$1" ]] && fail "no release specified"
	    release="$1"
	    ;;
	--ssh-key)
	    shift
	    [[ -z "$1" ]] && fail "no ssh-key specified"
	    ssh_key="$1"
	    ;;
	--no-debootstrap)
	    nodeboostrap=true
	    ;;
	*)
	    [[ -n "$rootfs" ]] && fail "rootfs already defined"
	    rootfs="$1"
	    ;;
    esac
    shift
done

[ -n "$rootfs" ] || fail "$(print_usage)"
[ ! -e "$rootfs" ] || fail "$rootfs already exists"
[ "$EUID" -eq 0 ] || fail "$0 must be run as root"

if [ -n "$nodebootstrap" ]; then
    echo "skipping deboostrap" >&2
else
    log "debootstrapping"
    if  [ $(dpkg -l | grep -c apt-cacher-ng) -gt 0 ]; then
	export http_proxy="http://localhost:3142"
    else
	echo "WARNING: running debootstrap will download a lot of data. It is recommended to use apt-cacher-ng." >&2
    fi

    qemu-debootstrap \
	--verbose \
	--include=wget,curl,openssl,ca-certificates \
	--arch=armhf \
	"$release" \
	"$rootfs" \
	http://httpredir.debian.org/debian

    log "debootstrap complete"
    # http_proxy conflicts with direct calls to apt-get
    #unset http_proxy
fi

# Run a command in the root file system
chroot_exec() {
    LANG=C LC_ALL=C DEBIAN_FRONTEND=noninteractive chroot "$rootfs" "$@"
}

# Unmount and remove any temporary files
cleanup() {
    log "cleaning"
    set +e
    fuser --kill -SIGTERM "$rootfs"
    sleep 2
    fuser --kill -SIGKILL --verbose "$rootfs"
    umount -l "$rootfs/proc" 2> /dev/null
    umount -l "$rootfs/sys" 2> /dev/null
    umount -l "$rootfs/dev/pts" 2> /dev/null
    umount -l "$rootfs/dev" 2> /dev/null
    trap - 0 1 2 3 6
}
trap cleanup 0 1 2 3 6

# Prepare filesystem to be chrooted into
log "creating temporary system mount points"
mount -t proc chproc "$rootfs/proc"
mount -t sysfs chsys "$rootfs/sys"
mount -t devtmpfs chdev "$rootfs/dev"
mount -t devpts chpts "$rootfs/dev/pts"

# Set up package archive
log "configuring apt archive"
cat > "$rootfs/etc/apt/sources.list" <<EOF
deb http://httpredir.debian.org/debian $release main contrib non-free
deb http://httpredir.debian.org/debian $release-updates main contrib non-free
deb http://security.debian.org $release/updates main contrib non-free
EOF
chroot_exec apt-get update

# Configure fstab
log "configuring fstab"
cat > "$rootfs/etc/fstab" <<EOF
/dev/mmcblk0p1 /boot/firmware vfat  noatime  0 2
/dev/mmcblk0p2 /              ext4  noatime  0 1
tmpfs          /tmp           tmpfs defaults 0 0
EOF

# Install proprietary 2nd-stage bootloader firmware
log "installing proprietary 2nd-stage bootloader"
mkdir -p "$rootfs/boot/firmware"
wget https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin \
     -O "$rootfs/boot/firmware/bootcode.bin"
wget https://github.com/raspberrypi/firmware/raw/master/boot/start.elf \
     -O "$rootfs/boot/firmware/start.elf"

# Install u-boot 3rd-stage bootloader
# https://blog.night-shade.org.uk/2015/05/booting-a-raspberry-pi2-with-u-boot-and-hyp-enabled/
log "installing u-boot 3rd stage bootloader"
chroot_exec apt-get install -y u-boot-rpi
cp "$rootfs/usr/lib/u-boot/rpi_2/u-boot.bin" "$rootfs/boot/firmware/u-boot.bin"
cat <<EOF > "$rootfs/boot/firmware/config.txt"
gpu_mem=64
kernel=u-boot.bin
EOF

# Install and configure kernel flashing utility
# http://sjoerd.luon.net/posts/2015/02/debian-jessie-on-rpi2/
chroot_exec apt-get install -y flash-kernel u-boot-tools
echo "Raspberry Pi 2 Model B" > "$rootfs/etc/flash-kernel/machine"
cat <<EOF > "$rootfs/etc/flash-kernel/ubootenv.d/10-partition"
setenv distro_bootpart 2
setenv prefix /boot/
EOF
echo "Machine: Raspberry Pi 2 Model B" >> "$rootfs/etc/flash-kernel/db"
echo "Boot-Script-Path: /boot/firmware/boot.scr" >> "$rootfs/etc/flash-kernel/db"

# Install kernel
log "installing kernel"
chroot_exec apt-get install -y linux-image-armmp-lpae

# Configure kernel boot args
log "setting kernel boot arguments"
cat <<EOF > "$rootfs/etc/default/flash-kernel"
LINUX_KERNEL_CMDLINE="quiet"
LINUX_KERNEL_CMDLINE_DEFAULTS="console=ttyAMA0 root=/dev/mmcblk0p2"
EOF
chroot_exec flash-kernel

# Set up hostname configuration
log "configuring network host names"
echo "$hostname" > "$rootfs/etc/hostname"
sed -i "/^127.0.0.1/ s/\$/ $hostname/" "$rootfs/etc/hosts"
sed -i "/^::1/ s/\$/ $hostname/" "$rootfs/etc/hosts"

# Set up network interfaces
log "configuring network interfaces"
cat > "$rootfs/etc/network/interfaces.d/lo" <<EOF
auto lo
iface lo inet loopback
EOF
cat > "$rootfs/etc/network/interfaces.d/eth0" <<EOF
allow-hotplug eth0
iface eth0 inet dhcp
EOF

# Set up root account
log "creating root account"
chroot_exec sh -c "echo root:guest | chpasswd"

# Install OpenSSH
log "setting up openssh-server"
chroot_exec apt-get -y install openssh-server
if [ -f "$ssh_key" ]; then
    mkdir -p "$rootfs/root/.ssh"
    chmod 600 "$rootfs/root/.ssh"
    cat "$ssh_key" > "$rootfs/root/.ssh/authorized_keys"
    chmod 600 "$rootfs/root/.ssh/authorized_keys"
    sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$rootfs/etc/ssh/sshd_config"
    echo "SSH password login disabled. Use the key in $ssh_key to login." 1>&2
else
    sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' "$rootfs/etc/ssh/sshd_config"
    echo "Warning: SSH password login is enabled! This can be a security risk." 1>&2
fi

# Prepare first boot
log "setting up first boot script"
chroot_exec apt-get -y install parted
cat <<'EOM' > "$rootfs/etc/rc.firstboot"
#!/bin/bash
set -e
logger -t "rc.firstboot" "Starting first boot"

led_blink() {
    local led=/sys/class/leds/"$1"

    local time_on="$2"
    local time_off="$time_on"
    local blinks="$3"

    local trigger=$(cat "$led/trigger" | sed 's/.*\[\(.*\)\].*/\1/g' -)
    local brightness=$(cat "$led/brightness")

    echo "none" > "$led/trigger"
    for i in $(seq 1 $blinks); do
        echo 255 > "$led/brightness"
        sleep "$time_on"
	echo 0 > "$led/brightness"
        sleep "$time_off"
    done
    echo "$brightness" > "$led/brightness"
    echo "$trigger" > "$led/trigger"
}

led_trigger() {
    local led=/sys/class/leds/"$1"

    if [ "$2" == "heartbeat" ]; then
        modprobe ledtrig_heartbeat || true
    fi
    echo "$2" > "$led/trigger"
}

led_blink ACT 0.1 20 &
led_blink PWR 0.1 20
led_trigger PWR heartbeat
led_trigger ACT mmc0

logger -t "rc.firstboot" "Generating ssh host keys"
rm -f /etc/ssh/ssh_host_*
systemctl stop sshd
ssh-keygen -q -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -q -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key
ssh-keygen -q -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key
ssh-keygen -q -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key
systemctl start sshd

logger -t "rc.firstboot" "Expanding root file system"
fdisk /dev/mmcblk0 <<EOF || true
p
d
2
n
p
2


n
w
EOF
partprobe
resize2fs /dev/mmcblk0p2

logger -t "rc.firstboot" "Generating D-Bus machine-id"
rm -f /var/lib/dbus/machine-id 
dbus-uuidgen --ensure

logger -t "rc.firstboot" "Regenerating initramfs"
update-initramfs -u

logger -t "rc.firstboot" "Restarting core services"
systemctl daemon-reload
systemctl restart networking.service
systemctl restart systemd-networkd.service

logger -t "rc.firstboot" "First boot actions finished"
rm -f /etc/rc.firstboot
sed -i '/.*rc.firstboot/d' /etc/rc.local

led_trigger ACT heartbeat
led_trigger PWR none
EOM
chmod +x "$rootfs/etc/rc.firstboot"

cat <<EOF > "$rootfs/etc/rc.local"
#!/bin/sh -e
/etc/rc.firstboot
exit 0
EOF
chmod +x "$rootfs/etc/rc.local"

cleanup

log "done"