Ein schreibgeschütztes Linux-System – so geht es

Von ·Kategorien: Technik erklärt·Published On: Juli 14th, 2022·11,7 min read·

Von Zeit zu Zeit erhalten wir Fragen von Kund:innen, die ein schreibgeschütztes Linux verwenden möchten. Dadurch möchten sie die Langlebigkeit ihrer Flash-Geräte und Solid-State-Speicher maximieren. Wir dachten, wir nutzen die Chance und erstellen einen Blog-Beitrag, in dem beschrieben wird, wie man ein schreibgeschütztes Linux-System erstellt.

Es gibt einige verschiedene Ansätze, um ein schreibgeschütztes Linux zu erhalten. Leider ist dies nicht so einfach wie die Verwendung eines konventionellen Dateisystems, das mit der Schreibschutz-Option gekoppelt ist. Viele Programme gehen davon aus, dass zumindest einige Teile des Systems beschreibbar sind. In einigen Fällen funktionieren diese Programme nicht richtig, wenn sich zeigt, dass dies nicht der Fall ist.

Ich werde hier skizzieren, was meiner Meinung nach der beste Ansatz für die meisten Anwendungen ist. Er ist ähnlich wie bei den wichtigsten Live-CD-Ausgaben.

Live-CDs hatten typischerweise einen schreibgeschützten Zugang zu einem Stammdateisystem, das oft in eine einzige Datei komprimiert ist. So kann es später mittels eines Loopback-Gerätes eingebunden werden. Knoppix hat neue Wege beschritten mit der Verwendung des Cloop-Dateisystems für diesen Zweck.

Andere Live-Ausgaben gehen noch einen Schritt weiter. Sie verwenden ein Union-Dateisystem, um das Stammdateisystem beschreibbar zu machen. Dieser Ansatz ist für unsere Intention ebenfalls sehr hilfreich.

Union-Dateisysteme

Im Allgemeinen kombiniert ein Union-Dateisystem mehrere Dateisysteme in einem einzigen virtuellen Dateisystem. Es gibt zwei beliebte Union-Dateisysteme: unionfs und aufs. Beide haben das gleiche Basismodell. Das Folgende ist eine drastische Vereinfachung:

  • Dateisysteme sind vertikal angeordnet.
  • Lesezugänge werden werden auf jedem Dateisystem abwechselnd von oben nach unten durchgeführt. Das erste Dateisystem, das die zu lesende Datei enthält, wird für den Lesevorgang verwendet.
  • Schreibzugänge werden ähnlich ausgeführt. Jedoch werden die Dateien, auf die geschrieben wird, im obersten beschreibbaren Dateisystem gespeichert. Das heißt in der Regel, dass es eine einzige beschreibbare Ebene in der Union gibt. Wenn Dateien, die in einer schreibgeschützten Ebene existieren, beschrieben werden, werden sie zunächst in die nächsthöhere Ebene kopiert.

Natürlich gibt es viele Feinheiten und Sonderfälle, die ich hier nicht präsentiere. Wichtig ist, dass wir ein schreibgeschütztes Dateisystem verwenden können. Dabei kann dies ein Image eines komprimierten Dateisystems oder ein Flash-Gerät sein, das ein konventionelleres Dateisystem wie ext3 enthält). Darauf bauen wir ein beschreibbares System auf. Alles, was wir benötigen, ist die Vereinigung eines beschreibbaren Dateisystems mit der Leseebene.

Die beschreibbare Ebene

Welche Art eines beschreibbaren Dateisystems Sie verwenden, hängt davon ab, was Sie erreichen möchten. Wenn Sie keine Persistenz zwischen den Boots benötigen, ist es ziemlich leicht, tmpfs zu verwenden. Schreibvorgänge in das System werden im RAM gespeichert, während das System hochgefahren ist. Jedoch verschwinden sie, wenn das System heruntergefahren oder neu gestartet wird. 

