btrfs auf LUKS mit systemd

Systemd scheitert momentan noch am Erkennen der Abhängigkeiten von multi-volume btrfs Dateisystemen auf LUKS-Basis.

Mein aus vier LUKS-Volumes (btrfs-RAID1) bestehendes Archiv wird in der /etc/fstab so eingebunden:

/dev/mapper/archive1	/mnt/archive	btrfs	device=/dev/mapper/archive1,device=/dev/mapper/archive2,device=/dev/mapper/archive3,device=/dev/mapper/archive4,defaults,noatime,nodiratime	0 0

systemd erkennt dabei allerdings nur die Abhängigkeit zu /dev/mapper/archive1, und versucht es folgerichtig schon nach dessen Auftauchen einzubinden, was oft fehlschlägt weil die restlichen Volumes noch nicht angelegt sind.

Bis zu einem Upstream-Fix hilft ein manuelles .mount-File für den entsprechenden fstab-Eintrag, also in meinem Fall /etc/systemd/system/mnt-archive.mount:

[Unit]
Description=/mnt/archive
Wants=cryptsetup.target
After=cryptsetup.target
 
[Mount]
What=/dev/mapper/archive1
Where=/mnt/archive
Type=btrfs
Options=defaults,noatime,nodiratime

(Der Dateiname ist hier wichtig, da er das automatisch aus der fstab generierte .mount-File überlagern muss!)

EDIT: Ab munin-node-2.0.4-1 ändert sich u.A. der Pfad zum Binary, daher muss das .service-File angepasst werden:

[Unit]
Description=Munin Node Service
After=syslog.target network.target
 
[Service]
Type=forking
PIDFile=/run/munin/munin-node.pid
ExecStart=/usr/bin/munin-node
 
[Install]
WantedBy=multi-user.target

munin-node mit systemd

Für den Betrieb von munin-node unter systemd sind die folgende Service-Unit und ein Eintrag in /etc/tmpfiles.d nötig:

/etc/systemd/system/munin-node.service:

[Unit]
Description=Munin Node Service
After=syslog.target network.target
 
[Service]
Type=forking
PIDFile=/var/run/munin/munin-node.pid
ExecStart=/usr/sbin/munin-node
 
[Install]
WantedBy=multi-user.target

/etc/tmpfiles.d/munin-node.conf:

D /var/run/munin 0755 munin munin -

Eigenes Arch Repo

Unter repo.tuxed.org hoste ich seit kurzem ein kleines Arch-Repo mit AUR-Paketen, die ich auf diversen Maschinen verwende und nicht immer neu bauen möchte. Wer es einbinden möchte ist herzlich willkommen:

[tuxed.org]
Server = http://repo.tuxed.org/arch/x86_64

Derzeit sind es nur 64bit-Pakete, vielleicht erweitere ich bei Gelegenheit mal. Alte Versionen der Pakete sortiere ich nur von Zeit zu Zeit manuell aus, was evtl. für Downgrades interessant sein könnte.

Bis auf kernel26-ck sind alle Pakete unmodifiziert aus dem AUR gebaut; bei kernel26-ck habe ich die Prozessoroptimierung auf “Core 2 / Xeon or newer” und den Timer auf 1000Hz gestellt.

Benutzung auf eigene Gefahr!

iptables init-script für Arch

Ja, das vom Paket iptables mitgebrachte init-script leistet wunderbare Dienste. Nein, es besteht kein akuter Grund, eine Alternative zu schreiben…
Einen Nachteil hat es schon: die Konfiguration wird in einem nicht kommentierbaren Format hinterlegt, was bei mir in letzter Zeit ständig zur Frage “WTF hat doch gleich dieser Port da verloren? Was lässt die rule da nochmal durch?” geführt hat.

<bash-addicted>Yep, eindeutige Notwenigkeit für ein selbst gehämmertes, kommentierbares  Script!</bash-addicted> Wie in guten alten Debian-Zeiten übernimmt jetzt also bei mir ein einfaches, aber für den Job völlig ausreichendes init-script den Job. Wer es verwenden mag:

#!/bin/bash
 
#
#   easy-to-edit iptables init script for Arch
#           2010 by Alexander Koch
#
 
 
# TCP services: ssh
SERVICES_TCP=( 22 )
# UDP services:
SERVICES_UDP=( )
 
. /etc/rc.conf
. /etc/rc.d/functions
 
