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