Wenn Sie Persistenz über die gesamte Systemstruktur möchten, müssen Sie eine dauerhaft beschreibbare Ebene verwenden. Dies ist wahrscheinlich ein konventionelles Dateisystem auf einem anderen Medium (vielleicht eine zweite Festplatte). Vermutlich ist dies am nützlichsten für Live-Systeme oder Thin-Clients, bei denen die Verwendung einer schreibgeschützten Basis nicht so sehr für die Langlebigkeit als vielmehr zur Minimierung der lokalen Speicheranforderungen erfolgt.

In vielen Fällen, in denen Sie Persistenz benötigen, brauchen Sie diese nur für spezifische Dateien. Zum Beispiel muss die Datenbank auf der Festplatte verbleiben, wenn Sie einen Kiosk haben, der Nutzer-Input in einer lokalen Datenbank speichert. Aber Sie möchten wahrscheinlich nicht, dass temporäre Daten oder andere transiente Laufzeitdaten fortbestehen. Der beste Ansatz zum Umgang mit diesem üblichen Anwendungsfall ist es, eine tmpfs-Lese-Schreibebene zu haben und dann einige beschreibbare Medien an einem beliebigen Aktivierungspunkt wie zum Beispiel /var/local/data  einzufügen.

Implementierung

Ein schreibgeschütztes Linux-System zu implementieren, erfordert das Einklinken in den Boot-Vorgang. Wie dies zu tun ist, variiert von Ausgabe zu Ausgabe. Es kann innerhalb einer Ausgabe wahrscheinlich auf viele Arten erfolgen. In diesem Artikel zeige ich einen Ansatz, der mit Ubuntu 8.04 arbeitet.

Standardmäßig nutzt Ubuntu 8.04 ein initramfs. Dies ist der beste Ort, um unsere Modifikation durchzuführen. So können wir sicherstellen, dass das Union-Dateisystem früh im Boot-Vorgang eingebunden wird.

initramfs-tools

Ubuntu hat ein erweiterbares System für den Bau von initramfs. Es heißt “initramfs-Tools”. Wir können es nutzen, um einige Skripte an die initramfs anzubinden. Dabei gibt es verschiedene Wege, initramfs-Tools zu erweitern: “hooks” und “scripts”. 

Hooks werden beim Aufbau der initramfs ausgeführt. Sie sind nützlich, um Kernel-Module oder ausführbare Dateien zum initramfs-Image hinzufügen. Hooks, die mit den Packages ausgegeben werden, sind meist unter /usr/share/initramfs-tools/hooks installiert. Sie nutzen die Funktionen, welche unter /usr/share/initramfs-tools/hook-functions definiert sind. Lokale Hooks sollten unter /etc/initramfs-tools/hooks platziert werden.

Skripte werden innerhalb der initramfs-Umgebung zur Boot-Zeit ausgeführt. Diese können verwendet werden, um den frühen Boot-Vorgang zu modifizieren. Wie bei den Hooks werden Skripte, die mit den Packages ausgegeben werden, meist unter /usr/share/initramfs-tools/scripts installiert. Lokale Skripte sollten unter /etc/initramfs-tools/scripts sein.

Die initramfs-Generation wird von den Konfigurationsdateien gesteuert, die unter /etc/initramfs-tools zu finden sind. /etc/initramfs-tools/initramfs.conf ist die primäre Konfigurationsdatei. Aber Dateien können auch unter /etc/initramfs-tools/conf.d abgelegt werden. 

Die primäre Boot-Methode kann unter initramfs.conf konfiguriert werden, indem der Wert der Variablen “BOOT” geändert wird. Standardmäßig ist dieser “local”, eine Boot-Methode, die das Stammdateisystem auf ein lokales Medium wie eine Festplatte einsetzt.

Boot-Methoden

Für jede Boot-Methode gibt es ein Skript mit dem Namen, der die Funktion dieser Boot-Methode steuert. Beispielsweise gibt es ein Skript mit dem Namen “local”, das definiert, wie ein lokaler Boot ausgeführt wird. Viele solcher Skripte liefern auch Hooks für andere Shell-Skripte, die an bestimmten Punkten während des Boot-Vorgangs ausgeführt werden. So werden alle Skripte, die unter /usr/share/initramfs-tools/local-premount platziert werden, vom “local”-Skript unmittelbar vor dem Einhängen des Stammdateisystems ausgeführt. 

