Some tech documentation and snippets, finally organized.

Mount a partition of a qcow2 (or raw) disk image

There are situations when you need to access a VM's disk / partition directly, e.g to reset a forgotten root password. For QEMU's qcow2 partitions this can be achieved with the "network block device" (nbd) driver and qemu-nbd from the qemu-utils package. (For "raw" disk images, see below.)

First, load the nbd kernel module:

$ modprobe nbd max_part=12

Now use the command qemu-nbd to make the disk image available as network block device:

$ qemu-nbd --connect=/dev/nbd0 /path/to/image.qcow2

fdisk can be used to list the available partitions inside the disk image:

$ fdisk -l /dev/nbd0

Finally mount the partition (in this case partition #2) to an arbitrary mount point:

$ mount /dev/nbd0p2 /path/to/mountpoint/

chroot into the directory as usual. When you're done, exit the chroot, then unmount the partition and disconnect the network block device:

$ umount /dev/nbd0p2
$ qemu-nbd --disconnect /dev/nbd0

Similarly, a partition inside a raw disk image can be mounted as loop device:

$ losetup /dev/loop0 /path/to/diskimage.img

Make the partition mappings available:

$ kpartx -av /dev/loop0

And mount the partition (here partition #1):

$ mount /dev/mapper/loop0p1 /mnt/image

Alternatively, the partition can be mounted directly: In this case its offset (in bytes) from the beginning of the disk image needs to be passed to the mount command, fdisk will provide the necessary values to calculate it.

Here an example disk image with only one partition:

$ fdisk -l netinst.img

Disk netinst.img: 32 MiB, 33554432 bytes, 65536 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb6bdaf05

Device         Boot   Start     End   Sectors    Size   Id   Type
netinst.img1   *       2048   65535     63488     31M    b   W95 FAT32

The "Start" sector of the target partition (here 2048) needs to be multiplied with the disk's sector size, here 512 bytes. In this case the partition's offset is 512*2048=1048576 bytes:

$ mount -o loop,offset=1048576 netinst.img /path/to/mountpoint/

From DNSmasq to Unbound with DNSSEC and ISC's DHCP server.

Fiddling with dnsmasq to enable DNSSEC for my LAN convinced me of replacing it with unbound as DNS resolver and the ISC dhpcd.

Network overview: WAN, LAN, the router's hostname is raspi and its LAN IP The local domain is lan.

$ apt-get install unbound unbound-host resolvconf isc-dhcp-server

After installing the necessary software, configure the unbound DNS server for LAN and loopback device. As unbound doesn't parse /etc/hosts, any LAN hosts to be resolved need to be defined as local-data.

### File: /etc/unbound/unbound.conf

#include: "/etc/unbound/unbound.conf.d/*.conf"

    access-control: allow 
    do-ip6:                 no
    verbosity:              1
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    local-zone:             "lan" static
    local-data:             "raspi.lan. IN A"
    local-data:             "$HOSTNAME.lan. IN A"
    local-data:             "$HOSTNAME1.lan. IN A"
    private-domain:         "lan"

    name:                   "."
    forward-addr:           $DNS_IP
    forward-addr:           $DNS_IP1
    forward-addr:           $DNS_IP2

The $HOSTNAMEs und $DNS_IPs need to be defined.

Note: The OpenNIC DNS servers don't support DNSSEC yet. To improve the experience, you may want to add the line "harden-dnssec-stripped: no" to the server configuration, which makes dnssec validation optional. Otherwise many sites will fail to resolve due to missing DNSSEC data.

To test the configuration, disable dnsmasq's DNS by adding the line port=0 to /etc/dnsmaq.conf and restart the daemon (we still need its dhcp), before starting unbound:

$ /etc/init.d/dnsmasq restart
$ /etc/init.d/unbound start
$ /etc/init.d/resolvconf restart

Note: After rebooting my Raspberry Pi, the auto-trust-anchor-file mechanism failed to verify the anchor, due to the lack of a hardware clock while openntpd not yet being ready with setting the time - so unbound failed to start. Adding openntpd to the Required-Start section of unbound's init script solves this issue, if openntpd is run with the -s flag set. For Devuan's SysV-Init:

### File: /etc/init.d/unbound:
# ...
# Required-Start:    $network $remote_fs $syslog $openntpd
# ...


### File: /etc/default/openntpd:

DAEMON_OPTS="-s -f /etc/openntpd/ntpd.conf"

If DNS works for the router itself and on the LAN, it's time to turn towards the new DHCP server. A simple configuration as the only authoritative DHCP Server on one subnet and with two static leases - the "empty" subnet definition for the uplink saves some lines of daemon.log:

### File: /etc/dhcp/dhcpd.conf

default-lease-time 7200;
max-lease-time 14400;

subnet netmask {

subnet netmask {
option domain-name "lan";
option domain-name-servers;

host $HOSTNAME {
hardware ethernet 00:11:22:33:44:55;

host $HOSTNAME1 {
hardware ethernet 01:11:22:33:44:55;

Now fully disable dnsmasq by setting ENABLED=0 in /etc/default/dnsmasq and run

$ /etc/init.d/dnsmasq restart
$ /etc/init.d/isc-dhcp-server start

Any typos in the configuration? Be prepared to edit your workstations /etc/network/interfaces when you now request a new DHCP lease!

When back on the network, you can test the DNSSEC authentication chain by running dig from the dnsutils package

$ dig com. SOA +dnssec | grep flags

(dig's output, on the router as on its DHCP clients, should include the ad flag.), respectively unbound-host on the router itself:

$ unbound-host -C /etc/unbound/unbound.conf -rd

Also nice to have is the DNSSEC/TLSA Validator Mozilla add-on by cz.nic.

Devuan Jessie (beta) on a Raspberry Pi 2 as LAN router with WiFi uplink.

Featuring dnsmasq, openntpd and the simple webfsd HTTP server. As I don't need a full-grown mail server on the LAN, exim4-daemon-light is configured to only deliver local mail, which will be served by dovecot-imapd to mail clients on the LAN. Finally, the RPi's underclocking capabilities will be enabled to save idle CPU cycles.

Image used: devuan_jessie_1.0.0-beta_armhf_raspi2.img.xz from

Basic setup

xzcat the image to an SD card and boot the Pi with connected WiFi adapter and ethernet. Connect via wired LAN, login with ssh (root:toor) and adjust the defaults with the command raspi-config. Create an unprivileged user "piuser" and change the root password:

$ adduser piuser
$ passwd

Disable ssh root login and restart sshd to apply the changes:

### File: /etc/ssh/sshd_config

PermitRootLogin no

Hint: Connect with ssh as the new user and su before logging off from the current root shell.

Configuring apt

Add Devuan's security repository:

### File: /etc/apt/sources.list

deb jessie-security main
deb-src jessie-security main

Avoid the automatic installation of recommended packages:

### File /etc/apt/apt.conf.d/01norecommends

APT::Install-Recommends "0";
APT::Install-Suggests "0";

​Update the system:

$ apt-get update && apt-get dist-upgrade

Install additional software:

$ apt-get install dnsmasq wireless-tools iptables-persistent \
    dovecot-imapd exim4-daemon-light webfsd cpufrequtils \

Local mail transport

Create a mail alias to forward root's mail to the newly created unprivileged user:

$ echo "root: piuser" >> /etc/aliases

To configure exim4, run "dpkg-reconfigure exim4-config" and change the respective options to:

"local delivery only; not on a network"
"Maildir format in home directory"

Test local mail delivery:

$ echo test | mailx -s test1 postmaster@localhost

Network setup

Enable port forwarding:

### File: /etc/sysctl.conf


Configure the network interfaces:

### File: /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

auto wlan0
iface wlan0 inet dhcp
    wpa-ssid    $WIFI_SSID
    wpa-psk     $WIFI_PASSWD

Note: SSID and passphrase for wlan0 need to be defined.

Disable IPv6 by blacklisting the IPv6 kernel module:

$ echo "install ipv6 /bin/true" >> /etc/modprobe.d/blacklist.conf

Configure iptables with some basic NAT and filtering rules:

### File: /etc/iptables/rules.v4

#-A PREROUTING -i wlan0 -p tcp --dport 10022 -j DNAT --to

-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -m state --state NEW -j ACCEPT
#-A INPUT -p tcp --dport 8000 -j ACCEPT
-A FORWARD -i eth0 -o wlan0 -m state --state NEW -j ACCEPT
#-A FORWARD -p tcp -d --dport 22 -j ACCEPT

NOTE: Rules for webfsd listening at port 8000 and ssh port forwarding from the WAN interface to are commented out.

Optionally, change the IPv6 default rules:

### File: /etc/iptables/rules.v6


Configure the dnsmasq DHCP and DNS server:

### File: /etc/dnsmasq.conf


Add some nearby DNS servers (See here for a full OpenNic DNS server list.)

### File: /etc/dnsmasq.d/10opennic


Define static DHCP leases:

### File: /etc/dnsmasq.d/20static_leases


As a workaround for the RPi's lack of a hardwareclock, add the -s flag to the DAEMON_OPTS variable in /etc/default/openntpd.

To apply the previously made changes and turn the RPi from DHCP client to server / router mode, run:

$ sysctl -p /etc/sysctl.conf
$ iptables-restore < /etc/iptables/rules.v4
$ ip6tables-restore < /etc/iptables/rules.v6
$ /etc/init.d openntpd restart
$ /etc/init.d/dnsmasq restart

Note: To unload the IPv6 kernel module, a reboot may be necessary.

Now it's time to take the RPi off the LAN and connect your workstation, again wired, directly to your new RPi2 Devuan router. Log back in:

$ ssh piuser@ 
$ su -

If everything went fine, you can now ping LAN and WAN from the Pi, as well as access the WAN from within the LAN.

The webfsd http server

There's not much configuration needed to bring webfsd up:

### File: /etc/webfsd.conf

web_extras="-4 -b user:pass"

Start the daemon:

$ /etc/init.d/webfs restart

Sufficient file permissions given, the content of $web_root will now be served at port 8000 on all interfaces; login with user:pass.

Setting up the dovecot IMAP server

Generate a self-signed SSL certificate:

$ openssl genrsa -out /etc/dovecot/private/dovecot.key 1024
$ openssl req -new -x509 -key /etc/dovecot/private/dovecot.key \
-out /etc/dovecot/dovecot.pem -days 365

Some minimal configuration:

### File: /etc/dovecot/dovecot.conf AND/OR /etc/dovecot/conf.d/*

disable_plaintext_auth = yes
auth_mechanisms = plain
listen =
mail_location = maildir:~/Maildir
protocols = "imap"
ssl_cert = </etc/dovecot/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.key

By disabling any auth_mechanisms but plain while setting disable_plaintext_auth, SSL/TLS login will be forced. Note: This won't prevent misconfigured clients from sending unencrypted passwords. To start the server, run "/etc/init.d/dovecot restart".

CPU underclocking

(This might void the RPi's warranty.)

Mount the boot partition:

$ mount /dev/mmcblk0p1 /mnt/

Edit / create the following file; the values here have proven to not cause instabilities:

### File: /mnt/config.txt


Although cpufrequtils defaults to the governor ondemand, it can be explicitely defined or changed by creating the following file; avilable governors are listed in /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors.

### File: /etc/default/cpufrequtils


Reboot to activate and watch the changes in /sys/devices/system/cpu/cpu[0-3]/cpufreq/cpuinfo_cur_freq.

Automatic system update

To keep the new Devuan system up to date, I let cron execute my update-script every four hours: Open/edit the crontab with the command "cronatb -e".

### File: root crontab (/var/spool/cron/crontabs/root)

25 */4 * * * PATH='/usr/sbin:/usr/bin:/sbin:/bin' /path/to/


Test the upgrade script and mail transport, e.g. by downgrading the tzdata package, then running and receiving the upgrade log with an IMAP client (StartTLS/Port143 or SSL/Port993 with piuser login credentials) over the new LAN.

Final steps

Delete the content of /var/cache/apt/archives, then "dd if=/dev/zero" the free space on the SD card, shut down the Pi and pull a disk image!

Create a bootable FreeDOS medium from within Linux to flash your BIOS

The quick and dirty way is to simply dd a DOS floppy image to an empty USB drive. I use the FreeDOS FDOEM.288 image from, because it contains barely anything but kernel and COMMAND.COM, so there is enough space left for the payload.

Note: All data on the drive will be lost.

$ dd if=/path/to/FDOEM.288 of=/dev/sdX

Now mount the USB drive and add the updater and flashrom binaries. That's it.

If you want to flash a computer that neither has a floppy drive nor can't boot from USB, you can create a CD-ROM from the floppy disk image. Mount the FDOEM.288 image and add the updater and flashrom binaries to the image. Working directory is the directory containing the floppy disk image:

$ mount -o loop -t msdos FDOEM.288 /mnt

$ cp /path/to/updater /path/to/firmware /mnt/

$ umount /mnt

genisoimage creates a bootable ISO9660 image, which will then be burned to CD-ROM with wodim:

$ genisoimage -o fdoem288.iso -b FDOEM.288 FDOEM.288

$ wodim -v dev=/dev/cdrw fdoem288.iso

If you need more space than the approximately 2.5MB available in the FDOEM.288 image, or a MS Windows environment, you might want to boot a copy of BartPE in a virtual machine. From within that, you can easily create a custom BartPE boot CD or even a multi purpose / emergency hard disk.