ubuntuusers.de

Nur-Lesen Root-Dateisystem

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Artikel für fortgeschrittene Anwender

Dieser Artikel erfordert mehr Erfahrung im Umgang mit Linux und ist daher nur für fortgeschrittene Benutzer gedacht.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

Dieser Artikel beschreibt, wie Schreibzugriffe auf das Root-Dateisystem auf eine RAM-Disk umgeleitet werden können. Dadurch ergeben sich die unter anderem folgende Vorteile:

  • Nach jedem Neustart ist immer ein „sauberes“ (und stabiles) System verfügbar.

  • Das Root-Dateisystem kann nicht beschädigt werden, wenn der Rechner einfach ausgeschaltet wird.

  • Änderungen am Dateisystem werden sichtbar

Dabei liegt der Fokus dieses Artikels darauf, ein System für den alltäglichen Einsatz zu haben, wobei trotzdem einfache permanente Änderungen und Aktualisierungen möglichen sind - z.B. als Media-Server, Automotive Computer etc. Denn das Nur-Lesen Dateisystem kann bei Bedarf beschreibbar eingebunden werden. Ist dies nicht gewünscht oder sollen noch weitere Anforderungen erfüllt werden, können evtl. die folgenden Ansätze weiterhelfen:

  • Der Wikipedia-Artikel Live-System beschreibt diverse Konfigurationen für verschieden Einsatzzwecke.

  • Das Aufsetzten eines einfachen Ubuntu Live-System auf einem USB-Stick ist im Artikel Live-USB beschrieben.

  • Einsatz von SquashFS für das Root-Dateisystem. Für jede permanente Änderung muss dann explizit ein neues Dateisystem-Abbild erzeugt werden.

Überblick

Prinzipiell existieren mit aufs und OverlayFS zwei Möglichkeiten, wie ein Nur-Lesen Root-Dateisystem erzeugt werden kann. Langfristig soll aufs aber durch OverlayFs ersetzt werden, da ersteres nur unter hohem Aufwand der Ubuntu-Kernel-Maintainer (Betreuer) geführt werden kann. Denn die aufs-Unterstützung wird nicht in den Upstream-Kernel aufgenommen.

Voraussetzungen

Die Kernelmodule aufs bzw. overlayfs müssen verfügbar sein. Dies lässt sich mit folgenden Kommandos feststellen:

sudo modprobe aufs 

bzw.

sudo modprobe overlayfs 

In den regulären von Ubuntu verwendeten Kerneln ist das aufs-Modul ab Ubuntu 10.04 bis einschließlich Ubuntu 12.04 verfügbar, das overlayfs-Modul ist ab Version 11.10 im Standardkernel enthalten. Es gibt allerdings spezielle Kernel für Portierungen, in denen diese Module fehlen, wie z.B. der neuste Kernel von TI für die ARM OMAP Platformen.

Umsetzung

Im Artikel Nur-Lesen Root-Dateisystem mit aufs ist eine Anleitung enthalten, die auch für OverlayFs geeignet ist. Es sind lediglich folgende Anpassungen nötig:

  1. Das Modul overlayfs muss in die Datei /etc/initramfs-tools/modules eingefügt werden, damit es im initramfs verfügbar ist:

    echo overlayfs >>/etc/initramfs-tools/modules 
  2. Es wird ein anderes Skript benutzt, nämlich root-ro.

    • Das root-ro Skript muss unter /etc/initramfs-tools/scripts/init-bottom/root-ro gespeichert werden.

    • Es muss ausführbar gemacht werden [4].

  3. Zuletzt muss das neue initramfs erstellt werden:

    sudo update-initramfs -u  

Dann kann das System neu gestartet werden. Ob alles geklappt hat, lässt sich dann mit folgendem Kommando überprüfen:

sudo mount 

Falls die originale Rootpartition /dev/sdXY war, sollte die Ausgabe jetzt u.a. diese drei Einträge enthalten:

overlayfs-root on / type overlayfs (rw)
/dev/sdXY on /mnt/root-ro type ext4 (rw,relatime,user_xattr,acl,barrier=1,data=ordered)
tmpfs-root on /mnt/root-rw type tmpfs (rw,relatime)

