はじめに
図書館やインターネットカフェなどの場所では、公共に開放されたコンピュータをよく見かけます。これらのコンピュータには一連のプリインストールされたアプリケーションが用意されており、ユーザーデータは再起動後に消去されます。
そのようなマシン上のソフトウェアはしばしばプロプライエタリ(専有)です。しかし、公共のコンピュータ向けにソフトウェアの自由を尊重する解決策も存在します。フリーソフトウェアの活動家として、図書館、インターネットカフェ、学校などでこれらの解決策を積極的に導入・周知すべきです。
本稿では、Debian GNU/Linux を稼働させたコンピュータを再起動時にすべてのデータが消去されるように設定する方法を示します。
ライセンス
本記事のコードはすべてパブリックドメインに献呈します。記事のその他の部分は CC BY-SA 4.0 の下にあります。
準備
少なくとも 50 GiB のハードディスク(可能なら HDD)を搭載したコンピュータと、Debian GNU/Linux のインストールメディア(Debian Live ではないもの)を用意してください。
手順 1:Debian GNU/Linux のインストール
次のパーティション構成で Debian GNU/Linux をインストールしてください:
- 1 GiB の
/dev/sda1(FAT ファイルシステム)、/boot/efiにマウント - 1 GiB の
/dev/sda2(ext4 ファイルシステム)、/bootにマウント - 少なくとも 40 GiB の
/dev/sda3(ext4 ファイルシステム)、/にマウント /dev/sda4はファイルシステム無し/dev/sda5をスワップ領域として使用
インストール後、必要なソフトウェアを導入し、システムを希望どおりに設定してください。強力な root パスワード(および強力な BIOS/UEFI パスワード)を設定することを忘れないでください。root アカウントを公共に開放するのは良い考えではありません。
手順 2:設定
/dev/sda4 を ext4 でフォーマットし、ラベルを OVERLAY_RW にします:
# mkfs.ext4 -L OVERLAY_RW /dev/sda4
次に /dev/sda3 に ROOT_TEMPLATE というラベルを付けます:
# e2label /dev/sda3 ROOT_TEMPLATE
ディレクトリを作成します:
# 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
/etc/fstab のルートファイルシステムをマウントする行をコメントアウトしてから、以下の行を追加します:
LABEL=OVERLAY_RW /overlay_storage ext4 defaults,noatime 0 2
パッケージキャッシュを更新し、以下のパッケージをインストールします(これにより busybox が削除される可能性があります。注意して実行してください):
# apt update
# apt install --no-install-recommends initramfs-tools busybox-static
/etc/initramfs-tools/modules に次の行を追加します:
overlay
ext4
jbd2
crc32c
mbcache
/etc/tmpfiles.d/overlay-runtime.conf を作成し、次の内容を追加します:
# /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 -
/etc/initramfs-tools/scripts/init-bottom/overlayroot を作成し、以下のスクリプトを配置してください(コードブロックは原文のままです):
#!/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
/etc/initramfs-tools/scripts/init-bottom/overlayroot を実行可能にします:
# chmod +x /etc/initramfs-tools/scripts/init-bottom/overlayroot
initramfs を更新します:
# update-initramfs -u -k all
/usr/local/sbin/overlay-prune.sh を作成し、以下の内容を追加します(コードは原文のまま):
#!/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
/usr/local/sbin/overlay-prune.sh を実行可能にします:
# chmod +x /usr/local/sbin/overlay-prune.sh
/etc/logrotate.d/overlay-prune を作成し、次の内容を入れます:
/var/log/overlay-prune.log {
rotate 7
daily
missingok
notifempty
compress
copytruncate
}
ログファイルを作成し、所有権と権限を設定します:
# touch /var/log/overlay-prune.log
# chown root:root /var/log/overlay-prune.log
# chmod 0640 /var/log/overlay-prune.log
/etc/systemd/system/overlay-prune.service を作成し、次の内容を入れます:
[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)
/etc/systemd/system/overlay-prune.timer を作成し、次の内容を入れます:
[Unit]
Description=Run overlay-prune daily (and shortly after boot)
[Timer]
OnBootSec=10min
OnUnitActiveSec=24h
Persistent=true
[Install]
WantedBy=timers.target
タイマーを有効にします:
# systemctl enable overlay-prune.timer
システムを再起動してください:
# reboot
手順 3:インストールの確認
再起動後、次を実行します:
$ findmnt /
/ が overlay ファイルシステムであることが確認できるはずです。
いくつかのファイルを適当な場所に作成し、システムを再起動してください。それらのファイルは消えているはずです。
すべて問題なければ、作業は完了です。