TrueNAS SCALE: HDD Standby

Seit mitt­ler­wei­le über einem Jahr mit Erschei­nen der Release Can­di­da­tes von True­NAS 25.10 funk­tio­niert das Ver­set­zen von Fest­plat­ten in den Stand­by nicht mehr. Die Ver­sio­nen bis 25.04 hat­ten die­ses Pro­blem nicht. Im True­NAS-Forum fin­det sich ein lan­ger Thread über die­ses Pro­blem und eini­ge Posts waren bei der Lösung des Pro­blems eine gro­ße Hil­fe. Aller­dings sind die vor­ge­schla­ge­nen Lösun­gen für mich nicht ganz zufrie­den­stel­lend, erwar­tet man von einer Appli­ance doch einen mög­lichst gerin­gen Auf­wand bei Betrieb und Wartung.

Die Patch­lö­sung lässt sich spe­zi­fisch anwen­den und geht bei der Pro­blem­lö­sung gezielt und sau­ber vor. Bei mei­nen Tests zeig­te sich aber, dass eini­ge der Angriffs­punk­te zumin­dest bei Con­su­mer-Plat­ten nicht not­wen­dig sind.
Das Ent­la­den eines Ker­nel­mo­duls mit­sammt der Deak­ti­vie­rung des Aus­le­sens aller SMART-Wer­te kommt eben­falls nicht in Fra­ge. Jedoch ent­hält die­se Vari­an­te einen Hin­weis auf eine upgrade­taug­li­che Dau­er­lö­sung, indem Post-Init-Skrip­te vor­ge­schla­gen werden.

In bei­den Vari­an­ten wird jedoch kom­plett ver­nach­läs­sigt, dass der inzwi­schen stan­dard­mä­ßig akti­ve Sys­temd-Ser­vice smartmontools.service eben­falls den Stand­by der Fest­plat­ten ver­hin­dert, da regel­mä­ßig der Plat­ten­zu­stand aus­ge­le­sen wird. Mei­nen Ana­ly­sen nach han­delt es sich um fol­gen­de Zugrif­fe. Der Schlüs­sel-Sync, wel­cher alle 24 Stun­den durch­ge­führt wird, führ­te bei mei­nen unver­schlüs­sel­ten ZFS-Pools nicht zum Auf­we­cken der Platten.

  • Pol­ling der Tem­pe­ra­tur alle 5 Minu­ten bei bestimm­ten Festplattentypen
  • smartctl-Call ohne "-n stand­by" alle 90 Minuten
  • Sys­temd-Ser­vice smartmontools.service Pol­ling alle 30 Minuten

Die kon­kre­te Kon­fi­gu­ra­ti­on sieht z.B. vor, dass die Fest­plat­ten nach 30 Minu­ten in den Stand­by gehen sol­len. Dies wird bei den jewei­li­gen Disks kon­fi­gu­riert. Das Advanced Power Management kann deak­ti­viert blei­ben.
Nun ist dafür zu sor­gen, dass smartmontools.service in einem grö­ße­ren Inter­vall (z.B. 90 Minu­ten) die Plat­ten scannt. Das kann in den Para­me­tern des Ser­vices auf /etc per­sis­tent erfol­gen. Ob dies nach einem Update zu wie­der­ho­len ist, muss sich zeigen.

smartd_cfg_env=/etc/default/smartmontools
smartd_opts='smartd_opts="--interval=5400"'
if ! grep -Fqx -- "$smartd_opts" "$smartd_cfg_env"; then print -r -- "$smartd_opts" >> "$smartd_cfg_env"; fi

Es muss eben­falls dafür gesorgt wer­den, dass, wie bei den vori­gen Ver­sio­nen, der Stand­by-Para­me­ter bei den Midd­le­wa­re-Calls von smartctl der Stand­by-Para­me­ter mit­ver­wen­det wird. Dies lässt sich mit einem Wrap­per lösen, wel­cher mit­tels Post-Init-Skript beim Sys­tem­start erzeugt wird.