Funktion des Skripts root-ro

  • Die echte Rootpartition ist unter /mnt/root-ro nur lesbar eingehängt.

  • Die RAM-Disk, welche die Änderungen aufnimmt, ist unter /mnt/root-rw eingehängt.

  • Es werden die Treiber overlayfs und aufs unterstützt, dies wird über den Kernel Bootparameter root-ro-driver=[overlayfs|aufs] definiert. Dabei ist overlayfs der Standardwert, falls nichts angegeben ist.

Zum Deaktivieren gibt es zwei Möglichkeiten:

  • Es wird der Kernel Bootparameter disable-root-ro=true übergeben.

  • Im echten Root-Dateisystem kann eine (leere) Datei disable-root-ro in dessen Wurzelverzeichnis angelegt werden, also unter /mnt/root-ro/disable-root-ro. Nach einem Neustart sollte die Datei wieder entfernt werden.

Änderungen auf dem Nur-Lesen Dateisystem durchführen

Das original Root-Dateisystem ist unter /mnt/root-ro eingehängt und kann beschreibbar gemacht werden mit

sudo mount -o remount,rw /mnt/root-ro 

Dann können Änderungen einfach kopiert werden:

cp -v DATEI /mnt/root-ro 

Damit die Änderungen auch im „virtuellen“ Root-Dateisystem sichtbar werden, ist ggf. ein Neustart erforderlich. Denn wenn es sowohl auf der RAM-Disk (/mnt/root-rw) als auch im echten Root-Dateisystem (/mnt/root-ro) einen Eintrag gibt, hat derjenige auf der RAM-Disk Vorrang.

Deinstallation

Zur Deinstallation muss das Root-Dateinsystem zuerst wieder beschreibbar gemacht werden. Dafür gibt es die oben genannten Wege:

  1. Den Kernel mit dem Parameter disable-root-ro=true starten

  2. Oder die „magische“ Datei disable-root-ro benutzen

    sudo mount -o remount,rw /mnt/root-ro
    sudo touch /mnt/root-ro/disable-root-ro 

    neu starten

    sudo reboot  

    und nach dem Neustart die Datei wieder löschen:

    sudo rm /disable-root-ro 

Wurde das System mit einem „normalen“ beschreibaren Root-Dateisystem gestartet, kann das Skript einfach gelöscht und ein neues initramfs erzeugt werden:

sudo rm /etc/initramfs-tools/scripts/init-bottom/root-ro
sudo update-initramfs -u  

Das Skript root-ro

  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
#!/bin/sh
#  Copyright, 2012 Axel Heider
#
#  Based on scrpts from
#    Sebastian P.
#    Nicholas A. Schembri State College PA USA
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see
#    <http://www.gnu.org/licenses/>.
#
#
# Tested with Ubuntu 11.10
#
# Notes:
#   * no changes to the root fs are made by this script. 
#   * if /home/[user] is on the RO root fs, files are in ram and not saved.
#
# Install:
#  put this file in /etc/initramfs-tools/scripts/init-bottom/root-ro
#  chmod 0755 root-ro
#  optional: clean up menu.lst, update-grub
#  update-initramfs -u
#
# Disable read-only root fs
#   * option 1: kernel boot parameter "disable-root-ro=true"
#   * option 2: create file "/disable-root-ro"
#
# ROOT_RO_DRIVER variable controls which driver isused for the ro/rw layering
#   Supported drivers are: overlayfs, aufs
#  the kernel parameter "root-ro-driver=[driver]" can be used to initialize
#  the variable ROOT_RO_DRIVER. If nothing is given, overlayfs is used.
#

# no pre requirement
PREREQ=""

prereqs()
{
    echo "${PREREQ}"
}

case "$1" in
    prereqs)
    prereqs
    exit 0
    ;;
esac

. /scripts/functions

MYTAG="root-ro"
DISABLE_MAGIC_FILE="/disable-root-ro"