Das Init-Skript selbst funktioniert als Prozess #1 bis zum Punkt, an dem der echte Init-Daemon gestartet wird, nach dem Einhängen des Stammdateisystems. Dabei liefert es ähnliche Hooks. Sehen Sie die Inhalte unter /usr/share/initramfs-tools/scripts an, um eine Idee davon zu erhalten, welche anderen Hooks verfügbar sind. 

Zum Schluss müssen sowohl Hooks als auch Skripte so geschrieben werden, dass sie bei Ausführung mit einem einzigen Argument „prereqs“, eine durch Leerzeichen getrennte Liste mit den Namen anderer Skripte oder Hooks ausgeben, die vor der Ausführung dieses bestimmten Skripts oder Hooks ausgeführt werden sollten. Das liefert ein einfaches System aus Abhängigkeiten zwischen Hooks und Skripten. Ich nutze diese Funktion selten, aber sie ist verfügbar, sollte Ihre Anwendung sie benötigen.

Hooks und Skripte 

Wir implementieren unser schreibgeschütztes System in Linux, indem wir einen Hook und ein Skript einbringen. Unser Skript wird eigentlich ein Init-Bottom-Skript sein. Dabei wird es laufen, nachdem das echte Root-Gerät bereits verbunden ist. 

Unser Ziel wird es sein, ein bereits eingebundenes Stammdateisystem zu nehmen und es umzumischen als Basis für eine aufs-Union mit einer tmpfs-beschreibbaren Ebene. Das ermöglicht uns, weiterhin die standardmäßigen Ubuntu-Konfigurationsmechanismen zu verwenden, um das Gerät zu spezifizieren, welches das echte Stammdateisystem enthält.

Wir benötigen einen Hook, um den initramfs-Tools zu sagen, dass wir einige Kernel-Module (aufs und tmpfs, von denen beide in Ubuntu 8.04 enthalten sind) benötigen sowie eine ausführbare (chmod). Wir sehen in Kürze, warum wir chmod brauchen. Unser Hook ist ziemlich simpel wie die meisten.

Wir nennen dies hooks/ro_root:

#!/bin/sh

PREREQ=“

prereqs() {

  echo „$PREREQ“

}

case $1 in

prereqs)

  prereqs

  exit 0

  ;;

esac

. /usr/share/initramfs-tools/hook-functions

manual_add_modules aufs

manual_add_modules tmpfs

copy_exec /bin/chmod /bin

Das Skript leistet die wirkliche Arbeit, indem es dafür sorgt, dass alle Dateisysteme an den richtigen Orten eingebunden sind. An diesem Punkt im Boot-Vorgang wurde das echte Root-Gerät auf $rootmnt installiert. /sbin/init steht an diesem Einhängepunkt kurz vor der Ausführung. An diesem Punkt werden wir versuchen, das Root-Gerät auf einen anderen Einhängepunkt zu verschieben und unser Union-Mount an dieser Stelle aufzubauen.

Wir machen dies folgendermaßen:

  1. Verschieben Sie $rootmnt nach /${rootmnt}.ro (das ist die schreibgeschützte Ebene).
  2. Hängen Sie unsere beschreibbare Ebene als tmpfs in /${rootmnt}.rw ein.
  3. Fügen Sie die Union auf ${rootmnt} ein.

Zusätzlich möchten wir einen von der Union unabhängigen Zugang zu den schreibgeschützten und Lese-/Schreibebenen haben. Um Zugänge zu diesen Trägern zu haben, müssen wir sie in einen neuen Einbaupunkt unter ${rootmnt} einbinden. Wir machen dies mit  “mount –bind”.

Die Union ist weiterhin in der Lage, auf den originalen schreibgeschützten und Lese-/Schreib-Zugang zuzugreifen, selbst nachdem die Stammfunktion gedreht und init gestartet wurde. Das führt dazu, dass diese Einhängepunkte aus dem neuen Stammdateisystem herausfallen. Ich nehme an, dass aufs diese Verzeichnisse zur Einhängezeit öffnet und die Dateisysteme weiterhin so lange zugänglich sind, wie die Prozesse offene Dateizugriffe haben. Somit scheint der Kernel im Umgang mit dieser Art von interessanten Situationen ziemlich schlau zu sein.

