Hi,
da ich meine Schüssel sowie den Multiswitch auf dem Dachboden habe, den Server aber im Keller, experimentiere ich zur Zeit damit, eine Handvoll USB-DVB-S2 Stick im Netzwerkmodus zu betreiben. Ich wollte eine Readonly Lösung, da ich auf dem Dachboden keine USV aufstellen wollte, bei einem Stromausfall könnte Datenverlust entstehen, im schlimmsten fall könnte nach einem kurzem Stromausfall, keine Sendung mehr geschaut oder aufgenommen werden, oder noch schlimmer, man müsste diesen Dachbodenserver neu aufsetzen.
Benötigt wird ein PC der als Server dient mit USB-Schnittstelle der die Sundtek-Sticks aufnehmen kann und ein USB-Stick für ubuntu.
Ich habe mich entschieden, das letzte Ubuntu Release zu nutzen, also 12.04 Server LTS, da dieser gerade frisch ist und LongTermSupport hat.
Fangt also an und ladet euch Ubuntu 12.04 Server runter, achtet dabei auf eure Architektur, ich nutze AMD64, ist für dieses Howto aber uninteressant.
Ich empfehle eine Installation auf einem USB-Stick, dieser sollte min. 2 besser 4 GB groß sein.
Startet die Installation und wählt dabei folgendes im Laufe der Installation an:
- eine Partition, kein Swap, kein LVM, ext2 als Dateisystem,
- keine automatische Aktualisierung,
- nur openssh server installieren,
- nach neustart anmelden, [Bemerkung: alle Befehle müssen als root eingeben werden, ich lasse also nachfolgend das sudo weg, holt euch am besten nach einem Neustart mit sudo bash eine root-shell] dann "sudo bash" eingeben und anschließend ein
-
apt-get update && apt-get dist-upgrade
ausführen,
- dann installieren wir die sundtek Treiber wie auf der
Supportseite beschrieben
cd /tmp
wget http://www.sundtek.de/media/sundtek_netinst.sh
chmod 777 sundtek_netinst.sh
./sundtek_netinst.sh
- unter /etc legen wir eine Datei namens "sundtek.conf" an
vi /etc/sundtek.conf
und fügenfolgende Zeile hinzu.
enablenetwork=on
- wenn ihr irgendwelche Pakete benötigt, installiert diese wie gehabt.
Nun fangen wir an, Ubuntu readonly beizubringen.
- wir verwenden overlayfs und machen dafür die Änderungen laut
UbuntuWiki - Zuerstmal geben wir der Initramfs das Modul mit
echo overlayfs >>/etc/initramfs-tools/modules
- nun fügen wir das root-ro script hinzu und machen es ausführbar
vi /etc/initramfs-tools/scripts/init-bottom/root-ro
#!/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
chmod +x /etc/initramfs-tools/scripts/init-bottom/root-ro
- und generieren einen neuen initramfs
update-initramfs -u
Wir müssen nun eine Kleinigkeit ändern, nachdem Ubuntu gestartet wurde, teilt das System Grub mit, das alles in Ordnung ist, dies passiert, indem es das init.d-Script grub-common das ausführt:
grub-editenv /boot/grub/grubenv unset recordfail
Nun ist unser root-system aber readonly, das würde heißen, es würde zwar das script ausführen, aber es könnte die Änderung nur temporär speichern. Beim nächsten boot, würdet ihr das Grub-menü sehen und ihr müsstet manuell mit enter den Kernel starten. Macht für ein embedded System keinen sinn
, es gibt eine Reihe von Möglichkeiten dies zu umgehen, ich habe mich entschieden, folgende Variante zu nutzen.
editiert die Datei /etc/grub.d/00_header, irgendwo ab zeile 232 seht ihr folgenden Schnipsel
make_timeout ()
{
cat << EOF
if [ "\${recordfail}" = 1 ]; then
set timeout=-1
else
set timeout=${2}
fi
EOF
}
ändert die Zeile mit
set timeout=-1
in bspw
set timeout=2
um, speichern und einmal
update-grub
ausführen. Man könnte auch das script ind /etc/init.d/grub-common anpassen, dazu müsstet Ihr erst /mnt/root-ro schreibbar mounten, die unset recordfail Zeile barbeiten und ausführen lassen und das ganze wieder readonly mounten lassen, aber wenn genau in dem Moment ein stromausfall kommt haben wir nix gewonnen...
Im Prinzip sind wir fertig. Startet Ubuntu neu, meldet euch an und gebt folgendes ein
mount
die Anzeige sollte in etwa wie folgt aussehen
overlayfs-root on / type overlayfs (rw)
/dev/sdXY on /mnt/root-ro type ext2 (ro,relatime,errors=continue)
Wichtig ist die erste Zeile, Ihr könnte nun wie ihr wolltet auf auf der platte "schreiben" es wäre nur virtuell (daher overlay).
Um nachträglich Änderungen vorzunehmen gibt es zwei mögliche Varianten.
Variante 1
ihr wollt nur mal eben schnell eine Datei editieren, oder ein paar daten kopieren.
mount -o remount,rw /mnt/root-ro
Nun könnt Ihr Dateien editieren, kopieren etc....
Seid ihr fertig müsste Ihr das ganze wieder readonly mounten
mount -o remount,ro /mnt/root-ro
Variante 2
Da wir nun ein neues Ubuntu mit LTS unser eigene nennen könnte es ja sein, das wir auch mal updates oder gleich ganze Pakete einspielen wollen. Es empfiehlt sich daher folgende Vorgehensweise.
mount -o remount,rw /mnt/root-ro
touch /mnt/root-ro/disable-root-ro
mount -o remount,ro /mnt/root-ro
Wir haben also das Dateisystem schreibend gemountet und eine leere Datei namens disabled-root-ro im Wurzelverzeichnis erstellt. Nach einem Neustart, wird Ubuntu ganz normal gestartet, wir können Pakete (de)installieren, wie sonst auch.
Seit Ihr fertig mit den Änderungen, löscht einfach die Datei disable-root-ro
rm /disable-root-ro
und startet neu. Nun haben wir wieder eine geschützte ReadOnly Umgebung, ein Stromausfall oder ein hartes ausschalten kann dem System nichts mehr anhaben.
Das ganze belegt ca 1.1GB, was zwar happig ist, aber bei den heutigen Preisen für ein USB-Stick zu vernachlässigen.
Ich hoffe dieses Howto ist für den ein- oder anderen eine Hilfe.
Mfg
KRis