# parse kernel boot command line 
ROOT_RO_DRIVER=
DISABLE_ROOT_RO=
for CMD_PARAM in $(cat /proc/cmdline); do 
    case ${CMD_PARAM} in 
        disable-root-ro=*)
            DISABLE_ROOT_RO=${CMD_PARAM#disable-root-ro=}
            ;;
        root-ro-driver=*)
            ROOT_RO_DRIVER=${CMD_PARAM#root-ro-driver=}
            ;;
    esac
done

# check if read-only root fs is disabled
if [ ! -z "${DISABLE_ROOT_RO}" ]; then
    log_warning_msg "${MYTAG}: disabled, found boot parameter disable-root-ro=${DISABLE_ROOT_RO}"
    exit 0
fi
if [ -e "${rootmnt}${DISABLE_MAGIC_FILE}" ]; then
    log_warning_msg "${MYTAG}: disabled, found file ${rootmnt}${DISABLE_MAGIC_FILE}"
    exit 0
fi

# generic settings 
# ${ROOT} and ${rootmnt} are predefined by caller of this script. Note that
# the root fs ${rootmnt} it mounted readonly on the initrams, which fits nicely
# for our purposes.
ROOT_RW=/mnt/root-rw
ROOT_RO=/mnt/root-ro

# check if ${ROOT_RO_DRIVER} is defined, otherwise set default 
if [ -z "${ROOT_RO_DRIVER}" ]; then
    ROOT_RO_DRIVER=overlayfs
fi
# settings based in ${ROOT_RO_DRIVER}, stop here if unsupported. 
case ${ROOT_RO_DRIVER} in
    overlayfs)
        MOUNT_PARMS="-t overlayfs -o lowerdir=${ROOT_RO},upperdir=${ROOT_RW} overlayfs-root ${rootmnt}"
        ;;
    aufs)
        MOUNT_PARMS="-t aufs -o dirs=${ROOT_RW}:${ROOT_RO}=ro aufs-root ${rootmnt}"
        ;;
    *)
        panic "${MYTAG} ERROR: invalide ROOT_RO_DRIVER ${ROOT_RO_DRIVER}"
        ;;
esac


# check if kernel module exists 
modprobe -qb ${ROOT_RO_DRIVER}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: missing kernel module ${ROOT_RO_DRIVER}"
    exit 0
fi

# make the mount point on the init root fs ${ROOT_RW}
[ -d ${ROOT_RW} ] || mkdir -p ${ROOT_RW}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to create ${ROOT_RW}"
    exit 0
fi

# make the mount point on the init root fs ${ROOT_RO}
[ -d ${ROOT_RO} ] || mkdir -p ${ROOT_RO}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to create ${ROOT_RO}"
    exit 0
fi

# mount a tempfs using the device name tmpfs-root at ${ROOT_RW}
mount -t tmpfs tmpfs-root ${ROOT_RW}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to create tmpfs"
    exit 0
fi


# root is mounted on ${rootmnt}, move it to ${ROOT_RO}.
mount --move ${rootmnt} ${ROOT_RO}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to move root away from ${rootmnt} to ${ROOT_RO}"
    exit 0
fi

# there is nothing left at ${rootmnt} now. So for any error we get we should
# either do recovery to restore ${rootmnt} for drop to a initramfs shell using
# "panic". Otherwise the boot process is very likely to fail with even more 
# errors and leave the system in a wired state. 

# mount virtual fs ${rootmnt} with rw-fs ${ROOT_RW} on top or ro-fs ${ROOT_RO}.
mount ${MOUNT_PARMS}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to create new ro/rw layerd ${rootmnt}"
    # do recovery and try resoring the mount for ${rootmnt}
    mount --move ${ROOT_RO} ${rootmnt}
    if [ $? -ne 0 ]; then
       # thats badm, drpo to s shell to let the user try fixing this
       panic "${MYTAG} RECOVERY ERROR: failed to move ${ROOT_RO} back to ${rootmnt}"
    fi
    exit 0
fi