Um zum Wesentlichen zurückzukommen: Hier ist das init-bottom-Skript, das wir verwenden werden:

(scripts/init-bottom/ro_root):

#!/bin/sh

PREREQ=“

prereqs() {

  echo „$PREREQ“

}

case $1 in

prereqs)

  prereqs

  exit 0

  ;;

esac

ro_mount_point=“${rootmnt%/}.ro“

rw_mount_point=“${rootmnt%/}.rw“

# Erstellen Sie Einhängepunkte für die schreibgeschützten und die Lese-/Schreibebenen:

mkdir „${ro_mount_point}“ „${rw_mount_point}“

# Verschieben Sie das bereits eingehängte Root-Dateisystem auf den Einhängepunkt ro:

mount –move „${rootmnt}“ „${ro_mount_point}“

# Hängen Sie das Lese-/Schreib-Dateisystem ein:

mount -t tmpfs root.rw „${rw_mount_point}“

# Verbinden Sie die Einheit:

mount -t aufs -o „dirs=${rw_mount_point}=rw:${ro_mount_point}=ro“ root.union „${rootmnt}“

# Korrigieren Sie die Berechtigungen von /:

chmod 755 „${rootmnt}“

# Stellen Sie sicher, dass die einzelnen ro- und rw-Verbindungen vom Stammverzeichnis aus zugänglich sind

# sobald die Einheit als / angenommen wird. Dadurch ist es möglich,

# auf die einzelnen Dateisysteme der Komponenten einzeln zuzugreifen.

mkdir „${rootmnt}/ro“ „${rootmnt}/rw“

mount –bind „${ro_mount_point}“ „${rootmnt}/ro“

mount –bind „${rw_mount_point}“ „${rootmnt}/rw“

Neuaufbau der initramfs

Der Hook und das init-bottom-Skript, das wir oben geschrieben haben, können an den folgenden Orten entsprechend eingerichtet werden:

  • /etc/initramfs-tools/hooks/ro_root.
  • /etc/initramfs-tools/scripts/init-bottom/ro_root.

Sie sollten beide das Ausführerlaubnis-Bit gesetzt haben. 

Nachdem Sie die Dateien an den richtigen Ort kopiert haben, generieren Sie Ihre initramfs neu mit:

update-initramfs -u

Der -u-Schalter weist update-initramfs an, die initramfs für den neuesten Kernel auf dem System zu aktualisieren. Ich nehme an, dass dies der Kernel ist, den Sie einsetzen. Für die meisten Embedded- oder anderen Einzweck-Maschinen ist typischerweise nur ein Kernel installiert.

Booting

Das System sollte den Anschein machen, als würde es booten ohne die von uns vorgenommenen Änderungen. Dennoch können Sie, nachdem das Booten beendet ist, Folgendes bestätigen:

  • /ro enthält das schreibgeschützte Basis-Dateisystem.
  • /rw enthält die Lese-/Schreibebene und hat meist einige neue Dateien, die unmittelbar nach dem Boot folgen (/var/run, etc.).
  • Wenn Sie eine Datei erstellen und dann rebooten, wird die Datei weg sein.

Ein System wie dieses hat einige Einschränkungen:

  • Die Inhalte von /etc/mtab sind wahrscheinlich nicht korrekt, daher fehlen der Ausgabe des Einhängen-Befehls vermutlich einige Informationen. Es gibt Schritte, die wir vornehmen können, um /etc/mtab zu korrigieren. Jedoch werde ich hier nicht im Detail darauf eingehen.
  • Kein Laufzeitstatus ist gespeichert. Vergessen Sie das nicht und speichern Sie eine Datei in der Erwartung, dass Sie nach einem Reboot noch vorhanden ist!
  • Subtile semantische Unterschiede zwischen aufs, tmpfs und herkömmlichen Dateisystemen können Probleme mit einigen Anwendungen verursachen. Die meisten Anwendungen werden es nicht bemerken. Allerdings könnten die, die fortgeschrittenere Dateisystem-Funktionen nutzen oder sich auf Details der Dateisystem-Implementierungen verlassen, auf Fehler stoßen oder, noch schlimmer, subtil scheitern. Ich glaube, die meisten dieser Probleme gehören mittlerweile der Vergangenheit an. Wenn Sie aber auf der rätselhaften Suche nach Fehlern sind, denken Sie daran.