if [ -e "/etc/conf.d/iptables" ]; then
    . /etc/conf.d/iptables
fi
if [ -z "$IPTABLES" ]; then
    IPTABLES="/usr/sbin/iptables"
fi
if ! [ -x "$IPTABLES" ]; then
    echo "unable to execute iptables binary: $IPTABLES"
    exit 1
fi
 
function reset_tables() {
    ERR=0
    $IPTABLES -F || ERR=1
    $IPTABLES -X || ERR=1
    $IPTABLES -P INPUT ACCEPT || ERR=1
    $IPTABLES -P OUTPUT ACCEPT || ERR=1
    if [ $IPTABLES_FORWARD -eq 1 ]; then
        $IPTABLES -P FORWARD ACCEPT || ERR=1
        echo 1 >/proc/sys/net/ipv4/ip_forward || ERR=1
    else
        $IPTABLES -P FORWARD DROP || ERR=1
        echo 0 >/proc/sys/net/ipv4/ip_forward || ERR=1
    fi
 
    return $ERR
}
 
function setup_tables() {
    ERR=0
 
    # setup prevention chain against common attacks
    $IPTABLES -N preventions || ERR=1
    $IPTABLES -A preventions -f -j DROP || ERR=1                           # frags
    $IPTABLES -A preventions -p tcp --tcp-flags ALL ALL -j DROP || ERR=1   # XMAS
    $IPTABLES -A preventions -p tcp --tcp-flags ALL NONE -j DROP || ERR=1  # null
 
    # setup services chain
    $IPTABLES -N services || ERR=1
    for PORT in ${SERVICES_TCP[@]}; do
        $IPTABLES -A services -p tcp --dport $PORT -j ACCEPT || ERR=1
    done
    for PORT in ${SERVICES_UDP[@]}; do
        $IPTABLES -A services -p udp --dport $PORT -j ACCEPT || ERR=1
    done
 
    # allow incoming ping requests
    iptables -A services -p icmp --icmp-type echo-request -j ACCEPT || ERR=1
 
    # setup main chains
    $IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT || ERR=1
    $IPTABLES -A INPUT -i lo -j ACCEPT || ERR=1
    $IPTABLES -A INPUT -j preventions || ERR=1
    $IPTABLES -A INPUT -m state --state NEW -j services || ERR=1
    $IPTABLES -A INPUT -p tcp -j REJECT --reject-with tcp-reset || ERR=1
    $IPTABLES -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable || ERR=1
    $IPTABLES -P INPUT DROP || ERR=1
 
    return $ERR
}
 
case "$1" in
    start)
        stat_busy "Loading IP Tables"
        reset_tables
        if ! setup_tables; then
            stat_fail
            exit 1
        else
            add_daemon miptables
            stat_done
        fi
        ;;
    stop)
        stat_busy "Flushing IP Tables"
        if reset_tables; then
            rm_daemon miptables
            stat_done
        else
            stat_fail
            exit 1
        fi
        ;;
    restart)
        if ! ck_daemon miptables; then
            rm_daemon miptables
        fi
        $0 start
        exit $?
        ;;
    *)
        echo "usage: $0 {start|stop|restart}"
        ;;
esac
 
exit 0

Gefiltert werden nur eingehende Pakete; related und established Pakete werden ebenfalls ohne weitere Beachtung durchgelassen, ebenso alles über loopback. Für neue Pakete werden zuerst ein paar Vulnerability-Checks durchgeführt. Nach Außen wird ein Verhalten wie ohne iptables emuliert (z.B. REJECTs mit tcp-reset).

Bugreports wie immer willkommen, und Benutzung auf eigene Gefahr!

Update 31.07.10: Bugfixed :)

Arch rc.sysinit: LUKS parallel

Im Arch Bootscript /etc/rc.sysinit werden u.A. die per /etc/crypttab definierten verschlüsselten Volumes geöffnet und gemountet – sequentiell. Da ein luksOpen-Aufruf generell schon recht lange dauert, kommen bei mehreren solcher Volumes schnell einige Sekunden zusammen.

Auf einer Kiste mit fünf LUKS-Volumes kam mir die Idee, die entsprechende Funktion mal per “&” zu forken und das Ganze so ein wenig zu parallelisieren.

In wie fern das jetzt tatsächlich im messbaren Bereich liegt, sei mal dahingestellt. Ich bilde mir jedenfalls erfolgreich einen Geschwindigkeits-zuwachs ein, und da der Patch meines Erachtens recht unkritisch ist, bleibe ich dabei :)