midclt call initshutdownscript.create '{"type":"COMMAND","when":"POSTINIT","comment":"Wrap smartctl with standby parameter","command":"mkdir -p /run/hdd_standby && mount -t tmpfs -o size=16m,mode=755,exec tmpfs /run/hdd_standby && cp /usr/sbin/smartctl /run/hdd_standby/smartctl.real && printf \"%s\\n\" \"#!/bin/sh\" \"exec /run/hdd_standby/smartctl.real -n standby \\\"\\$@\\\"\" > /run/hdd_standby/smartctl && chmod 755 /run/hdd_standby/smartctl && mount --bind /run/hdd_standby/smartctl /usr/sbin/smartctl"}'

Zuletzt gibt es noch das Pro­blem, dass das Tem­pe­ra­tur­pol­ling alle fünf Minu­ten man­che Plat­ten am Stand­by hin­dert. Dies lässt sich ein­fach ver­hin­dern, wenn man die Zugriffs­rech­te auf den Tem­pe­ra­tur­wert ent­zieht. Im Ide­al­fall passt man dies ledig­lich an die Fest­plat­ten­mo­del­le an, wel­che davon betrof­fen sind. Im Fol­gen­den das Fest­plat­ten­mo­dell ST4000DM000. Meh­re­re Model­le kön­nen leer­zei­chen­ge­trennt ange­ge­ben werden.

midclt call initshutdownscript.create '{"type":"COMMAND","when":"POSTINIT","comment":"Block drivetemp on unsupported disks","command":"MODELS=\"ST4000DM000\"; for model in /sys/block/sd*/device/model; do [ -f \"$model\" ] || continue; read -r name < \"$model\"; for unsupported in $MODELS; do case \"$name\" in \"$unsupported\"*) chmod 000 \"${model%/device/model}\"/device/hwmon/hwmon*/temp1_input 2>/dev/null; break ;; esac; done; done"}'

Benö­tigt man die Tem­pe­ra­tu­ren gene­rell nicht, kann man das Modul beim Sys­tem­start auch kom­plett entladen.

midclt call initshutdownscript.create '{"type":"COMMAND","when":"POSTINIT","comment":"Unload drivetemp hwmon driver","command":"modprobe -r drivetemp"}'

Passwortrichtlinie des UniFi OS Server

Immer wie­der gibt es Soft­ware, wel­che vor­schreibt, wie genau ein Pass­wort aus­zu­se­hen hat. Der Uni­Fi OS Ser­ver ist eine davon und erlaubt es nicht, die Richt­li­nie anzu­pas­sen. Mit fol­gen­dem Bash-Skript kann das Pass­wort über die undo­ku­men­tier­te API belie­big geän­dert werden.

#!/usr/bin/env bash
set -u

if [[ $# -ne 3 ]]; then
	echo "Verwendung: $0 <Server-IP> '<altes-passwort>' '<neues-passwort>'"
	exit 1
fi

USER="admin"
BASE_URL="https://$1:11443"
OLD_PASSWORD="$2"
NEW_PASSWORD="$3"

LOGIN_BODY="$(mktemp)"
LOGIN_HEADERS="$(mktemp)"
PASSWORD_BODY="$(mktemp)"
PASSWORD_HEADERS="$(mktemp)"

trap 'rm -f "$LOGIN_BODY" "$LOGIN_HEADERS" "$PASSWORD_BODY" "$PASSWORD_HEADERS"' EXIT

# Login
LOGIN_JSON="$(
	jq -n \
		--arg username "$USER" \
		--arg password "$OLD_PASSWORD" \
		'{
			username: $username,
			password: $password
		}'
)"

wget \
	--no-check-certificate \
	--quiet \
	--server-response \
	--header="Content-Type: application/json" \
	--post-data="$LOGIN_JSON" \
	-O "$LOGIN_BODY" \
	"$BASE_URL/api/auth/login" \
	2>"$LOGIN_HEADERS"