# now the real root fs is on ${ROOT_RO} of the init file system, our layered
# root fs is set up at ${rootmnt}. So we can write anywhere in {rootmnt} and the
# changes will end up in ${ROOT_RW} while ${ROOT_RO} it not touched. However 
# ${ROOT_RO} and ${ROOT_RW} are on the initramfs root fs, which will be removed
# an replaced by ${rootmnt}. Thus we must move ${ROOT_RO} and ${ROOT_RW} to the
# rootfs visible later, ie. ${rootmnt}${ROOT_RO} and ${rootmnt}${ROOT_RO}.
# Since the layered ro/rw is already up, these changes also end up on 
# ${ROOT_RW} while ${ROOT_RO} is not touched.

# move mount from ${ROOT_RO} to ${rootmnt}${ROOT_RO} 
[ -d ${rootmnt}${ROOT_RO} ] || mkdir -p ${rootmnt}${ROOT_RO}
mount --move ${ROOT_RO} ${rootmnt}${ROOT_RO}
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to move ${ROOT_RO} to ${rootmnt}${ROOT_RO}"
    exit 0
fi

# move mount from ${ROOT_RW} to ${rootmnt}${ROOT_RW} 
[ -d ${rootmnt}${ROOT_RW} ] || mkdir -p ${rootmnt}${ROOT_RW}
mount --move ${ROOT_RW} ${rootmnt}${ROOT_RW}
if [ $? -ne 0 ]; then
    s "${MYTAG}: ERROR: failed to move ${ROOT_RW} to ${rootmnt}${ROOT_RW}"
    exit 0
fi

# technically, everything is set up nicely now. Since ${rootmnt} had beend 
# mounted read-only on the initfamfs already, ${rootmnt}${ROOT_RO} is it, too.
# Now we init process could run - but unfortunately, we may have to prepare 
# some more things here. 
# Basically, there are two ways to deal with the read-only root fs. If the 
# system  is made aware of this, things can be simplified a lot.
# If it is not, things need to be done to our best knowledge. 
#
# So we assume here, the system does not really know about our read-only root fs.
#
# Let's deal with /etc/fstab first. It usually contains an entry for the root 
# fs, which is no longer valid now. We have to remove it and add our new 
# ${ROOT_RO} entry. 
# Remember we are still on the initramfs root fs here, so we have to work on
# ${rootmnt}/etc/fstab. The original fstab is ${rootmnt}${ROOT_RO}/etc/fstab.
ROOT_TYPE=$(cat /proc/mounts | grep ${ROOT} | cut -d' ' -f3)
ROOT_OPTIONS=$(cat /proc/mounts | grep ${ROOT} | cut -d' ' -f4)
cat <<EOF >${rootmnt}/etc/fstab
#
#  This fstab is in RAM, the real one can be found at ${ROOT_RO}/etc/fstab
#  The original entry for '/' and all swap files have been removed.  The new 
#  entry for the read-only the real root fs follows. Write access can be 
#  enabled using:
#    sudo mount -o remount,rw ${ROOT_RO}
#  re-mounting it read-only is done using:
#    sudo mount -o remount,ro ${ROOT_RO}
#

${ROOT} ${ROOT_RO} ${ROOT_TYPE} ${ROOT_OPTIONS} 0 0

#
#  remaining entries from the original ${ROOT_RO}/etc/fstab follow.
#
EOF
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to modify /etc/fstab (step 1)"
    #exit 0
fi

#remove root entry and swap from fstab
cat ${rootmnt}${ROOT_RO}/etc/fstab | grep -v ' / ' | grep -v swap >>${rootmnt}/etc/fstab
if [ $? -ne 0 ]; then
    log_failure_msg "${MYTAG} ERROR: failed to modify etc/fstab (step 2)"
    #exit 0
fi

# now we are done. Additinal steps may be necessary depending on the actualy
# distribution and/or its configuration. 

log_success_msg "${MYTAG} sucessfully set up ro/tmpfs-rw layered root fs using ${ROOT_RO_DRIVER}"

exit 0 

Diese Revision wurde am 26. Mai 2013 23:07 von leonidas666 erstellt.
Die folgenden Schlagworte wurden dem Artikel zugewiesen: Installation, System, Sicherheit