Wer es ausprobieren mag:

--- /etc/rc.sysinit.backup      2010-01-24 15:35:12.000000000 +0100
+++ /etc/rc.sysinit             2010-05-04 18:57:53.380890577 +0200
@@ -149,7 +149,7 @@
                        cpass="$3"
                        shift 3
                        copts="$*"
-                       stat_append "${cname}.."
+                       #stat_append "${cname}.."
                        # For some fun reason, the parameter ordering varies for
                        # LUKS and non-LUKS devices.  Joy.
                        if [ "${cpass}" = "SWAP" ]; then
@@ -188,15 +188,16 @@
                        fi
                        if [ $? -ne 0 ]; then
                                csfailed=1
-                               stat_append "failed "
+                               stat_append "${cname} failed "
                        else
-                               stat_append "ok "
+                               stat_append "${cname} ok "
                        fi
                fi
        }
        while read line; do
-               eval do_crypt "$line"
+               eval do_crypt "$line" &
        done </etc/crypttab
+       wait
        if [ $csfailed -eq 0 ]; then
                stat_done
        else

Arch ramdisk-script 1.4

In meinem Arch Ramdisk-Script <1.4 hatte sich ein dämlicher Bug eingeschlichen: bei sämtlichen rsync-Aufrufen fehlte das --delete.

Das bewirkt z.B. bei Verwendung mit Firefox, dass der Cache nie geleert wird und (streng) monoton wächst – unschön.

Hier die gefixte Version :)

#!/bin/sh
 
#
# Manages outsourcing of specified directories into memory on bootup and
# takes care of synchronization/backup on system shutdown.
#
# Version 1.4, 2010-04-26, by Alexander Koch
#
 
# includes
 
. /etc/rc.conf
. /etc/rc.d/functions
 
 
# configuration (syntax is: [persist. storage]:[mountpoint]:[mount options])
 
DISKS=('/home/alex/.ramdisks/_mozilla:/home/alex/.mozilla:size=100M,uid=1000,gid=100' \
	   'empty:/home/alex/.adobe:size=10M,uid=1000,gid=100' \
	   'empty:/home/alex/.macromedia:size=10M,uid=1000,gid=100')
 
 
# helper functions
 
function activate_rd() {
	[ -d "$1" ] || [ "$1" = "empty" ] || return 1
	[ -d "$2" ] || return 1
	mount | grep "$2" &>/dev/null && return 1
	MNT="mount -t tmpfs"
	[ -z "$3" ] || MNT="$MNT -o $3"
	$MNT none "$2"
	[ $? -gt 0 ] && return 1
	if [ "$1" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				umount "$2"
				return 1
			fi
		done
	fi
	return 0
}
 
function backup_rd() {
	mount | grep "$1" &>/dev/null || return 0
	if [ "$2" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq --delete "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				tar -cf "/root/$(basename "$2")-failed.tar" "$1"
				return 1
			fi
		done
	fi
	umount "$1" || return 1
	return 0
}
 
 
# main logic
 
case $1 in
	start)
		stat_busy "Mounting ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 1)"
			TO="$(echo "$M" | cut -d ':' -f 2)"
			OPTS="$(echo "$M" | cut -d ':' -f 3)"
			activate_rd "$FROM" "$TO" "$OPTS" || error=1
		done
		if [ $error -eq 0 ]; then
			add_daemon ramdisks
			stat_done
		else
			stat_fail
			exit 1
		fi
		;;
	stop)
		stat_busy "Saving ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 2)"
			TO="$(echo "$M" | cut -d ':' -f 1)"
			backup_rd "$FROM" "$TO" || error=1
		done
		if [ $error -eq 0 ]; then
			rm_daemon ramdisks
			stat_done
		else
			stat_fail
			echo -n "WARNING: failed to save ramdisk(s), tried to make "
			echo "backup(s) under /root."
			echo "Hit enter to proceed shutdown."
			read DUMMY
			exit 1
		fi
		;;
	restart)
		if ! ck_daemon ramdisks; then
			"$0" stop && sleep 3
		fi
		"$0" start
		;;
	*)
		echo "usage: $0 {start|stop|restart}"
		;;
esac
 
exit 0

Arch ramdisk-script 1.3