HTTP_STATUS="$(
	sed -n 's/^  HTTP\/[^ ]* \([0-9][0-9][0-9]\).*/\1/p' "$LOGIN_HEADERS" | tail -n 1
)"

if [[ "$HTTP_STATUS" != "200" ]]; then
	echo "Login fehlgeschlagen (HTTP $HTTP_STATUS)."
	if [[ -s "$LOGIN_BODY" ]]; then
		cat "$LOGIN_BODY"
	else
		cat "$LOGIN_HEADERS"
	fi
	exit 1
fi

USER_UUID="$(jq -r '.id // empty' "$LOGIN_BODY")"
if [[ -z "$USER_UUID" ]]; then
	echo "Fehler: Keine User-UUID in der Login-Antwort gefunden."
	exit 1
fi

TOKEN="$(sed -n 's/.*Set-Cookie: TOKEN=\([^;]*\).*/\1/p' "$LOGIN_HEADERS" | tail -n 1)"
if [[ -z "$TOKEN" ]]; then
	echo "Fehler: Kein TOKEN-Cookie gefunden."
	exit 1
fi

JWT_PAYLOAD="$(
	printf '%s' "$TOKEN" |
	cut -d. -f2 |
	tr '_-' '/+' |
	awk '{l=length($0)%4; if(l==2) printf "%s==",$0; else if(l==3) printf "%s=",$0; else printf "%s",$0}' |
	base64 -d 2>/dev/null
)"

CSRF_TOKEN="$(
	printf '%s' "$JWT_PAYLOAD" |
	jq -r '.csrfToken // empty'
)"

if [[ -z "$CSRF_TOKEN" ]]; then
	echo "Kein csrfToken im JWT gefunden."
	exit 1
fi

echo "Login erfolgreich."
echo "  User-UUID: $USER_UUID"
echo "  JWT: $TOKEN"
echo "  csrfToken: $CSRF_TOKEN"

# Passwort ändern
PASSWORD_JSON="$(
	jq -n \
		--arg password "$NEW_PASSWORD" \
		'{
			password: $password
		}'
)"

wget \
	--no-check-certificate \
	--quiet \
	--server-response \
	--header="Content-Type: application/json" \
	--header="Cookie: TOKEN=$TOKEN" \
	--header="X-CSRF-Token: $CSRF_TOKEN" \
	--body-data="$PASSWORD_JSON" \
	--method=PUT \
	-O "$PASSWORD_BODY" \
	"$BASE_URL/proxy/users/api/v2/user/$USER_UUID" \
	2>"$PASSWORD_HEADERS"

HTTP_STATUS="$(sed -n 's/.*HTTP\/[^ ]* \([0-9][0-9][0-9]\).*/\1/p' "$PASSWORD_HEADERS" | tail -n 1)"
if [[ "$HTTP_STATUS" != "200" ]]; then
	echo "Passwortänderung fehlgeschlagen (HTTP $HTTP_STATUS)."
	if [[ -s "$PASSWORD_BODY" ]]; then
		cat "$PASSWORD_BODY"
	else
		cat "$PASSWORD_HEADERS"
	fi
	exit 1
fi

echo "Passwort erfolgreich geändert."

Linux Dienst mit privilegiertem Port ohne Administrator-Rechte

Diens­te soll­ten nicht mit admi­nis­tra­ti­ven Rech­ten gestar­tet wer­den. Es sei denn der Dienst sieht dies vor und erstellt die Unter­pro­zes­se mit nor­ma­len Pri­vi­le­gi­en.
Nicht jede Appli­ka­ti­on berück­sich­tigt das, was zur Fol­ge hat, dass man­che Pro­zes­se kei­ne Diens­te im unte­ren Port­be­reich zur Ver­fü­gung stel­len können.