Diese Art der Systemanpassung zeigt wirklich die Leistung und Flexibilität der Konfigurationsinfrastruktur der initramfs-Tools. Dieser Architektur-Stil ist in Debian und Ubuntu üblich und macht diese Ausgaben zur idealen Wahl für Embedded- und angewandte Informatikprojekte.

Ich hoffe, das war hilfreich. Wenn Sie weitere Fragen zur Erstellung eines schreibgeschützten Linux-Systems haben, wenden Sie sich noch heute an unser Team.

Verbesserungen

[Abschnitt hinzugefügt am 23.02.2009, aktualisiert am 14.07.2022]

Das folgende aktualisierte Skript beinhalten einige Verbesserungen, die bei gewissen Problemen geholfen haben, auf die die Kommentierenden gestoßen sind:

  • Booten Sie mit dem normal eingestellten Lese-/Schreib-Stammdateisystem, wenn der Nutzer einen Einzelbenutzermodus (auch Wiederherstellungsmodus) anfordert.
  • Verhindern Sie die Ausführung von /etc/init.d/checkroot.sh beim Booten in das schreibgeschützte System.
  • Verwenden Sie –move instead of mount –bind beim Verschieben der ro und rw Einhängepunkte in die neue Root.

#!/bin/sh

PREREQ=“

prereqs() {

  echo „$PREREQ“

}

case $1 in

prereqs)

  prereqs

  exit 0

  ;;

esac

# Booten Sie normal, wenn der Benutzer den Einzelbenutzermodus auswählt.

if grep single /proc/cmdline >/dev/null; then

  exit 0

fi

ro_mount_point=“${rootmnt%/}.ro“

rw_mount_point=“${rootmnt%/}.rw“

# Erstellen Sie Einhängepunkte für die schreibgeschützten und die Lese-/Schreibebenen:

mkdir „${ro_mount_point}“ „${rw_mount_point}“

# Verschieben Sie das bereits eingehängte Root-Dateisystem auf den Einhängepunkt ro:

mount –move „${rootmnt}“ „${ro_mount_point}“

# Hängen Sie das Lese-/Schreib-Dateisystem ein:

mount -t tmpfs root.rw „${rw_mount_point}“

# Installieren Sie die Einheit:

mount -t aufs -o „dirs=${rw_mount_point}=rw:${ro_mount_point}=ro“ root.union „${rootmnt}“

# Korrigieren Sie die Berechtigungen von /:

chmod 755 „${rootmnt}“

# Korrigieren Sie die Berechtigungen von /:

# sobald die Einheit als / angenommen wird. Dadurch ist es möglich,

# auf die einzelnen Dateisysteme der Komponenten einzeln zuzugreifen.

mkdir „${rootmnt}/ro“ „${rootmnt}/rw“

mount –move „${ro_mount_point}“ „${rootmnt}/ro“

mount –move „${rw_mount_point}“ „${rootmnt}/rw“

# Stellen Sie sicher, dass checkroot.sh nicht ausgeführt wird.  Es könnte fehlschlagen oder fälschlicherweise / neu einhängen.

rm -f „${rootmnt}/etc/rcS.d“/S[0-9][0-9]checkroot.sh

Tech-Updates & neue Einblicke

Abonnieren Sie unseren Newsletter und die neuesten Mitteilungen von OnLogic kommen direkt in Ihren Posteingang. News und Einblicke von unserem Team aus Fachleuten sind nur einen Klick entfernt. Über den Button gelangen Sie zu unserer Abo-Seite.

Teilen

About the Author: OnLogic

OnLogic ist ein globaler Hersteller von Industrie-Computern, der stark konfigurierbare, lösungsorientierte Computer entwirft, die für Verlässlichkeit im IoT-Edge konzipiert sind.