Mein Arch ramdisk-script hat ein weiteres Update erfahren, die Konfiguration ist nun mehr nach KISS und dot-Ordner werden korrekt behandelt.

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
#!/bin/sh
 
#
# Manages outsourcing of specified directories into memory on bootup and
# takes care of synchronization/backup on system shutdown.
#
# Version 1.3, 2010-03-15, by Alexander Koch
#
 
# includes
 
. /etc/rc.conf
. /etc/rc.d/functions
 
 
# configuration (syntax is: [persist. storage]:[mountpoint]:[mount options])
 
DISKS=('/home/alex/.ramdisks/_mozilla:/home/alex/.mozilla:size=100M,uid=1000,gid=100' \
	   'empty:/home/alex/.adobe:size=10M,uid=1000,gid=100' \
	   'empty:/home/alex/.macromedia:size=10M,uid=1000,gid=100')
 
 
# helper functions
 
function activate_rd() {
	[ -d "$1" ] || [ "$1" = "empty" ] || return 1
	[ -d "$2" ] || return 1
	mount | grep "$2" &>/dev/null && return 1
	MNT="mount -t tmpfs"
	[ -z "$3" ] || MNT="$MNT -o $3"
	$MNT none "$2"
	[ $? -gt 0 ] && return 1
	if [ "$1" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				umount "$2"
				return 1
			fi
		done
	fi
	return 0
}
 
function backup_rd() {
	mount | grep "$1" &>/dev/null || return 0
	if [ "$2" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				tar -cf "/root/$(basename "$2")-failed.tar" "$1"
				return 1
			fi
		done
	fi
	umount "$1" || return 1
	return 0
}
 
 
# main logic
 
case $1 in
	start)
		stat_busy "Mounting ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 1)"
			TO="$(echo "$M" | cut -d ':' -f 2)"
			OPTS="$(echo "$M" | cut -d ':' -f 3)"
			activate_rd "$FROM" "$TO" "$OPTS" || error=1
		done
		if [ $error -eq 0 ]; then
			add_daemon ramdisks
			stat_done
		else
			stat_fail
			exit 1
		fi
		;;
	stop)
		stat_busy "Saving ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 2)"
			TO="$(echo "$M" | cut -d ':' -f 1)"
			backup_rd "$FROM" "$TO" || error=1
		done
		if [ $error -eq 0 ]; then
			rm_daemon ramdisks
			stat_done
		else
			stat_fail
			echo -n "WARNING: failed to save ramdisk(s), tried to make "
			echo "backup(s) under /root."
			echo "Hit enter to proceed shutdown."
			read DUMMY
			exit 1
		fi
		;;
	restart)
		if ! ck_daemon ramdisks; then
			"$0" stop && sleep 3
		fi
		"$0" start
		;;
	*)
		echo "usage: $0 {start|stop|restart}"
		;;
esac
 
exit 0

Ramdisk-Script

Dem neusten Trend, alles mögliche in Ramdisks zu packen, folgend habe ich auf Anregung eines Freundes (Danke, Stefan! ;) ) mein Firefox-Profil per tmpfs ausgelagert und war wie er vom Performancegewinn in Firefox beeindruckt.

Um das Ganze dann noch etwas besser verwalten und bei Bedarf unkompliziert auch auf andere Verzeichnisse anwenden zu können habe ich (mal wieder) ein kleines bash-Skript für Arch erstellt, was sich um Ein-/Aushängen kümmert (/etc/rc.d/ramdisks):

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
#!/bin/sh
 
#
# Manages outsourcing of specified directories into memory on bootup and
# takes care of synchronization/backup on system shutdown.
#
 
. /etc/rc.conf
. /etc/rc.d/functions
 
 
# helper functions
 
function activate_rd() {
	[ -d "$1" ] || [ "$1" = "empty" ] || return 1
	[ -d "$2" ] || return 1
	mount | grep "$2" &>/dev/null && return 1
	mount -t tmpfs -o noauto,size=$3,uid=$4,gid=$5 none "$2"
	[ $? -gt 0 ] && return 1
	if [ "$1" != "empty" ]; then
		rsync -axq "$1" "$(dirname "$2")" &>/dev/null
		if [ $? -gt 0 ]; then
			umount "$2"
			return 1
		fi
	fi
	return 0
}
 