Hier schafft man ein­fach mit authbind Abhil­fe. So kann zum Bei­spiel eine klei­ne Web­ser­ve­r­ap­pli­ka­ti­on mit Benut­zer­rech­ten auf Port 80 lau­fen.
Möch­te man dem Benut­zer "mini­ser­ver" das Recht geben dies durch­zu­füh­ren, legt man mit touch /etc/authbind/byport/80 eine lee­re Datei an und über­gibt mit chown miniserver:miniserver /etc/authbind/byport/80 deren Besitz. Zuletzt wer­den mit chmod 500 /etc/authbind/byport/80 noch die Rech­te limi­tiert.
Nun kann die Appli­ka­ti­on mit authbind als Wrap­per gestar­tet wer­den und darf auf Port 80 lauschen.

WIM von ISO für USB-Stick splitten

Inzwi­schen sind die Win­dows Instal­la­ti­ons­images so groß gewor­den, dass sie nicht mehr auf FAT32-Daten­trä­ger kopiert wer­den kön­nen. Mit fol­gen­dem Befehl kön­nen die­se gesplit­tet werden:

dism /Split-Image /ImageFile:"<Pfad>\install.wim" /SWMFile:"<Pfad>\install.swm" /FileSize:1024

Anschlie­ßend kann die install.wim gelöscht und der ISO-Inhalt pro­blem­los auf den USB-Stick kopiert werden.

Problem mit Desktop-Symbolen unter Windows 10 Creators Update beheben

Das Crea­tors Update (Ver­si­on 1703 = Build 15063) für Win­dows 10 bringt auf eini­gen Sys­te­men mit bestimm­ten Moni­tor-Auf­lö­sun­gen die Desk­top-Sym­bo­le durch­ein­an­der. Anschlie­ßend las­sen sich die­se auch nicht mehr kor­rekt anord­nen. Dies fällt beson­ders dann auf, wenn meh­re­re Icons gleich­zei­tig mar­kiert und ver­scho­ben werden.

Hier­für gibt es eine ein­fa­che Lösung:
Durch einem Rechts­klick auf eine freie Stel­le des Desk­tops erscheint das Kon­text­me­nü. Hier ist unter "Ansicht" die bereits akti­ve Grö­ße der Desk­top-Icons (z.B. "Mit­tel­gro­ße Sym­bo­le") erneut aus­zu­wäh­len. Dadurch wer­den die Sym­bo­le neu am Git­ter aus­ge­rich­tet und die Posi­tio­nen korrigiert.

Anschlie­ßend funk­tio­niert die Posi­tio­nie­rung wie­der wie gewohnt und auch zuvor uner­reich­ba­re Berei­che an den Rän­dern kön­nen wie­der ord­nungs­ge­mäß genutzt werden.

Debian Minimal-Installation auf Raspberry Pi

Um den Raspber­ry Pi als klei­nen Ser­ver zu benut­zen, sind die vor­ge­fer­tig­ten Images von Raspbi­an über­di­men­sio­niert und im Hin­blick auf Boot­zeit und Grund­aus­las­tung unge­eig­net. Seit meh­re­ren Jah­ren nut­ze ich des­halb das Pro­jekt "Raspbi­an (mini­mal) unat­ten­ded net­in­stal­ler", wel­cher ab dem Modell 1B ein­ge­setzt wer­den kann.

Um die aktu­el­len Ker­nel zu nut­zen und auch das Modell 3 zu unter­stüt­zen habe ich das Pro­jekt ent­spre­chend ange­passt. Von der Releases-Sei­te mei­nes Forks kann eine ZIP-Datei her­un­ter­ge­la­den wer­den, des­sen Inhalt ledig­lich auf die SD-Kar­te kopiert wer­den muss.

Anschlie­ßend ein­fach die (µ)SD-Karte und ein mit DHCP und Inter­net­ver­bin­dung ver­sor­gen­des Netz­werk­ka­bel anste­cken, Strom­ver­sor­gung anschlie­ßen und nach ca. 15 Minu­ten Instal­la­ti­ons­zeit boo­tet auto­ma­tisch ein mini­ma­les, sau­be­res Debi­an auf dem Pi.

