Archiv für den Monat: September 2017

Raspberry Pi SD-Karte schreibgeschützt nutzen

Raspberry Pis sind berüchtigt dafür, dass sie die Inhalte verwendeter SD-Karten gerne mal beschädigen. Häufig ist der Grund dahinter, dass der Strom getrennt wurde, während der Pi versuchte, auf die SD-Karte schreibend zuzugreifen.

Sofern man nur eine einfache Anwendung hat, welche keine lokalen Daten schreiben muss, kann man die SD-Karte auch direkt „readonly“ mounten, und damit den schreibenden Zugriff komplett unterbinden.

Es ist unerheblich, ob man auf dem Raspberry Pi ein klassisches Raspbian oder ein minimal-Image nutzt. Allerdings wird es sehr viel einfacher, wenn man auf eine grafische Benutzeroberfläche verzichten kann.

Ich habe bei zwei Anwendungsfällen hierzu bereits Erfolg gehabt. Bei dem einen wurde der eingesetzte Raspberry Pi als Steuereinheit für ein Flipdot im Chaostreff Dortmund eingesetzt. Bei dem anderen sollte der Raspbery Pi ausschließlich im Kiosk-Modus eine (lokal gehostete) Webseite anzeigen. Für den letzeren Fall griff ich auf Chromium zurück, welcher sich gut über Kommandozeilenparameter anpassen ließ.

Zunächst müssen wir das System vorbereiten und ein wenig verkleinern. Sofern ein klassisches Raspbian verwendet wird, sollte man vorher mit sudo su - auf den root-Benutzer wechseln, um die nachfolgenden Befehle ausführen zu können. Um möglichst kompatibel in den Befehlen zu bleiben, führe ich das sudo nicht immer mit aus, da bei minimal-Linuxen sudo gar nicht erst als Paket eingebunden ist und man sowieso nur als root arbeiten kann.

Als erstes sollte sichergestellt sein, dass die gesamte SD-Karte auch verwendet wird. In raspi-config kann man dies einstellen.

apt-get update
apt-get -y upgrade
reboot

Der reboot ist notwendig, damit neuere Kernel bereits geladen sind.

Nachfolgend werden alle grafischen Bentuzerelemente entfernt sowie der cron-Daemon, welcher sonst log-Einträge verursachen würde.

apt-get remove --purge wolfram-engine triggerhappy anacron logrotate dphys-swapfile xserver-common lightdm
insserv -r x11-common
apt-get autoremove --purge

Log-Messages werden noch im Speicher gehalten und können mit logread ausgelesen werden, nachdem man den „busybox“-syslogd verwendet:

apt-get install busybox-syslogd; dpkg --purge rsyslog

Die Einträge fastboot noswap ro fügen wir nun der cmdline.txt an, hierdurch wird das Dateisystem als nicht-schreibbar („ro“ = „readonly“) gesetzt und der Swap (die Auslagerungsdatei) abgeschaltet. Ressourcenintensive Speicherfresser können somit nicht mehr eingesetzt werden!

echo -n "fastboot noswap ro" >>/boot/cmdline.txt

Einige typische Dateien und Verzeichnisse müssen nun direkt auf das tmpfs (das ist die Ramdisk im Speicher des Raspberry Pis) verlinkt werden.

rm -rf /var/lib/dhcp/ /var/run /var/spool /var/lock /etc/resolv.conf
ln -s /tmp /var/lib/dhcp
ln -s /tmp /var/run
ln -s /tmp /var/spool
ln -s /tmp /var/lock
touch /tmp/dhcpcd.resolv.conf; ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf

Die dhcpcd.pid muss ebenfalls auf das tmpfs zeigen, hierfür muss (zumindest auf dem Raspberry Pi 3) eine Datei angepasst werden.

sed -i /etc/systemd/system/dhcpcd5 's#PIDFile=/run/dhcpcd.pid#PIDFile=/var/run/dhcpcd.pid#'

Falls mit random-seed gearbeitet wird, muss dies ebenfalls ins tmpfs. Der Befehl ist ungefährlich, falls dies nicht der Fall ist und kann trotzdem ausgeführt werden:

rm /var/lib/systemd/random-seed
ln -s /tmp/random-seed /var/lib/systemd/random-seed

Das random-seed wird allerdings bei jedem Neustart benötigt, daher müssen wir die Datei mithilfe von systemd noch anlegen, wenn der Dienst gestartet wird.

sed -i /lib/systemd/system/systemd-random-seed.service 's#ExecStart=/lib/systemd/systemd-random-seed load#ExecStartPre=/bin/echo "" >/tmp/random-seed\nExecStart=/lib/systemd/systemd-random-seed load#'
systemctl daemon-reload

Abschließend müssen wir noch ein paar startup-Skripte loswerden:

insserv -r bootlogs; insserv -r console-setup

Da die SD-Karte üblicherweise mit „/dev/mmcblk0p1“ eingebunden wird, können wir mit einem kleinem Skript noch die /etc/fstab verändern, so dass „/boot“ und „/“ beim nächsten starten nur noch als Nur-Lesbar gemountet werden.

sed -i /etc/fstab 's#\(.*mmcblk.*defaults\)\(.*\)#\1,ro\2#'
cat <<EOT >>/etc/fstab
 
tmpfs           /tmp            tmpfs   nosuid,nodev         0       0
tmpfs           /var/log        tmpfs   nosuid,nodev         0       0
tmpfs           /var/tmp        tmpfs   nosuid,nodev         0       0
EOT

Um beim Einloggen auf den Raspberry Pi trotzdem Änderungen durchführen zu können, reichen zwei Kommandos ro und rw, welche man mit in die bash.bashrc hinzufügt. Falls man auf einem nicht-Multiuser-System unterwegs ist, muss man die „sudo“ natürlich noch vorher entfernen:

cat <<EOT >>/etc/bash.bashrc
# set variable identifying the filesystem you work in (used in the prompt below)
set_bash_prompt(){
    fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p")
    PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
}
 
alias ro='sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot'
alias rw='sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot'
 
# setup fancy prompt"
PROMPT_COMMAND=set_bash_prompt
EOT

Und beim Ausloggen noch automatisiert zurück auf read-only wechseln. Dies speichert auch die history für den aktuellen Benutzer ab.

cat <<EOT >>/etc/bash.bash_logout
mount -o remount,rw /
history -a
mount -o remount,ro /
mount -o remount,ro /boot
EOT