function flush_rd() {
	mount | grep "$1" &>/dev/null || return 1
	if [ -n "$2" ]; then
		rsync -axq --delete-after "$1" "$2" &>/dev/null
		if [ $? -gt 0 ]; then
			tar -cf "/root/$2-failed.tar" "$1"
			return 1
		fi
	fi
	umount "$1" || return 1
	return 0
}
 
 
# main logic
 
case $1 in
	start)
		stat_busy "Mounting ramdisks"
		error=0
		# list of directories to put into memory
		activate_rd /home/alex/.ramdisks/firefox/3tync6w9.default \
		            /home/alex/.mozilla/firefox/3tync6w9.default \
		            100M 1000 100 || error=1
		activate_rd empty /home/alex/.adobe 10M 1000 100 || error=1
		activate_rd empty /home/alex/.macromedia 10M 1000 100 || error=1
		# list may be continued here, remember arguments
		if [ $error -eq 0 ]; then
			add_daemon ramdisks
			stat_done
		else
			stat_fail
			exit 1
		fi
		;;
	stop)
		stat_busy "Saving ramdisks"
		error=0
		# list as above
		flush_rd /home/alex/.mozilla/firefox/3tync6w9.default \
		         /home/alex/.ramdisks/firefox || error=1
		flush_rd /home/alex/.adobe || error=1
		flush_rd /home/alex/.macromedia || error=1
		# keep in sync with the above one
		if [ $error -eq 0 ]; then
			rm_daemon ramdisks
			stat_done
		else
			stat_fail
			echo -n "WARNING: failed to save ramdisk(s), tried to make "
			echo "backup(s) under /root."
			echo "Hit enter to proceed shutdown."
			read DUMMY
			exit 1
		fi
		;;
	restart)
		if ! ck_daemon ramdisks; then
			"$0" stop && sleep 3
		fi
		"$0" start
		;;
	*)
		echo "usage: $0 {start|stop|restart}"
		;;
esac
 
exit 0

Update 25.11.: Erweiterte Version, unterstützt nun auch nicht zu sichernde Verzeichnisse, eingetragen am Beispiel meiner Flash-Caches.
Update 15.03.: Weiteres Update, siehe Post.

Pacman auf Speed: pacman-cage

Die Idee, die Pacman-Datenbank auf eine eigene kleine (evtl. virtuelle) Partition zu legen, liegt nicht sonderlich fern. Dass das aber mit einer simplen Lösung aus ext2 und einem Loopback-Volume einen derartigen Speedup bewirkt, hätte zumindest ich nicht gedacht:

$ time pacman -Ss kernel >/dev/null
real    0m0.019s
user    0m0.003s
sys    0m0.010s

Damit wäre dann der einzige Nachteil, den ich an Pacman gegenüber apt und Co. gesehen hätte, Geschichte :)
Wer Interesse daran hat, es auszuprobieren: pacman-cage.sh (setup script, leicht modifiziert übernommen aus diesem Thread). Zur Einbindung der Datenbank beim Systemstart, sowie fsck und Backup dient Folgendes in der /etc/rc.local:

. /etc/rc.conf
. /etc/rc.d/functions
 
# check and mount pacman db
stat_busy "Mounting pacman database"
/sbin/e2fsck -p /var/lib/pacman-X.db >/dev/null
if [ $? -gt 1 ]; then
    stat_fail
    echo
    echo "WARNING: PACMAN DB FILESYSTEM CHECK FAILED, NOT MOUNTED"
    echo
else
    mount /var/lib/pacman
    if [ $? -gt 0 ]; then
        stat_fail
        echo
        echo "WARNING: MOUNTING PACMAN DATABASE FAILED"
        echo
    else
        stat_done
        stat_busy "Backing up pacman database"
        rsync -Caxq --delete-after --exclude "lost+found/" /var/lib/pacman/local  /var/lib/pacman.bak/
        if [ $? -gt 0 ]; then
            stat_fail
        else
            stat_done
        fi
    fi
fi

Außerdem muss die Partition noch in die /etc/fstab eingetragen werden:

/var/lib/pacman-X.db   /var/lib/pacman   ext2   defaults,loop,noauto,noatime   0 0

mpd –no-ffmpeg

Eine weiteres abgespecktes Paket für meine Set-top-box: mpd. Selbiges zieht nämlich über ffmpeg als dependency libsdl und damit mehrere X11-Pakete mit, was mir auf einer Box ohne X gegen den Strich ging.

Bei der Gelegenheit flogen auch gleich IPv6, MMS und OSS raus – Geschmackssache. Wer es mag: PKGBUILD