Anpas­sun­gen des Instal­la­ti­ons­vor­gangs mit­tels einer installer-config.txt kön­nen ana­log zum Ursprungs­pro­jekt gemacht werden.

Man soll­te dar­an den­ken, nach der Instal­la­ti­on mit­tels dpkg-reconfigure locales die Spra­che, dpkg-reconfigure console-data das Tas­ta­tur­lay­out und dpkg-reconfigure tzdata die Zeit­zo­ne festzulegen.

Kernelupdate und Grub2 auf XEN-VMs

Ein XEN basier­ter vSer­ver hat den gro­ßen Vor­teil, dass eige­ne Ker­nel und Modu­le genutzt wer­den kön­nen.
Stellt der Pro­vi­der VMs jedoch nur mit Grub 1 zur Ver­fü­gung ver­hin­dert die Aus­gangs­kon­fi­gu­ra­ti­on aber unter Umstän­den das Boo­ten neue­rer Ker­nels, da "update-grub" die "menu.lst" nicht auto­ma­ti­siert anpas­sen kann.

Im Zuge der Kor­rek­tu­ren darf auch auf Grub 2 migriert wer­den. Hier­bei wer­den die Grub-Ein­trä­ge kom­plett neu gene­riert und sicher­ge­stellt, dass auch Hosts, wel­che Grub 2 nicht selbst­stän­dig laden, ent­spre­chend wei­ter­ge­lei­tet wer­den. Even­tu­el­le Feh­ler­mel­dun­gen, wel­che beim Auf­ruf von "update-grub" auf­tre­ten kön­nen igno­riert wer­den, sofern die jeweils letz­te Aus­ga­be den Erfolg des Befehls anzeigt.

Die fol­gen­den Befeh­le bezie­hen sich auf Debi­an v8.x:

