Einführung
An Orten wie Bibliotheken oder Internetcafés findet man häufig Computer, die der Öffentlichkeit zur Verfügung stehen. Diese Computer werden mit einer Anzahl vorinstallierter Anwendungen ausgeliefert, und die Benutzerdaten werden nach einem Neustart gelöscht.
Die Software auf solchen Geräten ist oft proprietär. Es gibt jedoch auch freiheitsorientierte Lösungen für öffentliche Computer. Als Aktivistinnen und Aktivisten für freie Software sollten wir diese Lösungen aktiv in Bibliotheken, Internetcafés, Schulen usw. anwenden und fördern.
In diesem Artikel zeige ich eine Lösung, mit der ein Computer unter Debian GNU/Linux so eingerichtet wird, dass nach einem Neustart alle Daten gelöscht werden.
Lizenz
Jeglicher Code in diesem Artikel steht dem öffentlichen Bereich zur Verfügung. Andere Teile dieses Artikels stehen unter CC BY-SA 4.0.
Vorbereitung
Ein Computer mit einer Festplatte (vorzugsweise einer HDD) von mindestens 50 GiB und ein Debian-GNU/Linux-Installationsmedium (nicht Debian Live).
Schritt 1: Debian GNU/Linux installieren
Installieren Sie Debian GNU/Linux auf Ihrem Rechner mit folgender Partitionsaufteilung:
- 1 GiB
/dev/sda1mit einem FAT-Dateisystem, eingehängt bei/boot/efi - 1 GiB
/dev/sda2mit einem ext4-Dateisystem, eingehängt bei/boot - Mindestens 40 GiB für
/dev/sda3mit einem ext4-Dateisystem, eingehängt bei/ /dev/sda4ohne Dateisystem/dev/sda5als Swap-Partition
Nach der Installation installieren Sie die gewünschten Programme und konfigurieren das System nach Ihren Wünschen. Denken Sie daran, ein starkes Root-Passwort zu setzen (und ein starkes BIOS/UEFI-Passwort); es ist keine gute Idee, das Root-Konto der Öffentlichkeit zugänglich zu lassen.
Schritt 2: Konfiguration
Formatieren Sie /dev/sda4 mit einem ext4-Dateisystem und labeln Sie es OVERLAY_RW:
# mkfs.ext4 -L OVERLAY_RW /dev/sda4
Labeln Sie anschließend /dev/sda3 als ROOT_TEMPLATE:
# e2label /dev/sda3 ROOT_TEMPLATE
Erstellen Sie Verzeichnisse:
# mkdir -p /mnt/overlay_prepare
# mount /dev/disk/by-label/OVERLAY_RW /mnt/overlay_prepare
# mkdir -p /mnt/overlay_prepare/upper-root
# mkdir -p /mnt/overlay_prepare/work-root
# chmod 0700 /mnt/overlay_prepare
# sync
# umount /mnt/overlay_prepare
Kommentieren Sie die Zeile aus, die Ihr Root-Dateisystem in /etc/fstab einhängt, und fügen Sie dann diese Zeile hinzu:
LABEL=OVERLAY_RW /overlay_storage ext4 defaults,noatime 0 2
Aktualisieren Sie den Paketcache und installieren Sie diese Pakete (das kann busybox entfernen; gehen Sie mit Vorsicht vor):
# apt update
# apt install --no-install-recommends initramfs-tools busybox-static
Fügen Sie diese Zeilen in /etc/initramfs-tools/modules ein:
overlay
ext4
jbd2
crc32c
mbcache
Erstellen Sie /etc/tmpfiles.d/overlay-runtime.conf mit folgendem Inhalt:
# /etc/tmpfiles.d/overlay-runtime.conf
d /run/dbus 0755 messagebus messagebus -
d /var/lib/dbus 0755 messagebus messagebus -
d /run/NetworkManager 0755 root root -
d /run/lock 0755 root root -
d /var/log 0755 root root -
d /tmp 1777 root root -
d /var/tmp 1777 root root -
Erstellen Sie /etc/initramfs-tools/scripts/init-bottom/overlayroot und fügen Sie folgendes Skript hinzu:
#!/bin/sh
PREREQ=""
prereqs() { echo "$PREREQ"; }
case "$1" in
prereqs) prereqs; exit 0;;
esac
set -eu
# If admin wants maintenance: skip overlay and let normal boot continue
if grep -q 'overlay=disabled' /proc/cmdline 2>/dev/null; then
echo "overlay disabled via kernel cmdline" >/dev/console 2>&1
exit 0
fi
# Try to load overlay module (best-effort)
modprobe overlay 2>/dev/null || true
# prepare mount points in initramfs
mkdir -p /overlay/lower /overlay/storage /overlay/overlay_root
# resolve device by label for the template root
DEV_LABEL=/dev/disk/by-label/ROOT_TEMPLATE
DEV_ROOT=$(readlink -f "$DEV_LABEL" 2>/dev/null || true)
if [ -z "$DEV_ROOT" ]; then
echo "overlayroot: ERROR: ROOT_TEMPLATE label not found" >/dev/console 2>&1
exit 1
fi
echo "overlayroot: resolved ROOT_TEMPLATE -> $DEV_ROOT" >/dev/console 2>&1
# find existing mount point for the device; fallback to using current root (/)
MOUNT_POINT=$(awk -v d="$DEV_ROOT" '($1==d){print $2; exit}' /proc/self/mounts || true)
if [ -z "$MOUNT_POINT" ]; then
B=$(basename "$DEV_ROOT" || true)
if [ -r "/sys/class/block/$B/dev" ]; then
DEVNUM=$(cat /sys/class/block/$B/dev)
MOUNT_POINT=$(awk -v num="$DEVNUM" '($3==num){print $5; exit}' /proc/self/mountinfo || true)
fi
fi
if [ -z "$MOUNT_POINT" ]; then
ROOT_SRC=$(awk '($2=="/"){print $1; exit}' /proc/self/mounts || true)
if [ -n "$ROOT_SRC" ]; then
echo "overlayroot: fall back to using current root mount ($ROOT_SRC) as lowerdir" >/dev/console 2>&1
mount --bind / /overlay/lower 2>/dev/null || true
MOUNT_POINT=/overlay/lower
fi
fi
if [ -n "$MOUNT_POINT" ] && [ "$MOUNT_POINT" != "/overlay/lower" ]; then
echo "overlayroot: bind existing mountpoint $MOUNT_POINT -> /overlay/lower" >/dev/console 2>&1
mount --bind "$MOUNT_POINT" /overlay/lower || true
fi
# if /overlay/lower still not a mountpoint, try direct ro mount (last resort)
if ! grep -q ' /overlay/lower ' /proc/self/mounts; then
echo "overlayroot: trying direct mount of $DEV_ROOT -> /overlay/lower" >/dev/console 2>&1
mount -o ro "$DEV_ROOT" /overlay/lower || {
echo "overlayroot: direct mount of $DEV_ROOT failed" >/dev/console 2>&1
exit 1
}
fi
# mount overlay storage partition (explicit ext4)
DEV_RW=$(readlink -f /dev/disk/by-label/OVERLAY_RW 2>/dev/null || true)
if [ -z "$DEV_RW" ]; then
echo "overlayroot: ERROR: OVERLAY_RW label not found" >/dev/console 2>&1
exit 1
fi
echo "overlayroot: mounting OVERLAY_RW ($DEV_RW) -> /overlay/storage" >/dev/console 2>&1
mount -t ext4 "$DEV_RW" /overlay/storage || {
echo "overlayroot: failed to mount OVERLAY_RW ($DEV_RW)" >/dev/console 2>&1
exit 1
}
# per-boot upper/work on overlay storage
BOOTID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || date +%s)
UPPER="/overlay/storage/upper-${BOOTID}"
WORK="/overlay/storage/work-${BOOTID}"
mkdir -p "$UPPER" "$WORK"
# make sure upper/work are owned by root and inaccessible to others
chown -R root:root "$UPPER" "$WORK" 2>/dev/null || true
chmod 0700 "$UPPER" "$WORK" 2>/dev/null || true
# mount overlay onto the location that initramfs-tools expects for the real root (/root)
mkdir -p /root
echo "overlayroot: mounting overlay lower=/overlay/lower upper=$UPPER work=$WORK -> /root" >/dev/console 2>&1
mount -t overlay overlay -o lowerdir=/overlay/lower,upperdir="$UPPER",workdir="$WORK" /root || {
echo "overlayroot: overlay mount failed" >/dev/console 2>&1
exit 1
}
# move overlay storage under the real root so the booted system can access/clean it later
mkdir -p /root/overlay_storage
mount --move /overlay/storage /root/overlay_storage 2>/dev/null || mount --bind /overlay/storage /root/overlay_storage || true
# --- Post-mount safety & runtime skeleton preparation ---
# Ensure the merged root is traversable by non-root users to allow services to chdir/exec
chmod 0755 /root 2>/dev/null || true
# Create essential runtime and state directories inside the merged root.
# These make sure systemd and daemons (dbus, NetworkManager, etc.) can create sockets and pidfiles.
mkdir -p /root/run /root/run/dbus /root/run/NetworkManager /root/run/lock
mkdir -p /root/var/lib/dbus /root/var/log /root/var/tmp /root/tmp
# Set permissive permissions for tmp directories and standard perms for others
chmod 0755 /root/run /root/var /root/var/log 2>/dev/null || true
chmod 1777 /root/tmp /root/var/tmp 2>/dev/null || true
# Attempt to set dbus ownership for dbus runtime dirs; ignore errors if the user does not exist in initramfs.
chown -R messagebus:messagebus /root/run/dbus /root/var/lib/dbus 2>/dev/null || true
# Ensure root owns the primary runtime dirs
chown root:root /root /root/run /root/var 2>/dev/null || true
# Ensure upper/work are secure on the merged root as well (in case overlay moved them)
# (this is best-effort; ignore failures)
[ -d "$UPPER" ] && chown -R root:root "$UPPER" 2>/dev/null || true
[ -d "$WORK" ] && chown -R root:root "$WORK" 2>/dev/null || true
chmod 0700 "$UPPER" "$WORK" 2>/dev/null || true
# Move pseudo-filesystems into the new root so the real init finds them after switch_root.
# These moves are best-effort; if they fail, systemd may still handle necessary mounts.
for P in dev proc sys run; do
if mountpoint -q "/$P" 2>/dev/null; then
mkdir -p /root/$P 2>/dev/null || true
mount --move "/$P" "/root/$P" 2>/dev/null || true
fi
done
# Leave final switch_root to initramfs /init (do not exec switch_root here).
echo "overlayroot: overlay prepared at /root; returning to initramfs /init to perform switch_root" >/dev/console 2>&1
exit 0
Machen Sie /etc/initramfs-tools/scripts/init-bottom/overlayroot ausführbar:
# chmod +x /etc/initramfs-tools/scripts/init-bottom/overlayroot
Aktualisieren Sie Ihr initramfs:
# update-initramfs -u -k all
Erstellen Sie /usr/local/sbin/overlay-prune.sh und fügen Sie diese Zeilen ein:
#!/usr/bin/env bash
# overlay-prune.sh
# Prune unused overlay upper-*/work-* directories safely.
# Usage:
# /usr/local/sbin/overlay-prune.sh [--dry-run] [--age DAYS]
# Default AGE = 7 days
set -euo pipefail
DRY_RUN=0
AGE=7 # days
LOG="/var/log/overlay-prune.log"
while [ $# -gt 0 ]; do
case "$1" in
--dry-run) DRY_RUN=1; shift ;;
--age) AGE="$2"; shift 2 ;;
--help) echo "Usage: $0 [--dry-run] [--age DAYS]"; exit 0 ;;
*) echo "Unknown arg: $1"; exit 2 ;;
esac
done
now() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
log() {
printf '%s %s\n' "$(now)" "$*" >> "$LOG"
}
# Determine overlay storage mountpoint(s).
# Try common locations then fallback to finding mount points for device labelled OVERLAY_RW.
CANDIDATES=(
"/root/overlay_storage"
"/overlay_storage"
"/overlay/merged/overlay_storage"
"/overlay/storage"
)
STORAGE=""
for p in "${CANDIDATES[@]}"; do
if [ -d "$p" ]; then
# choose the first existing one that is on a separate filesystem or contains upper-* dirs
if find "$p" -maxdepth 1 -mindepth 1 -type d -name 'upper-*' -print -quit >/dev/null 2>&1; then
STORAGE="$p"
break
fi
fi
done
# fallback: try findmnt by device label
if [ -z "$STORAGE" ]; then
DEV=$(readlink -f /dev/disk/by-label/OVERLAY_RW 2>/dev/null || true)
if [ -n "$DEV" ]; then
STORAGE=$(findmnt -n -o TARGET -S "$DEV" 2>/dev/null || true)
fi
fi
if [ -z "$STORAGE" ]; then
echo "overlay-prune: no overlay storage found; exiting" >&2
log "No overlay storage found; abort."
exit 0
fi
log "Starting prune on storage: $STORAGE (age > ${AGE}d) DRY_RUN=${DRY_RUN}"
# Collect currently in-use upper/work directories by scanning /proc/mounts overlay options
mapfile -t INUSE < <(awk -F',' '/lowerdir=/{for(i=1;i<=NF;i++){if($i ~ /^upperdir=/) print substr($i,10); if($i ~ /^workdir=/) print substr($i,9)}}' /proc/mounts | sort -u)
# Helper: check if path is referenced in INUSE
is_inuse() {
local p="$1"
for u in "${INUSE[@]}"; do
if [ "$u" = "$p" ]; then
return 0
fi
done
return 1
}
# Find candidate dirs named upper-* or work-*
while IFS= read -r d; do
# normalize
dir="$d"
# guard: only operate under STORAGE
case "$dir" in
"$STORAGE"/*) ;;
*) continue ;;
esac
# skip if currently in use
if is_inuse "$dir"; then
log "SKIP in-use: $dir"
continue
fi
# skip if younger than AGE
if [ "$(find "$dir" -maxdepth 0 -mtime -"$AGE" -print -quit)" ]; then
log "SKIP recent: $dir"
continue
fi
if [ "$DRY_RUN" -eq 1 ]; then
echo "DRY-RUN would remove: $dir"
log "DRY-RUN would remove: $dir"
else
# double-check no mount points below it
if mountpoint -q "$dir"; then
log "SKIP mounted: $dir"
continue
fi
# safe remove
log "REMOVING: $dir"
rm -rf -- "$dir"
if [ $? -eq 0 ]; then
log "REMOVED: $dir"
else
log "FAILED_REMOVE: $dir"
fi
fi
done < <(find "$STORAGE" -maxdepth 1 -type d \( -name 'upper-*' -o -name 'work-*' \) -print | sort)
log "Prune finished."
exit 0
Machen Sie /usr/local/sbin/overlay-prune.sh ausführbar:
# chmod +x /usr/local/sbin/overlay-prune.sh
Erstellen Sie /etc/logrotate.d/overlay-prune mit folgendem Inhalt:
/var/log/overlay-prune.log {
rotate 7
daily
missingok
notifempty
compress
copytruncate
}
Erstellen Sie die Logdatei und setzen Sie Besitzrechte und Berechtigungen:
# touch /var/log/overlay-prune.log
# chown root:root /var/log/overlay-prune.log
# chmod 0640 /var/log/overlay-prune.log
Erstellen Sie /etc/systemd/system/overlay-prune.service mit folgendem Inhalt:
[Unit]
Description=Prune unused overlay upper/work directories
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/overlay-prune.sh --age 7
Nice=10
# Run as root (needs to remove files)
Erstellen Sie /etc/systemd/system/overlay-prune.timer mit folgendem Inhalt:
[Unit]
Description=Run overlay-prune daily (and shortly after boot)
[Timer]
OnBootSec=10min
OnUnitActiveSec=24h
Persistent=true
[Install]
WantedBy=timers.target
Aktivieren Sie den Timer:
# systemctl enable overlay-prune.timer
Starten Sie nun Ihr System neu:
# reboot
Schritt 3: Testen Sie Ihre Installation
Nach dem Neustart führen Sie aus:
$ findmnt /
Sie sollten sehen, dass / ein Overlay-Dateisystem ist.
Erstellen Sie mehrere Dateien an unterschiedlichen Orten und starten Sie dann das System neu; diese Dateien sollten verschwunden sein.
Wenn alles in Ordnung ist, sind Sie fertig.