apt-get -y install grub-legacy
cp -pR /boot/grub/ /boot/grub_old
rm -rf /boot/grub/*
rm -rf /boot/xen/*
update-grub
apt-get -y purge pv-grub-menu grub-common grub-legacy grub-pc grub2 grub2-common
apt-get -y install grub-xen
grub-install --target=x86_64-xen
sed -i 's/timeout\t\t5/timeout\t\t0/' /boot/grub/menu.lst
sed -i 's/# groot=(hostdisk\/\/dev\/xvda1)/# groot=(hd0,0)/' /boot/grub/menu.lst
nano /boot/grub/menu.lst

Alle Ker­nel in der Lis­te ent­fer­nen und erset­zen durch:

title Chainload Grub 2
root (hd0,0)
kernel /boot/xen/pvboot-x86_64.elf

Abschlie­ßend Grub 2 fer­tig kon­fi­gu­rie­ren lassen:

sed -i 's/GRUB_TIMEOUT=5/GRUB_TIMEOUT=0/' /etc/default/grub
update-grub

Nun kann die VM neu gestar­tet werden.

Optio­nal:
Wenn gewünscht, kann auch "fstab" ange­passt wer­den, sodass gemäß den Richt­li­ni­en von Debi­an v8 UUIDs als Iden­ti­fier genutzt wer­den. "xvda2" wird im fol­gen­den Bei­spiel als Swap gemountet:

sed -i 's/^\/dev\/xvd/#\/dev\/xvd/' /etc/fstab
sed -i 's/^proc/#proc/' /etc/fstab
echo "# /dev/xvda1" >> /etc/fstab
echo -e "$(blkid /dev/xvda1 -s UUID | awk '{print $2}' | sed 's/\"//g')\t/\t\t$(df -T /dev/xvda1 | grep "^/dev" | awk '{print $2}')\terrors=remount-ro\t0\t1" >> /etc/fstab
echo "# /dev/xvda2" >> /etc/fstab
echo -e "$(blkid /dev/xvda2 -s UUID | awk '{print $2}' | sed 's/\"//g')\tnone\t\tswap\tsw\t\t\t0\t0" >> /etc/fstab

GitHub Release Notifications

Nach­dem ich immer mehr Pro­jek­te bei Git­Hub ver­fol­ge und ein­set­ze, war der Ein­satz einer auto­ma­ti­schen Benach­rich­ti­gung bei neu­en Ver­sio­nen überfällig.

Lösun­gen, wie "Sib­bell" sind lei­der noch nicht aus­ge­reift oder funk­tio­nie­ren über­haupt nicht. Inzwi­schen set­ze ich "rss2email" ein.

Setzt man hin­ter die URL des Release-Tabs das Suf­fix ".atom", erhält man dadurch die URL, wel­che von "rss2email" ein­ge­le­sen wird und deren Ein­trä­ge — sofern noch nicht ver­sandt — per eMail an den gewünsch­ten Emp­fän­ger geschickt wer­den.
Die Kon­fi­gu­ra­ti­ons­da­tei ist nach der Initia­li­sie­rung unter "~/.config/rss2email.cfg" zu finden.

TeamSpeak Passwortwiederherstellung

Team­Speak ist in grö­ße­ren Grup­pen eine prak­ti­sche Alter­na­ti­ve zur VoIP-Kom­mu­ni­ka­ti­on wie sie z.B. auch Sky­pe bie­tet. Hat man eine Instanz im Archiv, zu wel­cher jedoch kei­ne Zugangs­da­ten mehr zur Ver­fü­gung ste­hen, kön­nen die­se leicht wie­der­her­ge­stellt wer­den:
Zunächst ist Team­Speak zu been­den und mit einem tem­po­rär gesetz­ten Pass­wort zu starten:

./ts3server_minimal_runscript.sh serveradmin_password=tempPassword

Anschlie­ßend kann man sich per telnet localhost 10011 mit der Ser­ver­Query-Schnitt­stel­le ver­bin­den und das Pass­wort zurücksetzen:

login serveradmin tempPassword
clientsetserverquerylogin client_login_name=serveradmin

Das neu gene­rier­te Pass­wort wird in der Kon­so­le angezeigt.


Benö­tigt man auch einen Admin-Token, bekommt man die­sen mit fol­gen­den Befehlen:

use sid=1
tokenadd tokentype=0 tokenid1=6 tokenid2=0

Der neu gene­rier­te Token für die Ser­ver­grup­pe "Ser­ver Admin (6)" wird in der Kon­so­le angezeigt.


Man soll­te nicht ver­ges­sen, Team­Speak anschlie­ßend ohne den Pass­wort-Para­me­ter neu zu starten.

Abstürze bei Silverlight in Vollbild-Modus

Nach­dem eini­ge Brow­ser den Sup­port von NPA­PI-Erwei­te­run­gen nach und nach abstel­len, wer­den zum Anse­hen von Video­streams wie­der ver­mehrt Flash-Play­er ein­ge­setzt.
Bei "Ama­zon Instant Video" ist im Flash-Play­er-Modus jedoch kein HD mög­lich, wes­halb sich der auf Win­dows-Sys­te­men in der Regel vor­in­stal­lier­te Inter­net Explo­rer als Ersatz-Brow­ser für das Fil­me­schau­en anbietet.

Hier­bei gibt es jedoch ein Pro­blem: Ver­lässt man meh­re­re Male den Voll­bild­mo­dus, erscheint ein Popup von Sil­ver­light, das jedoch auf kei­ne Ein­ga­be reagiert und zum Absturz des Brow­sers führt. Eine Neu­in­stal­la­ti­on von Sil­ver­light bringt hier­bei kei­ne Abhil­fe.
Um die­ses Pro­blem zu lösen, ist ledig­lich die fol­gen­de Anpas­sung in der Regis­try vorzunehmen:

Schlüs­sel:

HKEY_CURRENT_USER\Software\AppDataLow\Software\Microsoft\Silverlight\Permissions\http://ecx.images-amazon.com:80

DWORD-Wert:

"StaysInFullScreenWhenUnfocused"=dword:00000011