IT security, FreeBSD, Linux, mail server hardening, post-quantum crypto, DNS, retro computing & hands-on hardware hacks. Privater Tech-Blog seit 2003.

Schlagwort: Sysadmin (Seite 1 von 13)

BIOS erklärt, Teil 5: SpeedStep, Turbo, C-States und der Schalter der mir eine Woche Rätselraten erspart hätte

Willkommen bei Teil 5. Die bisherigen Folgen findest du gesammelt unter BIOS & Firmware. Heute geht es um Advanced > CPU Configuration > Advanced Power Management Configuration.

Advanced

Aptio Setup, Advanced Power Management Configuration, mit Power Technology auf Custom und sechs Untermenüs

Die Seite selbst ist kurz:

Power Technology                  [Custom]
Power Performance Tuning          [OS Controls EPB]
ENERGY_PERF_BIAS_CFG Mode         [Balanced Performance]
> CPU P State Control
> Hardware PM State Control
> Frequency Prioritization
> CPU C State Control
> Package C State Control
> CPU T State Control

Die erste Zeile ist wichtiger als sie aussieht. Power Technology kennt neben Custom noch Disable und Energy Efficient, und solange sie nicht auf Custom steht, sind die sechs Untermenüs Dekoration: die Firmware setzt dann ihre eigenen Werte und überschreibt, was man darunter einstellt. Wer sich also wundert, warum eine Änderung im C-State-Menü nichts bewirkt, sollte zuerst hier nachsehen.

Power Performance Tuning auf OS Controls EPB heisst, dass das Betriebssystem den Energy Performance Bias steuern darf und nicht das BIOS. Das ist genau das, was man auf einer Linux-Maschine will. Der dritte Wert ENERGY_PERF_BIAS_CFG Mode ist der Vorgabewert, den die Firmware setzt, solange sich niemand darum kümmert, und Balanced Performance ist dafür eine vernünftige Mitte.

P-States: die Frequenz

Aptio Setup, CPU P State Control, mit SpeedStep, Turbo Mode und der Tabelle der Speed-Select-Profile
SpeedStep (P-States)              [Enable]
AVX P1                            [Nominal]
Dynamic SST-PP                    [Disable]
Intel SST-PP                      [Base]
Activate SST-BF                   [Disable]
Configure SST-BF                  [Enable]
EIST PSD Function                 [HW_ALL]
Turbo Mode                        [Enable]
CPU Flex Ratio Override           [Disable]
CPU Core Flex Ratio               23

SpeedStep ist der Klassiker: die CPU darf ihre Frequenz und Spannung an die Last anpassen. Intel nennt das seit Ewigkeiten EIST, Enhanced Intel SpeedStep Technology. Ohne diesen Schalter läuft der Prozessor stur auf Basistakt, verbraucht im Leerlauf deutlich mehr und wird wärmer, ohne dafür irgendetwas zurückzugeben. Der bleibt an.

Turbo Mode ist die Gegenrichtung: die CPU darf über den Basistakt hinaus, solange Temperatur und Stromaufnahme es hergeben. Bei meinem Xeon Gold 5315Y sind das 3,2 GHz Basis und 3,6 GHz Turbo. Linux bestätigt beide Enden:

lscpu | grep MHz
#   CPU max MHz:  3600,0000
#   CPU min MHz:   800,0000

EIST PSD Function auf HW_ALL regelt, wer die Frequenzwechsel koordiniert. Bei HW_ALL macht das die Hardware für alle Kerne einer Domäne selbst, bei SW_ALL müsste das Betriebssystem mitreden. Hardware ist hier schneller und weiss ohnehin mehr, das lässt man so. ALso ich, öhm. joar. 😀

Der Teil der mich überrascht hat: Speed Select

Mitten auf der Seite steht eine kleine Tabelle, die ich vorher noch nie in einem BIOS gesehen habe:

Intel SST-PP            Base | Config 3 | Config 4
  Core Count             08  |    06    |    04
  Current P1 Ratio [0]   32  |    32    |    34
  Package TDP (W)       140  |   125    |   115
  Tjmax                 103  |   100    |   101

Das ist Intel Speed Select Technology, Variante Performance Profile. Die Idee dahinter: dieselbe CPU kann in mehreren fest definierten Betriebspunkten laufen, und man wählt beim Booten aus, welchen man haben möchte. Meine hat drei. Im Auslieferungszustand Base sind es acht Kerne bei 3,2 GHz und 140 Watt. Wählt man Config 4, bleiben vier Kerne übrig, dafür steigt der Basistakt auf 3,4 GHz und die Verlustleistung fällt auf 115 Watt.

Man tauscht also Kerne gegen Takt, und zwar nicht per Turbo-Zufall, sondern als garantierte Zusicherung. Für Software, die pro Kern lizenziert wird, ist das bares Geld. Für Lasten, die nicht parallelisieren, sind 200 MHz mehr Basistakt spürbar. Und für mich? Ehrlich gesagt nutzlos. Ich habe acht Kerne gekauft, weil ich acht Kerne wollte, und 200 MHz sind kein Argument dafür, die Hälfte davon wegzuwerfen.

Trotzdem finde ich es bemerkenswert, dass so etwas in einem BIOS steht, das ansonsten aussieht wie 2005. SST-BF daneben ist die verwandte Idee auf Kernebene: Base Frequency, einzelne Kerne bekommen dauerhaft mehr Takt, die übrigen entsprechend weniger. Steht bei mir auf Disable, und das passt, denn dafür müsste ich meine Last gezielt auf die schnellen Kerne pinnen. Wer das tut, weiss warum. Wer es nicht tut, gewinnt nichts.

Und hier lag die Antwort

Aptio Setup, Hardware PM State Control, Hardware P-States steht auf Disable
Hardware PM State Control
Hardware P-States                 [Disable]

Ein Untermenü mit genau einer Zeile, und diese Zeile hat mich mehr gefreut als der ganze Rest der Seite. Um zu erklären warum, muss ich kurz ausholen.

Seit dem Umbau ist mir aufgefallen, dass Linux die Frequenzsteuerung auf dieser Maschine anders macht als erwartet. intel_pstate, der Treiber der bei Intel-CPUs normalerweise das Kommando übernimmt, läuft hier im passiven Modus:

cat /sys/devices/system/cpu/intel_pstate/status        # passive
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver # intel_cpufreq
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # schedutil

Passiv bedeutet: der Treiber verhält sich wie ein gewöhnlicher cpufreq-Treiber und lässt den Kernel-Governor entscheiden. Aktiv bedeutet, dass er die Entscheidung selbst trifft und dabei Hardware-Unterstützung nutzt. Aktiv ist normalerweise der bessere Modus, weil die CPU schneller reagiert als jeder Governor es könnte.

Der Kernel schaltet in den aktiven Modus, wenn die CPU HWP beherrscht, Hardware P-States. Ice Lake beherrscht das. Nur taucht das entsprechende Flag bei mir gar nicht auf:

grep -o hwp /proc/cpuinfo | head -1
#   (keine Ausgabe)

Ich hatte das eine Weile für eine Eigenheit des Kernels gehalten, für eine Frage der Xeon-Variante, für irgendetwas Kompliziertes. Es war nichts davon. Es war dieser eine Schalter, zwei Menüebenen tief in einem Untermenü, das ich beim ersten Mal nicht geöffnet hatte. Ich sag doch, ich bin auch nur doof.

Was tut HWP eigentlich? Ohne HWP sagt das Betriebssystem der CPU, welche Frequenz sie fahren soll. Mit HWP sagt es ihr nur noch, worauf es ihm ankommt, irgendwo zwischen Sparsamkeit und Leistung, und die CPU sucht sich den Rest selbst. Der Vorteil ist die Reaktionszeit: die Hardware sieht Lastwechsel im Mikrosekundenbereich, der Governor im Millisekundenbereich. Bei kurzen, stossweisen Lasten, wie sie ein Desktop dauernd produziert, macht das einen Unterschied.

Werde ich es umstellen? Vermutlich ja, aber nicht heute und nicht ohne Messung. Auf der alten Maschine hatte ich Ärger mit schedutil, der mir zu träge hochgetaktet hat, insofern habe ich eine Vermutung wohin es geht. Aber genau solche Vermutungen sind der Grund, warum man am Ende Dinge kaputtoptimiert. Erst messen, dann drehen.

C-States: das Nichtstun

Aptio Setup, CPU C State Control, mit Monitor MWAIT, C6 Report und Enhanced Halt State
Enable Monitor MWAIT              [Enable]
CPU C6 Report                     [Auto]
Enhanced Halt State (C1E)         [Enable]

P-States regeln, wie schnell die CPU arbeitet. C-States regeln, wie tief sie schläft wenn sie gerade nichts tut. Je höher die Nummer, desto mehr wird abgeschaltet und desto länger dauert das Aufwachen. Was auf meiner Maschine ankommt, steht im sysfs:

POLL   0 us
C1     1 us
C1E    4 us
C6   170 us

C6 ist der interessante: dort wird der Kernzustand weggeschrieben und der Kern faktisch abgeschaltet. Das spart richtig Strom, kostet aber 170 Mikrosekunden zum Aufwachen. Für einen Rechner unter dem Schreibtisch ist das vollkommen egal. Für einen Paketfilter, der Latenz im einstelligen Mikrosekundenbereich garantieren soll, ist es das nicht, und genau deshalb steht in Tuning-Anleitungen für Netzwerkkram regelmässig, man solle die tiefen C-States abschalten.

Enable Monitor MWAIT ist die Voraussetzung für den ganzen Mechanismus: MONITOR und MWAIT sind die beiden Befehle, mit denen ein Kern sagt „weck mich, wenn sich diese Speicherstelle ändert“ und sich danach schlafen legt. Ohne sie bleibt nur die alte HLT-Schleife.

Package C State [Auto] eine Menüebene weiter ist die Steigerung davon: nicht mehr einzelne Kerne, sondern das ganze Paket samt Uncore und Speichercontroller. Das geht natürlich nur, wenn wirklich alle Kerne gleichzeitig nichts tun.

T-States, und warum sie aus sind

Software Controlled T-States      [Disable]

Der dritte Buchstabe im Bunde, und der unangenehmste. T-States drosseln nicht die Frequenz, sondern schieben Pausen ein: die CPU bekommt für einen Teil der Zeit schlicht keinen Takt. Das ist deutlich brutaler als ein P-State und eigentlich eine Notbremse für den Fall, dass es zu heiss wird.

Auf Disable heisst, dass das Betriebssystem diese Notbremse nicht selbst ziehen darf. Der thermische Schutz der Hardware bleibt davon unberührt, der arbeitet unabhängig weiter. Bei Tjmax 103 Grad und CPU-Temperaturen um die 40 im Leerlauf ist das eine sehr theoretische Diskussion.

Der Rest

Frequency Prioritization
RAPL Prioritization               [Disable]

RAPL ist Intels Mechanismus zur Leistungsbegrenzung. Mit RAPL Prioritization verteilt die Firmware ein knappes Leistungsbudget gezielt auf einzelne Kerne, statt alle gleichmässig zu drosseln. Das ergibt Sinn, wenn man dauerhaft am Powerlimit fährt und weiss, welche Kerne die wichtige Arbeit machen. Meine Maschine ist von beidem weit entfernt.

Was ich mitnehme

Der ganze Block ist erstaunlich gut eingestellt für ein Board, das im Auslieferungszustand von einem Serverhersteller kam. SpeedStep an, Turbo an, C-States an, T-States aus, das würde ich genau so einstellen. Ob da Supermicro oder Thomas Krenn sich Mühe gegeben hat?

Der einzige echte Fund ist Hardware P-States [Disable]. Und der ist vor allem deshalb schön, weil er zeigt, wozu so ein Durchgang gut ist: ich habe seit Wochen ein Verhalten beobachtet, das mich gewundert hat, und die Ursache lag die ganze Zeit sichtbar in einem Menü. Ich musste nur hineinschauen.

Nächste Folge: der Speicher. ECC, Patrol Scrub, PPR, und die Frage warum 256 GB DDR4-3200 in dieser Maschine mit 2933 laufen, klingt ja falsch oder?

Siehe auch

Falls jemand von euch intel_pstate im aktiven Modus gegen schedutil gemessen hat, auf einem Xeon und nicht auf einem Notebook: erzähl mir davon, bevor ich es selbst ausprobiere. Ihr dürft mich gerne fragen.

BIOS erklärt, Teil 4: AES-NI, SGX, TME und ein Schalter der nichts bewirkt

In der letzten Folge ging es um die obere Hälfte der CPU-Seite, also Kerne, Threads und Prefetcher. Die untere Hälfte ist die spannendere, weil dort keine Leistungsschalter mehr stehen, sondern Sicherheitsfunktionen. Und weil mindestens einer davon eine richtig gute Geschichte hat.

Zur Erinnerung für alle die neu dazustossen: ich gehe hier das BIOS meines Supermicro-Serverboards durch, Themenblock für Themenblock, alle Folgen liegen unter BIOS & Firmware.

AES-NI, oder: der Grund warum es eine Karte gab

Aptio Setup, CPU Configuration, mit Hyper-Threading, den fünf Prefetchern und AES-NI
AES-NI                          [Enable]

Ein Schalter und dahinter steckt eine ganze Ära. AES-NI sind sechs zusätzliche Prozessorbefehle, die AES in Hardware rechnen statt in Software. Der Unterschied ist keine Feinheit, er liegt grob bei einer Grössenordnung, und obendrein sind die Hardwarebefehle immun gegen die Cache-Timing-Seitenkanäle, mit denen man Software-AES angreifen kann. Aber vor allem is et dann ers rischtig flott mit der Crypto.

Ob die CPU es kann und darf, sieht man sofort im OS mit:

grep -o ' aes ' /proc/cpuinfo | head -1
#    aes

Warum es diesen Schalter überhaupt gibt, ist eine berechtigte Frage. Man schaltet AES-NI ja nicht ab, weil man gern langsam verschlüsselt. Die ehrliche Antwort ist vermutlich, dass er aus einer Zeit stammt, in der AES-NI ein Unterscheidungsmerkmal zwischen Produktlinien war, und niemand hat ihn seitdem entfernt. Oder hat jemand eine bessere Erklärung?

Über genau diese Zeit habe ich hier schon geschrieben. In meinem Beitrag zur Intel QuickAssist 8950-SCCP steckt eine Karte, die es nur deshalb gab, weil manche CPUs kein AES-NI hatten. Wer 2013 ein NAS mit einer Atom-CPU verschlüsseln wollte, brauchte Hilfe. Heute ist der Schalter eine Formalie, damals war er die Grenze zwischen „geht“ und „geht nicht“.

Virtualisierung und ein Schalter der Vertrauen heisst

Intel Virtualization Technology [Enable]
Enable SMX                      [Disable]

Der erste ist VT-x, im Handbuch etwas altmodisch VMX und in einer noch älteren Fassung sogar Vanderpool Technology genannt, nach dem Codenamen von 2003. Ohne ihn läuft keine Hardware-Virtualisierung, also weder KVM noch VirtualBox mit brauchbarer Geschwindigkeit. Bleibt selbstverständlich an.

Der zweite ist interessanter. SMX steht für Safer Mode Extensions und ist die Grundlage für Intel TXT, Trusted Execution Technology. Vereinfacht gesagt geht es darum, dass eine Maschine beim Start beweisen kann, dass sie genau die Software geladen hat, die sie laden sollte. Ein gemessener Start, verankert in der Hardware, ähnlich wie Measured Boot mit einem TPM, nur eine Ebene tiefer.

Dabei ist mir etwas aufgefallen, das ich nicht restlos erklären kann. Im Setup steht SMX auf Disable. In /proc/cpuinfo steht das Flag smx trotzdem drin. Die naheliegende Deutung ist, dass das Flag die Fähigkeit der CPU beschreibt und der BIOS-Schalter nur die Freigabe, dass CPUID also weiterhin „kann ich“ meldet, während die Firmware „darfst du nicht“ sagt. Belegen kann ich das nicht, ich habe es nur beobachtet. Wer da genauer Bescheid weiss, immer her damit. Ich bin ja auch nur doof.

Speicherverschlüsselung, und warum SGX daran hängt

Total Memory Encryption (TME)   [Disabled]

TME verschlüsselt den kompletten Arbeitsspeicher. Der Schlüssel wird bei jedem Start neu erzeugt, liegt im Speichercontroller und verlässt die CPU nie. Für das Betriebssystem ist das unsichtbar, es merkt schlicht nichts davon.

Wogegen hilft das? Gegen Angriffe, die physisch am Speicher ansetzen. Der Klassiker ist der Cold-Boot-Angriff, bei dem man die Riegel kühlt, aus der laufenden Maschine reisst und in einem anderen Gerät ausliest, weil DRAM seinen Inhalt für Sekunden bis Minuten behält. Dazu kommt alles, was per DMA am Speicher horcht. Bei einem Server im fremden Rechenzentrum ist das kein theoretisches Szenario. Sucht das mal auf YouTube ist so, das sieht immer krass aus. Vielleicht kann ich so etwas selbst irgendwann mal probieren.

Bei mir steht es auf Disabled, und das ist der Werksdefault. Meine Maschine steht in meiner Wohnung, die Systemplatte ist ohnehin mit LUKS verschlüsselt, und das Angriffsmodell, gegen das TME hilft, setzt jemanden voraus der hier steht während die Kiste läuft. Falls das passiert, habe ich grössere Probleme. Reizvoll finde ich es trotzdem, denn die Kosten sind gering: die Verschlüsselung sitzt im Speichercontroller und macht sich in Messungen kaum bemerkbar.

Ein Detail, über das ich beim Lesen des Handbuchs gestolpert bin, erklärt den Aufbau des Menüs:

*If the feature above is set to Enabled, the next five features are displayed*

Erst wenn TME an ist, erscheinen die fünf Zeilen darunter, und dazu gehört auch SGX. Die Enklaven-Technik hängt hier also an der Speicherverschlüsselung. Das ergibt Sinn, denn der geschützte Speicherbereich einer Enklave muss ja gegen genau die Zugriffe abgesichert sein, die TME abdeckt. Im Setup sieht man davon nur eine flache Liste, im Handbuch die Abhängigkeit.

SGX, und wie Intel die UHD-Blu-ray auf dem PC beerdigt hat

SW Guard Extensions (SGX)       [Disabled]
SGX Factory Reset               [Disabled]
SGX Package Info In-Band Access [Disabled]

SGX ist die Idee, dass ein Programm einen Speicherbereich anlegen kann, in den niemand hineinsehen darf. Nicht andere Programme, nicht der Kernel, nicht der Hypervisor, nicht einmal jemand mit Root. Die CPU verschlüsselt den Bereich und gibt ihn nur dem Code frei, der ihn angelegt hat. Diese Enklaven waren als Fundament für vertrauliche Berechnungen in fremden Rechenzentren gedacht.

Die Geschichte drumherum ist allerdings die bessere. SGX war jahrelang in normalen Desktop-Prozessoren drin, und die prominenteste Anwendung war nicht vertrauliches Rechnen, sondern Kopierschutz. Die Wiedergabe einer Ultra-HD-Blu-ray auf dem PC verlangte SGX, weil der Schlüsselaustausch in einer Enklave passierte. Dann hat Intel SGX ab der elften Generation aus den Consumer-CPUs entfernt, und damit war die UHD-Blu-ray-Wiedergabe auf neuen PCs vorbei. Wer eine Scheibe abspielen wollte, brauchte plötzlich einen älteren Prozessor.

Ich finde das aus mehreren Gründen bemerkenswert. Erstens ist es ein schönes Beispiel dafür, wie eine Sicherheitstechnik zweckentfremdet wird und dann an einer Produktentscheidung stirbt, die mit ihrem eigentlichen Zweck nichts zu tun hat. Zweitens hat es Leute getroffen, die für ihre legal gekauften Scheiben plötzlich keinen legalen Abspielweg mehr hatten, während der inoffizielle Weg weiterhin problemlos funktionierte. Kopierschutz eben.

Im Xeon lebt SGX weiter, dort war es nie der Kopierschutz sondern das Verkaufsargument. Bei mir steht es trotzdem aus, weil ich keine Software habe die Enklaven nutzt, und weil es ohnehin nur zusammen mit TME ginge. Der Vollständigkeit halber, im laufenden System taucht folgerichtig nichts davon auf:

grep -c ' sgx' /proc/cpuinfo    # 0
ls /sys/devices/system/cpu/ | grep -c sgx    # 0

46 Bit, wegen Hyper-V

Limit CPU PA to 46 bits         [Enable]

PA steht für Physical Address. Der Schalter begrenzt die Breite der physischen Adressen auf 46 Bit, was 64 Terabyte adressierbarem Speicher entspricht. Bei 256 GB im Rechner ist das reichlich weit weg von jeder Grenze. 46 Bit und 64 Bit, da muss ich direkt an fe80 bei IPv6 denken und das man die Luft gelassen hat, weil MAC Adressen auch irgendwann zu wenige sind. Ok da sind es 48 Bit aber denken muss ich daran.

Die Begründung im Handbuch ist knapp und ziemlich entlarvend:

Use this feature to limit the CPU physical address to 46 bits to support older hyper-v.

Ältere Hyper-V-Versionen kamen mit breiteren Adressen nicht klar, also gibt es im BIOS eines Serverboards von 2021 einen Schalter, der die CPU künstlich einschränkt, damit eine bestimmte Microsoft-Software läuft. Das ist Abwärtskompatibilität in ihrer reinsten Form. Der Effekt lässt sich direkt nachlesen:

grep -m1 'address sizes' /proc/cpuinfo
#   address sizes : 46 bits physical, 57 bits virtual

46 physisch, wie eingestellt. Die 57 virtuellen Bit sind übrigens eine ganz andere Baustelle, das ist Five-Level Paging, ebenfalls neu mit Ice Lake.

Eine Seriennummer im Prozessor

Aptio Setup, untere Hälfte der CPU Configuration, mit TME, SGX, PPIN Control und Extended APIC
PPIN Control                    [Lock/Disable]

PPIN steht für Protected Processor Inventory Number, eine eindeutige Nummer pro physischer CPU. Gedacht ist sie für die Fehleranalyse in grossen Flotten: wenn ein Prozessor Speicherfehler meldet, will man wissen welcher es war, und zwar auch nachdem er längst ausgebaut wurde.

Eine eindeutige, unveränderliche Hardwarenummer ist allerdings genau die Sorte Datenpunkt, an der sich vor Jahren schon einmal die Gemüter erhitzt haben. Der Pentium III hatte eine Seriennummer, der Aufschrei war so gross, dass Intel sie wieder entfernt hat. PPIN ist die gleiche Idee, nur diesmal mit einem Schalter davor und beschränkt auf Serverprozessoren.

Hier ist mir eine Abweichung aufgefallen. Das Handbuch nennt als mögliche Werte:

The options are Unlock/Disable and Unlock/Enable.

Im Setup steht aber Lock/Disable, ein Wert den das Handbuch gar nicht kennt. Passend dazu fehlt im laufenden System das Flag:

grep -c intel_ppin /proc/cpuinfo    # 0

Meine Lesart: Lock bedeutet, dass das Register gesperrt ist und die Nummer nicht ausgelesen werden kann, und das Handbuch ist an dieser Stelle schlicht nicht auf dem Stand der Firmware. Für mich ist das der Wunschzustand, ich brauche keine Seriennummer und niemand sonst braucht sie von mir. Nur schön dokumentiert ist es eben nicht.

Und zum Schluss der Schalter, der nichts bewirkt

Extended APIC                   [Disable]

Das ist mein Lieblingsfund auf dieser Seite, weil er aussieht wie eine vergessene Optimierung. Der APIC ist der Interrupt-Controller. x2APIC ist seine erweiterte Fassung, die mehr adressierbare Prozessoren und schnelleren Zugriff über Register statt über den Speicher erlaubt. Auf Disable denkt man reflexhaft: aha, hier liegt Leistung brach, das schalte ich mal ein.

Nur ist die Funktion längst aktiv. Aus dem Kernel-Log meiner laufenden Maschine:

DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
DMAR-IR: Enabled IRQ remapping in x2apic mode
x2apic enabled
APIC: Switched APIC routing to: cluster x2apic

Der Grund ist eine Abhängigkeit, die man kennen muss: Interrupt-Remapping über VT-d verlangt x2APIC. Sobald der Kernel die IOMMU mit Interrupt-Remapping hochfährt, schaltet er x2APIC selbst ein, unabhängig davon was die Firmware vorher getan hat. Der BIOS-Schalter steuert nur, ob die Firmware das schon vorab macht. B.T.W.: habe ihr euch MMU beim Commodore C128 mal angeschaut?

Ich lasse ihn deshalb bewusst in Ruhe, und ich schreibe das hier so ausführlich auf, weil er genau die Art Schalter ist, an dem man beim nächsten Setup-Besuch wieder hängenbleibt und denkt, man hätte etwas übersehen. Hat man nicht.

Nächste Folge: Speicher. ECC, Patrol Scrub, und die Frage warum mein DDR4-3200 mit 2933 läuft und das völlig in Ordnung ist.

Siehe auch

Weiss jemand genauer, warum das smx-Flag in /proc/cpuinfo auftaucht obwohl der BIOS-Schalter auf Disable steht? Ich habe dazu keine belastbare Quelle gefunden und würde es gern richtig verstehen. Ihr dürft mich jederzeit fragen.

Kein SMART für SD-Karten: sieben Linux-Werkzeuge, die trotzdem verraten, wie gesund eine Karte ist

Transcend-8-GB-microSD-Karte im SD-Adapter und USB-Kartenleser neben Linux-Werkzeugen zur Prüfung von Kapazität, Datenintegrität und Schreibleistung ohne SMART.

Eine alte 8-GB-microSD-Karte lag noch in der Schublade, entbehrlich genug für einen Härtetest. Die Ausgangsfrage war simpel: ist die Karte noch gut, oder produziert sie im Hintergrund längst stille Fehler? Bei einer SSD würde ich smartctl -a tippen und hätte binnen Sekunden Health-Prozent, Reallocated Sectors und Power-On-Hours auf dem Schirm. Bei einer SD-Karte geht genau das nicht, und der Grund dafür ist interessanter als die fehlende Zahl selbst.

Warum SMART hier nicht funktioniert

ATA- und NVMe-Laufwerke haben eine standardisierte SMART-Schnittstelle: fest definierte Attribute, die die Firmware selbst pflegt und die jedes Betriebssystem auf die gleiche Weise abfragen kann. SD-Karten haben nichts Vergleichbares. Der Flash-Controller auf der Karte übernimmt zwar Wear-Leveling und Bad-Block-Mapping, aber wie er das im Detail tut und was er darüber nach außen preisgibt, ist herstellerspezifisch und komplett verschlossen. Es gibt zwar eine SD Health Status Extension, CMD56-basiert, die unterstützt aber so gut wie kein Consumer-Werkzeug, und sie setzt einen nativen MMC-Host voraus, keinen USB-Kartenleser.

Ein Wort noch zu TRIM, bevor der Eindruck entsteht, SD-Karten hätten damit überhaupt nichts zu tun: der Linux-MMC-Stack unterstützt discard-artige Operationen für native mmcblk-Geräte durchaus. Nur bringt das über einen USB-Kartenleser nichts, weil die USB-Massenspeicher-Übersetzung diesen Pfad gar nicht durchreicht, das habe ich in diesem Test empirisch bestätigt. Und selbst auf einem nativen Host wäre das nur ein Erase-Hinweis an den Controller, keine standardisierte Health-Rückmeldung wie bei SSD-TRIM zusammen mit SMART.

Testkandidat und Aufbau

Für den Test kam eine Transcend Premium 8GB microSDHC zum Einsatz, Class 10, UHS-Speed-Class U1, in einem Samsung-Adapter auf volle SD-Größe gesteckt und über einen UGREEN-USB-Kartenleser ausgelesen. Der Leser meldet sich per USB als Genesys-Logic-Chip (05e3:0748), ein generischer Combo-Reader, wie er auch in vielen Laptops und günstigen USB-Dongles verbaut ist. Host war ein Linux Mint 22.3 mit Kernel 7.0.0-28-generic, Ubuntu-24.04-Unterbau, alle Befehle liefen als root.

MicroSD-Karte im Samsung-Adapter, gesteckt in einen schwarzen USB-Kartenleser auf einer Küchenarbeitsplatte
So kam die Karte beim Testsystem an: microSD im Samsung-Adapter, gesteckt in den UGREEN-USB-Kartenleser.

Werkzeuge installieren

Bis auf badblocks, smartctl, hdparm und dd, die auf den meisten Systemen ohnehin vorhanden sind, kommt der Rest aus den Standard-Paketquellen, keine Drittanbieter-PPA nötig:

apt-get install -y f3 mmc-utils sdparm flashbench
apt-get install -y fio   # nachinstalliert, sobald der Schritt feststand

Installiert wurden f3 8.0, mmc-utils als Git-Snapshot von 2022, sdparm 1.12, flashbench 62 und fio 3.36, alles aus dem Ubuntu-Noble-Universe-Repository. Getestet auf Linux Mint 22.3, aber auf einem reinen Ubuntu 24.04 sollten Paketnamen und Versionen identisch sein.

Schritt 1: Identifikation, warum SMART eine Sackgasse ist

$ smartctl -a /dev/sdc
/dev/sdc: Unknown USB bridge [0x05e3:0x0748 (0x1209)]
Please specify device type with the -d option.

$ smartctl -a -d sat /dev/sdc
Read Device Identity failed: scsi error unsupported scsi opcode
A mandatory SMART command failed: exiting.

$ smartctl --scan
/dev/nvme0 -d nvme
/dev/nvme1 -d nvme
# sdc taucht gar nicht erst auf, smartd hält es für keinen unterstützten Gerätetyp

$ sdparm -a /dev/sdc
MODE SENSE(10): Malformed SCSI command
    /dev/sdc: Generic   MassStorageClass  1209

$ sdparm -i /dev/sdc
    /dev/sdc: Generic   MassStorageClass  1209
Device identification VPD page:
  Addressed logical unit:
    designator type: T10 vendor identification,  code set: ASCII
      vendor id: Generic
      vendor specific: STORAGE DEVICE

smartctl erkennt die Genesys-Logic-USB-Bridge gar nicht erst als unterstützten Gerätetyp. Erzwingt man SAT, also ATA-Kommandos über SCSI getunnelt, scheitert das vollständig, weil die Bridge-Firmware das Kommando schlicht nicht implementiert. Selbst der Geräte-Scan von smartd listet /dev/sdc gar nicht auf. sdparm kommt einen Schritt weiter, eine simple SCSI-INQUIRY funktioniert, aber die gemeldete Identität bleibt vollständig generisch: „Generic MassStorageClass“, „STORAGE DEVICE“. Weder Hersteller-ID noch Seriennummer noch irgendeine Form von Wear- oder Health-Daten sind über diesen Pfad erreichbar. Das ist der konkrete, reproduzierbare Beleg dafür, dass es für SD-Karten hinter einem USB-Leser kein SMART-Äquivalent gibt.

$ lsusb -v -d 05e3:0748
Bus 002 Device 007: ID 05e3:0748 Genesys Logic, Inc. All-in-One Cardreader
  bcdUSB               3.10
  idVendor           0x05e3 Genesys Logic, Inc.
  idProduct          0x0748 All-in-One Cardreader
  iSerial                 5 000000001209
  bInterfaceClass         8 Mass Storage
  bInterfaceSubClass      6 SCSI
  bInterfaceProtocol     80 Bulk-Only
  SuperSpeed USB Device Capability:
    wSpeedsSupported   0x000e  (Full/High/SuperSpeed, bis zu 5Gbps Link)

Der Leser meldet sich als „All-in-One Cardreader“ von Genesys Logic, ein verbreiteter generischer Combo-Chip, USB-3.1-fähig, spricht auf der SCSI-Ebene aber Bulk-Only Transport (BOT), nicht UAS. Wichtig für die Kausalität: BOT statt UAS ist für sich genommen nicht der Grund, warum SAT-Passthrough scheitert. Es gibt genug BOT-Bridges, die SAT unterstützen, und genug, die es nicht tun, unabhängig vom Transportprotokoll. Belegt ist hier nur, dass ausgerechnet diese Genesys-Bridge keine ATA- beziehungsweise SAT-Kommandos durchreicht, nicht dass USB-Bulk-Only-Bridges das grundsätzlich nicht könnten.

Transcend Premium 8GB microSDHC Class 10 U1 im Samsung-Adapter, liegend auf einem UGREEN-USB-Kartenleser mit blau leuchtender Status-LED
Der Aufdruck verrät mehr als jedes Software-Tool: Transcend Premium, 8GB, Class 10, UHS-Speed-Class U1.

Schritt 2: Durchsatz mit dd und hdparm

hdparm -t allein ergab rund 90 MB/s gepuffertes Lesen, plausibel, aber es lohnt sich, mit echtem O_DIRECT-I/O gegenzuprüfen, damit keine Bridge- oder Seiten-Cache-Effekte den Wert verfälschen.

$ hdparm -Tt /dev/sdc
 Timing cached reads:   24182 MB in  2.00 seconds = 12113.48 MB/sec
 Timing buffered disk reads: 272 MB in  3.00 seconds =  90.66 MB/sec

# 512MB Zufallsnutzlast zuerst im tmpfs erzeugt, damit die CPU-Last von
# /dev/urandom die eigentliche Messung nicht verfälscht
$ dd if=/dev/urandom of=/root/sdtest/payload.bin bs=1M count=512
536870912 Bytes (537 MB, 512 MiB) kopiert, 1,74754 s, 307 MB/s

# SCHREIBEN: Direct I/O direkt auf das Rohgerät, kein Seiten-Cache beteiligt
$ dd if=/root/sdtest/payload.bin of=/dev/sdc bs=1M count=512 oflag=direct,sync
536870912 Bytes (537 MB, 512 MiB) kopiert, 22,8654 s, 23,5 MB/s

# LESEN: erst flushen, dann Direct I/O der gleichen 512MB zurück
$ blockdev --flushbufs /dev/sdc
$ dd if=/dev/sdc of=/root/sdtest/readback.bin bs=1M count=512 iflag=direct
536870912 Bytes (537 MB, 512 MiB) kopiert, 6,28156 s, 85,5 MB/s

# Integritätscheck
$ cmp /root/sdtest/payload.bin /root/sdtest/readback.bin
MATCH: identical

Ergebnis: rund 23,5 MB/s sequenzielles Schreiben, rund 85,5 MB/s sequenzielles Lesen, mit byteidentischem Rücklese-Ergebnis. Die Asymmetrie zwischen Schreiben und Lesen ist normal und erwartbar, Schreiben braucht auf Flash-Ebene ein Erase-before-Program, Lesen nicht. 23,5 MB/s reißt die Class-10- und U1-Mindestangabe von 10 MB/s locker, für U3/V30 mit 30 MB/s Minimum würde es nicht reichen, was exakt zum aufgedruckten Rating der Karte passt: Class 10, U1, keine U3- oder V30-Kennzeichnung.

Schritt 3: f3probe, ist die Kapazität echt?

Bevor der große, langsame Test kommt, lohnt sich der schnelle: f3 (Fight Flash Fraud) bringt mit f3probe einen Kapazitätsbetrugs-Check, der binnen weniger Minuten läuft, statt die ganze Karte zu beschreiben.

$ f3probe --time-ops /dev/sdc

Probe finished, recovering blocks... Done

Good news: The device `/dev/sdc' is the real thing

Device geometry:
	         *Usable* size: 7.31 GB (15333376 blocks)
	        Announced size: 7.31 GB (15333376 blocks)
	                Module: 8.00 GB (2^33 Bytes)
	Approximate cache size: 0.00 Byte (0 blocks), need-reset=no
	   Physical block size: 512.00 Byte (2^9 Bytes)

Probe time: 1'46"
 Operation: total time / count = avg time
      Read: 29.32s / 4197116 = 6us
     Write: 1'15" / 4192321 = 18us
     Reset: 0us / 1 = 0us

Verdikt: eine echte Karte, keine Fälschung. Die angekündigte Größe, 8-GB-Modul mit 7,31 GB nutzbar, deckt sich mit der tatsächlich nutzbaren Größe, die f3probe per binärer Suche gefunden hat, also dort, wo Schreibvorgänge aufhören, korrekt anzukommen. Approximate cache size: 0.00 Byte ist ebenfalls ein gutes Zeichen: manche gefälschten Karten täuschen ihre Kapazität vor, indem sie eine kleine Menge echten Flashs als Write-Back-Cache benutzen, der Schreibvorgänge über die tatsächliche Kapazität hinaus vorübergehend „schluckt“ und damit naive Benchmarks täuscht. Diese Karte zeigt diesen Trick nicht. f3probe hat die für die Prüfung benutzten Blöcke danach wiederhergestellt, ohne -n gestartet, die Karte blieb also in ihrem vorherigen, leeren Zustand. Reine Probe-Zeit rund 1:46 Minuten, mit Block-Wiederherstellung insgesamt rund 3 Minuten, deutlich schneller als ein vollständiger Schreib-Lese-Durchlauf, weil f3probe gezielt sucht statt jeden Block anzufassen.

Schritt 4: f3write und f3read, der Haupttest

$ wipefs -a /dev/sdc
$ parted -s /dev/sdc mklabel gpt mkpart primary ext4 0% 100%
$ mkfs.ext4 -F -L SDTEST /dev/sdc1
$ mount /dev/sdc1 /mnt/sdtest
$ df -h /mnt/sdtest
/dev/sdc1       7,2G     24K  6,8G    1% /mnt/sdtest

$ cd /mnt/sdtest && f3write .
Free space: 7.10 GB
Creating file 1.h2w ... OK!
...
Creating file 8.h2w ... OK!
Free space: 16.46 MB
Average writing speed: 22.78 MB/s

$ f3read .
                  SECTORS      ok/corrupted/changed/overwritten
Validating file 1.h2w ... 2097152/        0/      0/      0
...
Validating file 8.h2w ...  176128/        0/      0/      0

  Data OK: 7.08 GB (14856192 sectors)
Data LOST: 0.00 Byte (0 sectors)
	       Corrupted: 0.00 Byte (0 sectors)
	Slightly changed: 0.00 Byte (0 sectors)
	     Overwritten: 0.00 Byte (0 sectors)
Average reading speed: 90.32 MB/s

Verdikt: sauber. Jedes Byte jeder der 8 Testdateien, 7,08 GB insgesamt, bis auf rund 16 MB Restplatz gefüllt, kam exakt so zurück, wie es geschrieben wurde: 0 korrupte, 0 veränderte, 0 überschriebene Sektoren. Die gemessenen Geschwindigkeiten, 22,78 MB/s Schreiben und 90,32 MB/s Lesen, decken sich eng mit dem rohen dd-Direct-I/O-Benchmark aus Schritt 2, eine gute Gegenprobe, dass die Zahlen echt sind und kein Artefakt einer einzelnen Methode.

Schritt 5: badblocks, die Musterprüfung

$ umount /mnt/sdtest
$ badblocks -wsv /dev/sdc

Es wird nach defekten Blöcken gesucht (Lesen+Schreiben-Modus)
Von Block 0 bis 7666687
Es wird getestet Mit Muster 0xaa: ... 100% erledigt
Es wird getestet Mit Muster 0x55: ... 100% erledigt
Es wird getestet Mit Muster 0xff: ... 100% erledigt
Es wird getestet Mit Muster 0x00: ... 100% erledigt
Durchgang beendet, 0 defekte Blöcke gefunden. (0/0/0 Fehler)

Verdikt: 0 defekte Blöcke, über alle 4 Standard-Testmuster hinweg (0xAA/01010101, 0x55/10101010, 0xFF/lauter Einsen, 0x00/lauter Nullen, gewählt, um Stuck-at-0- und Stuck-at-1-Zellfehler ebenso wie Kopplungsfehler zwischen Nachbarbits zu erwischen). Gesamtlaufzeit für den kompletten Schreib-Lese-Vergleichs-Zyklus über alle 4 Muster: rund 26 Minuten 47 Sekunden auf dieser 7,31-GiB-Karte, was zur Schätzung von rund 27 Minuten aus den früheren Durchsatzwerten passt, 4-mal Schreibdurchlauf plus Lesedurchlauf bei rund 23,5/85 MB/s.

badblocks -w ist ein linearer, positionsbasierter Test, Teil von e2fsprogs. In einem Durchgang schreibt er dasselbe Muster auf jeden logischen Block und liest es zurück. Genau das ist der strukturelle Unterschied zu f3write/f3read, und der Grund, warum badblocks kein Ersatz für f3 bei der Kapazitätsbetrugsfrage ist: eine Karte, die Schreibvorgänge still auf einen kleineren physischen Bereich zurückführt, könnte in einem badblocks-Durchgang trotzdem das „richtige“ Muster zurückliefern, weil ohnehin jeder logische Block identischen Inhalt bekommt, das Aliasing bliebe unsichtbar. f3write vermeidet das, indem es positionsabhängige Pseudozufallsdaten schreibt, sodass Wraparound oder Aliasing als Mismatch auffällt. badblocks beantwortet dafür eine engere, ergänzende Frage: versagen bestimmte Blöcke oder Zellen dabei, irgendein Muster zuverlässig zu speichern, was der einmalige, pseudozufällige Schreibdurchgang von f3 pro Karte nicht so systematisch prüft, kein 0xAA/0x55/0xFF/0x00-Stuck-Bit-Sweep.

Schritt 6: flashbench, ein Blick unter die Haube

$ flashbench -f -o /root/sdtest/flashbench.out /dev/sdc
$ cat /root/sdtest/flashbench.out

4MiB    28.5M/s  28.3M/s  26.7M/s  27.6M/s  23.4M/s  28M/s
2MiB    26.6M/s  28.2M/s  26.7M/s  28.1M/s  26.8M/s  28.1M/s
1MiB    26.6M/s  28.8M/s  26.4M/s  28.4M/s  26.6M/s  27.3M/s
512KiB  26.7M/s  28.2M/s  26.9M/s  28.9M/s  26.8M/s  28.3M/s
256KiB  26.7M/s  28.2M/s  26.6M/s  27.8M/s  27M/s    28.8M/s
128KiB  26M/s    28.5M/s  26.7M/s  28.3M/s  26.8M/s  28.3M/s
64KiB   26.9M/s  28.8M/s  26.8M/s  28.3M/s  26.7M/s  27.9M/s
32KiB   12.6M/s  12.7M/s  13.1M/s  12.6M/s  12.9M/s  12.8M/s
16KiB   5.78M/s  5.91M/s  5.87M/s  5.82M/s  5.82M/s  5.86M/s

flashbench -f (find-fat) liest am Ende der ersten paar Erase-Blöcke zunehmend kleinere Häppchen und misst die Zeit dafür, auf der Suche nach dem Punkt, an dem die Lesegeschwindigkeit plötzlich einbricht. Die Grundidee: das deutet auf die interne Lese- beziehungsweise Page-Granularität des Flashs hin, weil das Lesen eines Teils einer internen Page oder eines Blocks genauso viel kosten kann wie das Lesen der ganzen Einheit, sub-granulare Reads verschwenden dann Bandbreite. Hier hält sich das Plateau, rund 26 bis 29 MB/s, passend zum rohen sequenziellen Lese-Benchmark, stabil bis hinunter zu 64 KiB, bricht dann bei 32 KiB auf weniger als die Hälfte ein (rund 12,6 bis 13,1 MB/s) und nochmal auf etwa ein Fünftel bei 16 KiB (rund 5,8 bis 5,9 MB/s). Ein sauberes, reproduzierbares Signal, 6 Wiederholungen pro Zeile, alle konsistent.

Die Schlussfolgerung bleibt bewusst vorsichtig formuliert: das ist ein Performance-Knick bei 64 KiB auf diesem konkreten Reader-Controller-Pfad, konsistent mit einer größeren internen Leseeinheit oder FTL-Gruppierung, aber keine direkte, verifizierte Messung der tatsächlichen physischen NAND-Page-Größe der Karte (typischerweise 8 bis 16 KiB, dafür bräuchte es Herstellerdokumentation oder eine andere Messmethode). Die Daten sind mit einer bestimmten Erklärung konsistent, sie beweisen sie nicht.

Schritt 7: fio, bleibt die Schreibrate konstant?

dd und f3write liefern Durchschnittswerte. fio mit einem Bandbreiten-Log pro Sekunde zeigt dagegen die Form des Schreibvorgangs über die vollen 6 GB, genau das will man sehen, wenn man einen Pseudo-SLC-Cache-Absturz sucht: viele Karten schreiben schnell in einen kleinen SLC-Modus-Puffer, bis der voll ist, und fallen danach auf eine deutlich niedrigere TLC- oder QLC-Dauerschreibrate ab.

$ fio --name=sdcard-write --filename=/dev/sdc --rw=write --bs=1M --size=6G --direct=1 --ioengine=psync --iodepth=1 --write_bw_log=/root/sdtest/fio_write --log_avg_msec=1000 --group_reporting

write: IOPS=26, BW=26.0MiB/s (27.3MB/s)(6144MiB/236179msec)
bw (KiB/s): min=24576, max=27675, per=100.00%, avg=26645.27, stdev=457.38, samples=236

Der Blick ins rohe Sekunden-Log lohnt sich, nicht nur die Zusammenfassung: 236 Einsekunden-Stichproben über den kompletten 6-GB-Schreibvorgang, alle im Bereich von 24576 bis 27675 KiB/s, also rund 24,0 bis 27,0 MB/s. Erste und letzte Stichprobe liegen im selben schmalen Band, kein erhöhter Burst am Anfang, kein Absturz später.

Verdikt: flach, kein erkennbarer SLC-Cache-Absturz auf dieser Karte. Auch hier lohnt sich die vorsichtige Formulierung: das beweist nicht, dass die Karte null Schreibpufferung hat, nur dass ein eventueller Puffer beziehungsweise Absturz innerhalb dieses 6-GB-Testfensters auf einer Karte mit nur rund 7,3 GB nutzbarer Gesamtkapazität nicht sichtbar wurde. Es könnte schlicht nicht genug Karte nach dem Puffer übrig sein, um einen Absturz zu zeigen, oder, wahrscheinlicher bei einer reinen Class-10/U1-Budgetkarte ohne A1/A2- oder „Extreme/Pro“-Einstufung, sie implementiert gar kein dynamisches SLC-Caching. So oder so steht das praktische Ergebnis: die Schreibleistung war über die gesamte Kapazität konsistent und vorhersagbar, nicht nach vorne verlagert.

Was für eMMC gilt, aber nicht für SD

Ein paar Dinge, die in diesem Testlauf nicht zum Einsatz kamen, aber der Vollständigkeit halber dazugehören. mmc extcsd read und seine Lebensdauer-Schätzfelder, das Nächste an einer echten Health-Prozentangabe in diesem Umfeld, sind eine eMMC-Eigenschaft. Eine wechselbare SD- oder microSD-Karte ist kein eMMC, dieser Pfad stand hier ohnehin nicht zur Verfügung, USB-Leser statt nativer MMC-Host. Ein nativer MMC-Host-Slot, etwa der eingebaute SD-Slot eines Laptops, würde dagegen mehr Identitätsdaten offenlegen als ein USB-Leser: /sys/block/mmcblkX/device/ mit cid, csd, scr, serial, manfid, name, oemid, preferred_erase_size und date, der Kernel parst die Identitätsregister der Karte direkt in sysfs, ganz ohne Zusatzwerkzeug. Wer also einen echten SD/MMC-Controller statt einer USB-SCSI-Bridge als Leser hat, bekommt das gratis dazu, in diesem Test war davon nichts erreichbar.

Und noch ein Missverständnis vorweg: der „SD Card Formatter“ der SD Association ist ein Formatierungs- und Reset-Werkzeug, das die werkseitige Partitionierung wiederherstellt, nachdem andere Betriebssysteme sie durcheinandergebracht haben, kein Health-Check-Werkzeug.

Die Reihenfolge, wenn du das nachmachen willst

Vom schnellsten und harmlosesten zum langsamsten und gründlichsten, ungefähr so, wie dieser Test auch abgelaufen ist:

  1. lsusb -v, udevadm info, dmesg. Leser und Karte identifizieren, Schreibschutz-Status vorab prüfen.
  2. smartctl -a -d sat und sdparm -a. Schneller, harmloser Versuch an Health- und Identitätsdaten, meist scheitert das über einen USB-Leser, und genau dieses Scheitern ist der Punkt, den man erklären sollte.
  3. f3probe. Schneller Kapazitätsbetrugs-Check, für 8 GB etwa 2 bis 3 Minuten, minimal destruktiv, stellt die benutzten Blöcke standardmäßig wieder her.
  4. f3write plus f3read. Der Haupttest, vollständige Schreib-Lese-Integritätsprüfung über die gesamte Kapazität, braucht ein Dateisystem.
  5. badblocks -w. Destruktive Vier-Muster-Prüfung auf defekte Blöcke, Rohgerät, kein Dateisystem nötig. Fängt Dinge, die f3 strukturell nicht kann, siehe Schritt 5.
  6. dd mit oflag/iflag=direct und/oder fio mit Bandbreiten-Log. Durchsatzzahlen und, mit fio, die Form der Schreibrate über die Zeit.
  7. flashbench -f -o DATEI. Optional, für die Neugier, Hinweise auf die interne Lese-Granularität.

Fazit

Für diese konkrete Karte: gesund, echt, keine Defekte gefunden, bei jedem Test, der überhaupt in der Lage gewesen wäre, welche zu finden. Keine SMART-Werte erreichbar über den USB-Leser, echte Kapazität ohne Cache-Trickserei, null Datenkorruption über 7,08 GB, null defekte Blöcke über alle 4 Muster, rund 23 bis 27 MB/s Schreiben und rund 85 bis 90 MB/s Lesen, konsistent über vier unabhängige Messmethoden, kein SLC-Cache-Absturz über die volle Kapazität. Die Karte reißt ihre aufgedruckte Class-10/U1-Angabe locker, für U3/V30 würde es nicht reichen, was sie auch gar nicht behauptet.

Der eigentliche Punkt reicht über diese eine Karte hinaus: ohne SMART bleibt nur Verhaltenstestung statt Register-Abfrage. Schreiben, lesen, vergleichen, und der Karte dabei zusehen, statt sie nach einer Zahl zu fragen, die sie gar nicht hat.

Siehe auch

Falls jemand einen zuverlässigeren Weg zur Health-Einschätzung einer SD-Karte unter Linux kennt, immer her damit. Und falls ihr selbst öfter mit fragwürdigen Billigkarten zu tun habt, dürft ihr mich zu dem Thema sehr gerne fragen.

BIOS erklärt, Teil 3: acht Kerne, sechzehn Threads und fünf Prefetcher

Ein Detail, das mich an dieser Maschine bis heute amüsiert: die Kiste, die hier steht, hat weniger Kerne als die, die sie ersetzt hat. Vorher waren es zwei Xeon E5-2687W v3, zusammen 20 Kerne und 40 Threads. Jetzt ist es ein einzelner Xeon Gold 5315Y mit 8 Kernen und 16 Threads. Auf dem Papier ein deutlicher Rückschritt, in der Praxis für fast alles was ich mache ein Fortschritt, weil jeder einzelne Kern erheblich mehr leistet und weil PCIe 4.0 und 256 GB Speicher am Ende mehr wert sind als zwölf zusätzliche langsame Kerne.

Womit wir bei der Seite wären, um die es heute geht. Das hier ist die dritte Folge meiner Reise durch das BIOS dieses Supermicro-Boards, alle Teile stehen gesammelt unter BIOS & Firmware. Dran ist Advanced > CPU Configuration, und zwar die obere Hälfte. Die untere mit AES-NI, SGX und den Sicherheitsfunktionen bekommt die nächste Folge, sonst wird es zu lang.

Der Teil zum Anschauen

Aptio Setup, Processor Configuration, mit Prozessorsignatur, Takt, Mikrocode-Stand und den Cache-Grössen

Oben auf der Seite steht ein Block, an dem man nichts einstellen kann. Trotzdem lohnt sich der Blick, weil dort Dinge stehen, die man sonst zusammensuchen müsste:

Processor BSP Revision          606A6 - ICX M1
Processor ID                    000606A6
Processor Frequency             3.200GHz
Processor Max Ratio             20H
Processor Min Ratio             08H
Microcode Revision              0D000421
L1 Cache RAM(Per Core)          80KB
L2 Cache RAM(Per Core)          1280KB
L3 Cache RAM(Per Package)       12288KB

606A6 ist die Prozessorsignatur, aufgedröselt: Familie 6, Modell 6A, Stepping 6. ICX M1 ist Intels interne Bezeichnung dafür, ICX steht für Ice Lake. Die beiden Ratios sind Multiplikatoren in hexadezimal, 08H sind 8 und 20H sind 32. Mit dem üblichen Referenztakt von 100 MHz ergibt das 800 MHz im Leerlauf und 3,2 GHz als Basistakt. Genau diese Spanne meldet auch Linux:

lscpu | grep MHz
#   CPU max MHz:  3600,0000
#   CPU min MHz:   800,0000

Die 3600 oben drüber sind der Turbo, der geht über den Basisratio hinaus. Dazu mehr, sobald ich das Power-Management-Menü nachgereicht bekomme, das fehlt mir in meiner Aufzeichnung noch.

Am interessantesten finde ich die Cache-Zeilen. 1280 KB L2 pro Kern klingt viel, und das ist es auch. Bei Ice Lake hat Intel den L2 gegenüber der Vorgängergeneration deutlich aufgebohrt und im Gegenzug den gemeinsamen L3 kleiner gemacht. 12 MB L3 für acht Kerne sind für einen Xeon nicht üppig. Der Gedanke dahinter ist, dass Daten näher am Kern liegen und seltener über den Ring wandern müssen. Ob das für die eigene Last aufgeht, merkt man erst beim Messen, aber die Zahlen im Setup erzählen die Designentscheidung ziemlich direkt.

Das Microcode Revision 0D000421 ist übrigens der Wert, den auch Linux meldet:

grep -m1 microcode /proc/cpuinfo
#   microcode : 0xd000421

Das BIOS bringt also bereits den Mikrocode mit, den auch das Betriebssystem laden würde. Wer sich fragt, ob ein BIOS-Update wegen Mikrocode nötig wäre: hier eindeutig nein.

Kerne abschalten, in hexadezimal

Aptio Setup, CPU1 Core Disable Bitmap, mit dem Hinweis dass mindestens ein Kern aktiv bleiben muss
> CPU1 Core Disable Bitmap

Ein eigenes Untermenü mit genau einem Eintrag, und dieser Eintrag ist herrlich unhöflich. Das Handbuch beschreibt ihn so:

Select 0 to enable all cores or FFFFFFFFFFF to disable all cores. One core must be enabled.

Man gibt also eine Bitmaske in hexadezimal ein, in der jedes gesetzte Bit einen Kern abschaltet. Keine Auswahlliste, keine Zahl „wie viele Kerne hätten Sie gern“, eine Bitmaske. Und dann der wunderbare Nachsatz, dass mindestens ein Kern übrig bleiben muss, was man vermutlich nicht schreiben würde, wenn es nicht mal jemand ausprobiert hätte.

Wozu macht man so etwas? Der häufigste Grund ist Lizenzkosten. Es gibt genug Software, die pro Kern abgerechnet wird, und wenn eine Maschine dafür zu viele hat, schaltet man welche ab. Der zweite Grund ist Testen: Verhalten einer Anwendung auf weniger Kernen prüfen, ohne die Hardware zu tauschen. Bei mir steht das erwartungsgemäss auf 0, alle 16 logischen Prozessoren sind da:

cat /sys/devices/system/cpu/online     # 0-15
cat /sys/devices/system/cpu/offline    # leer

Wichtig zu verstehen ist der Unterschied zum Abschalten unter Linux. Wenn ich dort einen Kern offline nehme, existiert er weiter, er bekommt nur keine Arbeit mehr. Die Bitmaske im BIOS blendet ihn aus, bevor das Betriebssystem überhaupt startet. Für eine Lizenzzählung, die sich ansieht was die Maschine meldet, ist das ein Unterschied.

Hyper-Threading

Hyper-Threading [ALL]           [Enable]

Der bekannteste Schalter der Seite. Aus acht physischen Kernen werden sechzehn logische, weil jeder Kern zwei Befehlsströme gleichzeitig verwaltet und die Recheneinheiten teilt, die gerade nichts zu tun haben. Der Gewinn liegt je nach Last irgendwo zwischen nichts und dreissig Prozent.

Es gibt drei gute Gründe, das abzuschalten, und keiner davon trifft auf mich zu. Der erste sind Seitenkanalangriffe: mehrere der Spectre-Nachfolger nutzen aus, dass sich zwei Threads einen Kern teilen, und wer fremden Code auf seiner Maschine ausführen lässt, fährt ohne Hyper-Threading sicherer. Der zweite ist Latenz: bei harten Echtzeitanforderungen ist ein Kern, der sich mit niemandem abstimmen muss, berechenbarer. Der dritte sind wieder Lizenzen.

Für eine Workstation, auf der ich weiss was läuft, überwiegt der Durchsatz. Bleibt an.

Die fünf Prefetcher

Aptio Setup, CPU Configuration, mit Hyper-Threading, den fünf Prefetchern und AES-NI

Und jetzt der Teil, der wirklich interessant ist:

Hardware Prefetcher             [Enable]
Adjacent Cache Prefetch         [Enable]
DCU Streamer Prefetcher         [Enable]
DCU IP Prefetcher               [Enable]
LLC Prefetch                    [Enable]

Fünf Zeilen, die aussehen wie eine Liste von Marketingbegriffen und in Wirklichkeit fünf verschiedene Stücke Hardware beschreiben. Alle haben dieselbe Aufgabe: raten, welche Daten die CPU als nächstes braucht, und sie schon mal holen. Speicher ist quälend langsam gegenüber dem Prozessor, ein Zugriff der bis zum RAM durchschlägt kostet ein Vielfaches dessen, was aus dem Cache kommt. Jeder erfolgreiche Rateversuch spart diese Wartezeit.

Sie raten nur unterschiedlich:

  • Hardware Prefetcher ist der Streaming-Prefetcher für den L2. Er erkennt, dass ein Programm Speicher der Reihe nach durchläuft, und holt vorausschauend nach. Der klassische Fall ist eine Schleife über ein grosses Array.
  • Adjacent Cache Prefetch ist der simpelste von allen. Er holt zu jeder angeforderten 64-Byte-Cachezeile gleich die benachbarte mit, arbeitet also faktisch in 128-Byte-Blöcken. Die Wette lautet: wer diese Zeile braucht, braucht gleich auch die daneben.
  • DCU Streamer Prefetcher macht dasselbe wie der erste, aber eine Ebene näher am Kern, für den L1-Datencache. DCU steht für Data Cache Unit.
  • DCU IP Prefetcher ist der cleverste. IP steht hier für Instruction Pointer. Er merkt sich pro Befehl, welche Zugriffsmuster dieser Befehl in der Vergangenheit erzeugt hat, und extrapoliert daraus. Damit erwischt er auch Muster mit konstantem Abstand, etwa jedes achte Element einer Struktur, an denen ein reiner Streaming-Prefetcher scheitert.
  • LLC Prefetch zieht Daten in den gemeinsamen L3, den Last Level Cache.

Das Handbuch beschreibt den DCU IP Prefetcher übrigens als etwas, das „IP addresses“ vorlädt und damit „network connectivity“ verbessert. Das ist schlicht falsch, da hat jemand Instruction Pointer mit Internet Protocol verwechselt und den Satz nie wieder gelesen. Solche Sätze stehen in erstaunlich vielen Handbüchern, und sie sind eine gute Erinnerung daran, dass auch offizielle Dokumentation nur von Menschen geschrieben wird.

Die Gegenprobe: was davon kommt wirklich an

Das Schöne an diesen fünf Schaltern ist, dass man sie nicht glauben muss. Sie landen in einem Prozessorregister, und das kann man auslesen. MSR 0x1A4 heisst Prefetch Control, und die unteren vier Bits schalten die Prefetcher ab, wenn sie gesetzt sind:

modprobe msr
rdmsr -0 0x1a4
#   0000000000000000

Null. Kein Bit gesetzt, also kein Prefetcher abgeschaltet. Das deckt sich exakt mit dem, was das Setup anzeigt. Die Zuordnung ist Bit 0 für den Hardware Prefetcher, Bit 1 für Adjacent Cache Line, Bit 2 für den DCU Streamer und Bit 3 für den DCU IP Prefetcher. Man beachte die umgekehrte Logik: gesetztes Bit bedeutet aus.

Ich mag solche Gegenproben. Ein Screenshot zeigt, was das Setup behauptet. Das Register zeigt, was die CPU tatsächlich macht. Bei Firmware ist das nicht immer dasselbe.

Wann man daran drehen würde

Fast nie, ehrlich gesagt. Die Prefetcher sind für allgemeine Lasten deutlich im Plus, sonst hätte Intel sie nicht eingebaut. Es gibt aber Fälle, in denen sie schaden:

Bei Datenbanken und Graphen-Workloads mit stark zufälligem Zugriffsmuster liegt der Prefetcher regelmässig daneben, holt Daten die keiner braucht, und verdrängt dabei Daten die jemand gebraucht hätte. Bei Echtzeitanwendungen sind sie eine Jitter-Quelle, weil sie Speicherbandbreite zu unvorhersehbaren Zeitpunkten verbrauchen. Und bei manchen HPC-Codes, die ihre Zugriffe selbst sehr genau steuern, stört der Prefetcher mehr als er hilft.

Der entscheidende Punkt: das misst man, das rät man nicht. Und der grosse Vorteil des MSR ist, dass man dafür gar nicht ins BIOS muss. Man kann die Bits im laufenden System setzen, messen, und wieder zurücksetzen. Wer das ernsthaft ausprobiert, sollte allerdings wissen, dass wrmsr genau so gefährlich ist wie es klingt, und dass die Einstellung pro logischem Prozessor gilt.

Nächste Folge: die untere Hälfte derselben Seite. AES-NI, TME, SGX und ein Schalter namens Extended APIC, der aussieht wie eine vergessene Optimierung und keine ist.

Siehe auch

Hat jemand von euch schon mal Prefetcher gezielt abgeschaltet und dabei etwas gemessen, das sich gelohnt hat? Das würde mich wirklich interessieren, ihr dürft mich jederzeit fragen.

BIOS erklärt, Teil 2: Boot Feature, oder was ein Interrupt von 1981 hier noch tut

Der erste Start der neuen Maschine war laut. Nicht akustisch, sondern optisch: eine Wand aus weissem Text auf schwarzem Grund, Speichertest, Controller die sich melden, Netzwerkkarten die ihre MAC-Adressen ausspucken, und das alles über mehrere Bildschirmseiten. Bei einem Desktop-Board wäre da ein Herstellerlogo gewesen und darunter vielleicht ein dezenter Fortschrittsbalken. Ich habe die Textwand gelassen wie sie ist, und der Grund dafür steht in den ersten beiden Zeilen des Menüs, um das es heute geht.

Das hier ist Teil 2 der Serie, in der ich das BIOS meines Supermicro-Boards Stück für Stück durchgehe. Die bisherigen Folgen findest du gesammelt in der Kategorie BIOS & Firmware. Heute: Boot Feature, die erste Seite unter Advanced, neun Einstellungen rund um das Einschalten.

Die Anzeige, oder: warum ich kein Logo will

Aptio Setup, Seite Boot Feature, mit Quiet Boot, Wait for F1, INT19 Trap Response, Watchdog und Restore on AC Power Loss
Quiet Boot                        [Disabled]
Option ROM Messages               [Force BIOS]
Bootup NumLock State              [On]

Quiet Boot ist der Schalter für genau die Textwand von oben. Auf Enabled zeigt das Board beim Start ein Logo, auf Disabled die POST-Meldungen. Das Logo ist hübscher, aber es versteckt genau die Informationen, die man braucht wenn etwas nicht stimmt. Auf einer Maschine mit vier Netzwerkports, einem Beschleuniger und einer NVMe an einem Breakout-Kabel möchte ich beim Booten sehen, wer sich meldet und wer nicht. Das kostet mich drei Sekunden Wartezeit und hat mir schon zweimal eine Fehlersuche erspart.

Option ROM Messages ist der kleine Bruder davon. Steckkarten bringen eigene Firmware mit, das Option ROM, und die möchte beim Start etwas ausgeben. Force BIOS heisst: das BIOS bestimmt, wie diese Ausgaben aussehen und dass sie überhaupt erscheinen. Keep Current überlässt das der Karte. Auch hier gilt, ich möchte die Meldungen sehen.

Bootup NumLock State ist der harmloseste Schalter des ganzen Setups und trotzdem einer, über den sich Menschen erstaunlich zuverlässig streiten. Er legt fest, ob der Ziffernblock nach dem Einschalten an ist. Bei mir: an.

Wait For „F1“ If Error

Wait For "F1" If Error            [Disabled]

Das ist der Klassiker unter den Server-Fallstricken. Steht der Schalter auf Enabled und beim Start tritt irgendein Fehler auf, dann bleibt die Maschine stehen und wartet darauf, dass jemand F1 drückt. Bei einem Rechner unter dem Schreibtisch ist das ärgerlich. Bei einem Server im Rechenzentrum ist es der Grund, warum man nachts hinfährt.

Der Fehler muss dabei nicht dramatisch sein. Eine leere CMOS-Batterie reicht, oder eine Konfigurationsänderung, die das BIOS für erwähnenswert hält. Auf Disabled protokolliert das Board den Fehler und bootet trotzdem weiter. Für alles ohne Tastatur davor ist das die einzig sinnvolle Einstellung. Wer den Rechner täglich vor sich hat, kann anderer Meinung sein, dann merkt man wenigstens sofort dass etwas war.

Der Interrupt aus dem Jahr 1981

INT19 Trap Response               [Immediate]

Und hier wird es schön. Das Handbuch erklärt den Schalter so:

Interrupt 19 is the software interrupt that handles the boot disk function.

Interrupt 19h, genauer INT 19h, ist der Bootstrap Loader aus dem BIOS des originalen IBM PC von 1981. Der Ablauf war denkbar einfach: das BIOS beendet seinen Selbsttest und ruft INT 19h auf. Was dahinter liegt, lädt den ersten Sektor eines Laufwerks und übergibt die Kontrolle dorthin. Das war der komplette Bootvorgang.

Interessant wird es durch das, was Steckkarten daraus gemacht haben. Ein SCSI-Controller mit eigenem Option ROM konnte sich in INT 19h einhängen, den Aufruf abfangen und stattdessen von seiner eigenen Platte booten. Genau so wurden Karten bootfähig, ohne dass das Mainboard-BIOS je von ihnen gehört hatte. Immediate heisst, die Karte darf sich den Interrupt sofort greifen. Postponed heisst, sie muss warten bis das System-BIOS fertig ist.

Meine Maschine bootet im reinen UEFI-Modus, ohne CSM, und lädt einen EFI-Bootloader von der NVMe. INT 19h spielt dabei überhaupt keine Rolle mehr. Der Schalter ist trotzdem noch da, weil irgendwo auf der Welt noch eine Karte steckt, die ihn braucht. Ich mag solche Fundstücke. Man scrollt durch ein Setup von 2025 und stolpert über einen Mechanismus, der älter ist als die meisten Leute, die ihn konfigurieren.

Neustart bei Fehlschlag, und der Watchdog

Re-try Boot                       [Disabled]
Watch Dog Function                [Disabled]

Re-try Boot startet die Maschine automatisch neu, wenn der erste Bootversuch fehlschlägt. Klingt hilfreich, ist es in einer Endlosschleife aber nicht. Wenn die Bootplatte weg ist, dann ist sie auch beim vierzigsten Versuch weg, und ich bekomme statt einer klaren Fehlermeldung eine Maschine die im Sekundentakt neu startet. Auf Disabled bleibt sie stehen und sagt mir was los ist. Wer eine Maschine an einem Ort betreibt, an den er schlecht hinkommt, wägt das anders ab.

Der Watchdog ist ein anderes Kaliber. Laut Handbuch löst er nach fünf Minuten aus und macht dann wahlweise einen Reset oder einen NMI, also einen nicht maskierbaren Interrupt. Der Sinn dahinter: eine Software auf dem laufenden System muss den Timer regelmässig zurücksetzen. Passiert das nicht mehr, weil das System hängt, greift der Watchdog ein.

Der Haken ist der zweite Halbsatz. Es braucht diese Software. Ohne einen Dienst, der den Timer füttert, hat man einen Timer der nach fünf Minuten unangekündigt die Reset-Leitung zieht, und das ist keine Verfügbarkeitsmassnahme sondern eine Zeitbombe. Unter Linux gäbe es dafür watchdogd, und wenn ich das eines Tages sauber einrichte, schalte ich den Schalter an. Vorher nicht.

Zwei Schalter, die ich gar nicht sehe

Im Handbuch stehen an dieser Stelle noch Front USB Port(s) und Rear USB Port(s), mit denen sich die USB-Anschlüsse einzeln abschalten lassen. Auf einem Rechner an einem öffentlich zugänglichen Ort ist das eine ernstzunehmende Massnahme. In meinem Setup tauchen die beiden Zeilen nicht auf, und das Handbuch sagt auch warum:

The next two features are available for configuration if the SFT-DCMS-SINGLE license is installed.

Wer Teil 1 gelesen hat, kennt die Lizenz schon. Sie ist der Grund, warum ich meine BIOS-Konfiguration nicht maschinell auslesen kann. Dass sie zusätzlich zwei Sicherheitseinstellungen im Setup versteckt, hatte ich nicht erwartet. Das ist kein Management-Feature für Flottenbetreiber mehr, das ist eine Funktion des Boards, das ich gekauft habe, hinter einer Bezahlschranke im eigenen Setup.

Nach dem Stromausfall bleibt sie aus

Restore on AC Power Loss          [Stay Off]
Power Button Function             [4 Seconds Override]

Drei Möglichkeiten gibt es: Stay Off, Power On und Last State. Der Werksdefault ist Last State, also der Zustand von vor dem Stromausfall. Bei mir steht es auf Stay Off, und das ist eine bewusste Entscheidung gegen den naheliegenden Reflex.

Der Reflex sagt: klar soll die Maschine wieder hochkommen, sonst steht sie nach einem Stromausfall im Urlaub zwei Wochen tot herum. Für einen Server stimmt das auch. Für meine Workstation nicht. Wenn hier der Strom weg war, dann ist etwas passiert, und ich möchte selbst entscheiden wann und ob die Kiste wieder angeht. Dazu kommt der Fall, den man leicht übersieht: ein flatternder Strom, der mehrfach hintereinander kommt und geht. Mit Power On fährt das Board dann jedes Mal wieder an, mitten in den nächsten Ausfall hinein. Ein Rechner der aus ist, kann nicht kaputtgehen.

Beim Power Button Function habe ich 4 Seconds Override gelassen. Kurz drücken meldet dem Betriebssystem einen Shutdown-Wunsch, den es sauber abarbeitet. Vier Sekunden halten schneidet den Strom hart ab. Die Alternative Instant Off macht das sofort, ohne Rückfrage, und dafür sehe ich keinen Grund. Ein versehentlicher Stups ans Gehäuse soll keine ungespeicherte Arbeit kosten.

Fazit

Neun Schalter, davon acht auf Default und einer bewusst geändert. Das ist ungefähr die Quote, die sich durch das ganze Setup zieht, und sie ist auch in Ordnung so. Interessant sind trotzdem alle neun, weil hinter jedem eine Entscheidung steckt, die jemand mal getroffen hat.

Falls du beim Watchdog oder bei INT 19h anderer Meinung bist oder ich etwas verkürzt dargestellt habe: immer her damit. Besonders bei INT 19h würde mich interessieren, ob jemand noch aktiv Hardware betreibt, bei der Postponed einen Unterschied macht. Ich habe keine gefunden.

Nächste Folge: die CPU. Kerne, Threads und die fünf Prefetcher, von denen die Hälfte Namen trägt, die nach Marketing klingen und es nicht sind.

Siehe auch

Wie steht bei dir Restore on AC Power Loss, und aus welchem Grund? Schreib es mir gerne, ihr dürft mich jederzeit fragen.

Sieben Einstellungen, kein Setup-Besuch: die BIOS-Konfiguration meines Supermicro-Boards ändern

Im letzten Beitrag ging es darum, die BIOS-Einstellungen meines Supermicro X12SPi-TF überhaupt erst als Datei in die Hand zu bekommen. Das Ergebnis war eine XML mit 372 Einstellungen, und der Weg dorthin führte über eine Lizenz für 28,17 Euro.

Zwei Terminalausgaben untereinander: mit Hardware P-States auf Disable liefert die Suche nach dem hwp-Flag in /proc/cpuinfo keine Ausgabe und intel_pstate läuft passiv, mit Native Mode erscheinen hwp, hwp_epp und hwp_act_window und intel_pstate läuft aktiv.

Damit war die halbe Arbeit getan. Eine Konfiguration auslesen zu können ist schön, aber der eigentliche Gewinn liegt darin, sie auch zurückschreiben zu können. Genau das habe ich jetzt gemacht, und zwar zum ersten Mal überhaupt an dieser Maschine: sieben Einstellungen geändert, ohne den Rechner ins Setup zu booten, ohne Tastatur, ohne die Fernkonsole des Management-Controllers.

Vorweg die gute Nachricht für alle, die schon rechnen: die kleine Lizenz deckt das Schreiben mit ab. Ich hatte damit gerechnet, dass Supermicro genau an dieser Stelle noch einmal die Hand aufhält, und das tut es auch, aber nur an einer sehr kleinen Ecke. Von den 372 Einstellungen tragen 21 den Vermerk, dass sie die große Lizenz verlangen. Es sind ausschliesslich Zertifikatseinträge für KMIP-Server und HTTPS-Boot. Nichts davon betrifft normale Setup-Optionen.

Was ich ändern wollte, und warum

Der langweiligste Teil eines solchen Artikels ist die Liste der geänderten Werte. Der interessante Teil ist die Begründung, deshalb fange ich damit an.

Mein Rechner bootet von einer NVMe-SSD. Er hat das noch nie anders gemacht und wird es auch nicht. Trotzdem lädt das BIOS bei jedem Start die Pre-Boot-Netzwerktreiber für vier Netzwerkanschlüsse, jeweils für IPv4 und IPv6, und baut daraus Boot-Einträge, die niemand benutzt. Dazu kommen die Option-ROMs von drei leeren Steckplätzen.

Ein Option-ROM ist ein kleines Stück Firmware auf einer Steckkarte, das beim Start ins BIOS geladen wird, damit die Karte schon vor dem Betriebssystem funktioniert. Für eine Netzwerkkarte heisst das Netzwerk-Boot, für einen Speichercontroller das Booten von daran angeschlossenen Platten. Wer von keinem dieser Geräte bootet, braucht den Code nicht.

Also:

Network Stack von *Enabled* auf *Disabled*. Damit fällt die komplette UEFI-Netzwerkinitialisierung weg. – Onboard LAN1 Option ROM aus. Der Pre-Boot-Treiber der onboard X550 wird nicht gebraucht. – CPU SLOT6 OPROM aus. Dort steckt meine X710. Dazu gleich mehr, das ist der eigentliche Grund für die Aktion. – CPU SLOT1, SLOT2 und SLOT7 OPROM aus. Alle drei Steckplätze sind leer.

Bei den drei leeren Steckplätzen bin ich ehrlich: der Zeitgewinn ist exakt null. Wo keine Karte steckt, gibt es auch kein Option-ROM zu laden. Ich habe sie trotzdem mitgenommen, weil ich einen dokumentierten Sollzustand haben will und nicht eine Mischung aus bewusst gesetzt und zufällig übrig.

Die siebte Änderung ist die, um die es mir wirklich ging.

Die eine Zeile, die eine Woche Rätselraten beendet hat

Beim Schreiben meiner BIOS-Serie ist mir etwas aufgefallen, das ich mir lange nicht erklären konnte. Mein Prozessor ist ein Xeon Gold 5315Y, Ice Lake-SP. Diese Generation beherrscht Hardware P-States, kurz HWP. Die CPU regelt ihren Takt dabei selbst, in feineren Stufen und deutlich schneller, als das Betriebssystem es könnte.

Nur meldete mein Kernel davon nichts. In /proc/cpuinfo fehlte das hwp-Flag, der Treiber intel_pstate lief im passiven Modus, und die Taktregelung machte der Governor schedutil. Also genau der Zustand, den man auf einer CPU erwartet, die HWP gar nicht kann.

Ich habe eine Weile in Richtung Kernel gesucht. Das war die falsche Richtung. Der Schalter sass im BIOS, ziemlich tief vergraben:

Advanced > CPU Configuration > Advanced Power Management Configuration
        > Hardware PM State Control > Hardware P-States   [Disable]

Der Auslieferungszustand dieser Option ist tatsächlich *Disable*. Es gibt vier Werte, und der Hilfetext im BIOS beschreibt sie so:

Disable                              hardware will choose a P-state setting for
                                     the system based on an OS request
Native Mode                          hardware will choose a P-state setting
                                     based on OS guidance
Native Mode with No Legacy Support   hardware will choose a P-state setting
                                     independently without OS guidance
Out of Band Mode                     hardware autonomously choose a P-state
                                     without OS guidance

Ich habe Native Mode genommen, also die zahmste der drei aktiven Varianten. Der Unterschied zu den beiden anderen ist wichtig: bei *Native Mode* behält das Betriebssystem seinen Einfluss, es wandert nur die Feinsteuerung in die CPU. Bei den beiden anderen wird der Kernel bei der Taktwahl übergangen, und dann kann er dir auch nicht mehr sinnvoll sagen, was die CPU gerade tut.

Was ich bewusst nicht angefasst habe

Diese Liste finde ich wichtiger als die Liste der Änderungen, denn hier hätte ich mir den Rechner unbedienbar machen können.

Das Option-ROM von SLOT4 bleibt an. Dort sitzt die Grafikkarte. Ihr GOP-Modul ist das, was beim Start überhaupt ein Bild auf den Monitor bringt. Wer es abschaltet, sitzt bis zum Laden des Grafiktreibers im Dunkeln, und wenn dabei etwas schiefgeht, sitzt er dauerhaft im Dunkeln.

Onboard Video Option ROM bleibt an. Das ist die Grafik des Management-Controllers, und daran hängt die Fernkonsole. Schaltet man sie ab, bleibt beim Start das Fenster schwarz, über das man aus der Ferne ins Setup kommt. Das ist genau der Rettungsweg, den man sich nicht abschneiden will, wenn man gerade anfängt, das BIOS aus dem laufenden Betrieb heraus umzukonfigurieren.

Beide NVMe-Option-ROMs bleiben an. Das erste trägt das Betriebssystem. Auf das zweite soll irgendwann Windows, und auch wenn der Bootloader auf der ersten Platte das Kommando behält, muss die zweite dem Firmware-Bootmanager sichtbar bleiben.

Legacy USB Support bleibt an. Hier hätte ich vermutlich etwas Startzeit gewinnen können. Ich habe es gelassen, weil ich den Gewinn nicht gemessen habe und das Risiko eine tote Tastatur im Setup wäre. Eine Einstellung, deren Nutzen man nicht beziffern kann, deren Schaden aber konkret ist, ändert man nicht.

Der Weg: eine Teildatei, nicht die ganze

Naheliegend wäre, die ausgelesene XML zu bearbeiten und komplett zurückzuschreiben. Bei 372 Einstellungen und 260 Kilobyte ist das unnötig riskant, und es ist auch nicht der vorgesehene Weg.

Das Werkzeug kennt einen Filter, der genau die Einstellungen ausgibt, die vom Auslieferungszustand abweichen:

./saa -c GetCurrentBiosCfg --file nondefault.xml --overwrite --filter nondefault

Heraus kommt eine kleine Datei mit derselben Struktur wie die grosse, nur eben mit den Menüzweigen, die auch wirklich etwas enthalten. Und genau diese Struktur nimmt das Schreibkommando entgegen. Teildateien sind also kein Trick, sondern der normale Betriebsfall.

Hier der erste Fallstrick, und der hat mich ehrlich geärgert: die eingebaute Hilfe des Werkzeugs beschreibt den Filter als Ziffer.

--filter    Sets filter type to: 1 = nondefault

Die Ziffer 1 wird abgewiesen. Das Kommando bricht ab und wirft den Hilfetext aus, der einem gerade die Ziffer empfohlen hat. Akzeptiert wird ausschliesslich das ausgeschriebene Wort nondefault. Wenn du also an dieser Stelle hängst, liegt es nicht an dir.

Meine Änderungsdatei habe ich nicht getippt, sondern mit einem kleinen Python-Skript aus dem Auslesestand erzeugt. Das klingt nach Übertreibung für sieben Werte, hat aber einen handfesten Grund: die Einstellungen müssen im richtigen Menüzweig stehen, und die Menünamen sind lang und fehleranfällig. CPU SLOT2 PCI-E 4.0 X8(IN X16) OPROM vertippt sich schneller, als man denkt, und ein Tippfehler im Namen führt nicht zu einer Fehlermeldung, sondern dazu, dass die Einstellung stillschweigend nicht gesetzt wird.

Das Schreiben selbst ist unspektakulär:

./saa -c ChangeBiosCfg --file change-boot-oprom-hwp.xml

Status: The BIOS configuration is updated for the managed system
Note: You have to reboot or power up the system for the changes to take effect.

Es gibt eine Option --reboot, die den Neustart gleich mit erledigt. Die habe ich weggelassen. Wann ein Rechner neu startet, entscheide ich lieber selbst.

Der zweite Fallstrick, und der ist gemein

Nach dem Schreiben will man natürlich nachsehen, ob es geklappt hat. Also dasselbe Auslesekommando noch einmal, und dann steht da:

Hardware P-States              = Disable     (geschrieben: Native Mode)
Network Stack                  = Enabled     (geschrieben: Disabled)
CPU SLOT6 PCI-E 4.0 X16 OPROM  = EFI         (geschrieben: Disabled)

Nichts. Alle alten Werte, unverändert.

Der erste Reflex ist, das Schreibkommando noch einmal laufen zu lassen. Tu das nicht. Das Auslesen liefert die aktive Konfiguration, und die ändert sich erst beim nächsten Start. Deine Änderung liegt so lange in einem Bereich, den das Werkzeug nicht anzeigt. Zwischen dem Schreiben und dem Neustart gibt es damit keine Möglichkeit, die Änderung zu überprüfen, ausser dem Rückgabewert des Kommandos.

Das ist unschön, aber es ist logisch. Die Einstellungen werden erst beim Start aus dem Speicher gelesen, und vorher gibt es schlicht nichts Aktives, das anders wäre.

Nach dem Neustart

Und dann stimmt alles. Die Zahl der Abweichungen vom Auslieferungszustand ist von sechs auf dreizehn gestiegen, also genau um meine sieben:

Quiet Boot                            Unchecked
Restore on AC Power Loss              Stay Off
Power Button Function                 4 Seconds Override
Hardware P-States                     Native Mode
Network Stack                         Disabled
Re-Size BAR Support                   Enabled
VGA Priority                          Offboard
CPU SLOT1 PCI-E 4.0 X8 OPROM          Disabled
CPU SLOT2 PCI-E 4.0 X8(IN X16) OPROM  Disabled
CPU SLOT6 PCI-E 4.0 X16 OPROM         Disabled
CPU SLOT7 PCI-E 4.0 X8 OPROM          Disabled
Onboard LAN1 Option ROM               Disabled
WHEA Support                          Disabled

Die Netzwerk-Booteinträge sind verschwunden, übrig bleibt der Bootloader und die eingebaute Shell:

BootCurrent: 000F
BootOrder: 000F,0001
Boot0001* UEFI: Built-in EFI Shell
Boot000F* ubuntu

Und der Punkt, um den es mir ging, ist eingetreten. Der Prozessor meldet jetzt Fähigkeiten, die er die ganze Zeit hatte:

$ grep -o ' hwp[_a-z]*' /proc/cpuinfo | sort -u
 hwp
 hwp_act_window
 hwp_epp
 hwp_pkg_req

$ cat /sys/devices/system/cpu/intel_pstate/status
active

Der Treiber ist damit vom passiven in den aktiven Modus gewechselt. Was dabei kurz irritiert: der Governor heisst jetzt powersave statt schedutil, und das sieht nach einem Rückschritt aus. Ist es nicht. Bei intel_pstate im aktiven Modus ist powersave der Name für den Betrieb, bei dem die Hardware regelt. Wie sie das tut, steuert ein separater Wert:

$ cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
balance_performance

Die CPU geht im Leerlauf auf 800 MHz und hat den vollen Turbo bis 3600 MHz zur Verfügung. Genau so soll es sein.

Wichtig war mir noch die Gegenprobe, dass ich mir das Netzwerk nicht zerschossen habe. Die Karte, deren Option-ROM ich abgeschaltet habe, ist schliesslich die, über die diese Maschine am Netz hängt:

$ ethtool ens6f1np1 | grep -E 'Speed|Link detected'
	Speed: 10000Mb/s
	Link detected: yes

Unverändert. Das ist auch zu erwarten, denn der Treiber im laufenden Betrieb kommt aus dem Kernel und hat mit dem Option-ROM nichts zu tun. Das Option-ROM ist reiner Startcode. Trotzdem ist das der Punkt, an dem die meisten zögern, deshalb steht die Messung hier.

Ein Punkt ist noch offen, und den will ich nicht verschweigen: der ursprüngliche Anlass für das Abschalten von SLOT6 war eine Meldung auf der Treiberstatus-Seite im Setup, wo meine Netzwerkkarte seit einem Firmware-Update als *Failed* geführt wird. Ob die Meldung jetzt weg ist, kann ich von aussen nicht sehen. Das prüfe ich beim nächsten Setup-Besuch nach.

Gibt es dafür keine Oberfläche?

Diese Frage lag für mich die ganze Zeit im Raum, und die Antwort ist unbefriedigend.

Es gäbe einen herstellerneutralen Weg unter Linux. Der Kernel kennt eine Geräteklasse namens firmware-attributes, über die sich BIOS-Einstellungen als ganz normale Dateien lesen und schreiben lassen. Der Firmware-Updater fwupd kann das seit Version 1.8.4 direkt bedienen. Das wäre die Lösung: ein Kommando, egal welcher Hersteller.

Auf meiner Maschine sieht das so aus:

$ ls /sys/class/firmware-attributes/
ls: cannot access '/sys/class/firmware-attributes/': No such file or directory

$ fwupdmgr get-bios-setting
This system doesn't support firmware settings

Das Hilfsmodul dafür liegt im Kernel, es ist also nicht so, dass die Infrastruktur fehlen würde:

/lib/modules/7.0.0-28-generic/kernel/drivers/platform/x86/firmware_attributes_class.ko.zst

Was fehlt, ist der Treiber des Herstellers. Der Kernel bringt genau drei mit:

drivers/platform/x86/dell/dell-wmi-sysman     Dell
drivers/platform/x86/lenovo/think-lmi         Lenovo
drivers/platform/x86/hp/hp-bioscfg            HP

Supermicro ist nicht dabei. Statt eines Treibers, der diese Kernel-Schnittstelle bedient, gibt es ein eigenes Werkzeug und eine Lizenz. Auf einem Dell oder Lenovo wäre der Inhalt dieses Beitrags ein Einzeiler ohne Zusatzkosten.

Ganz ohne Oberfläche ist man aber nicht. Das Werkzeug bringt einen textbasierten Editor mit, der das Setup im Terminal nachbaut:

./saa -c GetCurrentBiosCfg --tui

Dazu gibt es --compact, das anschliessend nur die geänderten Zeilen herausschreibt. Über eine SSH-Pipe blieb das Fenster bei mir schwarz, es braucht ein echtes Terminal. Ausprobiert habe ich es deshalb noch nicht richtig. Und es gibt das Werkzeug ausserdem als Windows-Version und als Variante für die UEFI-Shell, also für den Fall, dass überhaupt kein Betriebssystem installiert ist.

Eine echte grafische Oberfläche gibt es auch, sie heisst SSM. Die will dann allerdings wieder die grosse Lizenz.

Zurück auf Anfang

Das Wichtigste zum Schluss, und man legt es sich besser vorher zurecht als hinterher. Vor dem Schreiben habe ich den vollständigen Stand weggesichert. Der Rückweg ist dann dasselbe Kommando mit dieser Datei:

./saa -c ChangeBiosCfg --file bios-current-2026-08-09.xml

Als Notnagel gibt es darunter noch LoadDefaultBiosCfg, das alles auf den Auslieferungszustand zurücksetzt. Das ist aber ein grober Hebel: es wirft auch die sechs Abweichungen weg, die ich absichtlich eingestellt habe, und die stehen dann eben nicht mehr so, wie ich sie haben will. Die Teildatei ist der bessere Weg, der Vollreset die letzte Reserve.

Und weil es hier um Firmware geht, der offensichtliche Hinweis: bei einem Rechner, an den du nicht rankommst, änderst du keine Boot-Einstellungen, ohne dass jemand vor Ort ist oder die Fernkonsole zuverlässig läuft. Ich habe das Option-ROM der Grafik und das des Management-Controllers genau deshalb nicht angefasst.

Was bleibt

Sieben Einstellungen, ein Neustart, kein Setup-Besuch. Das klingt nach wenig, ändert aber die Arbeitsweise. BIOS-Einstellungen waren für mich bisher etwas, das man beim Aufbau einmal einstellt und danach ungern anfasst, weil jede Änderung einen Neustart mit Tastatur und Monitor bedeutet. Jetzt sind sie eine Datei, die ich versionieren, vergleichen und zurückspielen kann.

Der schönste Nebeneffekt war die Sache mit den Hardware P-States. Ich hatte das Verhalten meiner CPU wochenlang für eine Eigenheit des Kernels gehalten und in die falsche Richtung gesucht. Es war eine einzige Zeile in einem Menü, das vier Ebenen tief liegt und das ich beim Durchklicken nie geöffnet hatte. Eine durchsuchbare Datei aller 372 Einstellungen hätte mir das in dreissig Sekunden gezeigt.

Falls du auf demselben Board sitzt: das Auslesen ist der Beitrag davor, das Schreiben braucht keine weitere Lizenz, und die drei Stolperstellen sind der Filter, der nur als Wort funktioniert, die Gegenprobe, die vor dem Neustart nichts zeigt, und die Versuchung, mehr auf einmal zu ändern, als man beim nächsten Start noch reparieren kann.

Siehe auch: Achtundzwanzig Euro für eine Textdatei: die BIOS-Konfiguration meines Supermicro-Boards auslesen und die Serie BIOS erklärt.

Wenn dazu etwas unklar ist oder du es auf deinem Board anders erlebt hast, dürft ihr mich sehr gerne fragen.

Achtundzwanzig Euro für eine Textdatei: die BIOS-Konfiguration meines Supermicro-Boards auslesen

In meiner Serie BIOS erklärt arbeite ich mich durch das Setup eines Supermicro X12SPi-TF. Die Grundlage dafür war bisher eine Bildschirmaufnahme: ich bin durch alle Menüs gelaufen, habe das Video in Einzelbilder zerlegt und die Werte abgetippt. Das funktioniert, ist aber Handarbeit, und Handarbeit übersieht Dinge.

Zwei Terminalausgaben untereinander: ohne Lizenz bricht das Kommando saa mit ExitCode 80 ab, mit der OOB-Lizenz erzeugt dasselbe Kommando eine XML-Datei mit 372 Einstellungen.

Was ich eigentlich wollte, ist eine Datei. Eine Liste aller Optionen mit ihrem aktuellen Wert, maschinenlesbar, damit ich sie durchsuchen und mit einem späteren Stand vergleichen kann. Das Board kann das. Es rückt es nur nicht heraus.

Drei Wege, drei Absagen

Der erste Weg führt über Redfish, die HTTP-Schnittstelle des Management-Controllers. Der passende Endpunkt existiert, antwortet aber nicht mit Daten:

GET /redfish/v1/Systems/1/Bios
HTTP 403

"MessageId": "SMC.1.0.OemLicenseNotPassed",
"Message": "Not licensed to perform this request. The following licenses DCMS were needed"

Der zweite Weg ist Supermicros eigenes Kommandozeilenwerkzeug. Es heißt inzwischen SAA, SuperServer Automation Assistant, und liegt dem BIOS-Paket bei. Es kann genau das, was ich suche, und arbeitet dabei nicht über das Netz, sondern direkt über die Firmware:

./saa -c GetCurrentBiosCfg --file bios-current.xml
ExitCode 80
Node product key is not activated.
One of the node product key (SFT-OOB-LIC or SFT-DCMS-SINGLE) should be activated

Der dritte Weg wäre, die UEFI-Variablen selbst zu lesen. Die liegen unter /sys/firmware/efi/efivars/, und tatsächlich gibt es dort Einträge, die nach BIOS-Einstellungen aussehen. Nur sind das undurchsichtige Datenblöcke ohne die Zuordnungstabelle des Boards. Man sieht Bytes, aber man weiß nicht, welches Byte welcher Menüpunkt ist.

Drei unabhängige Wege, dieselbe Sperre. An dieser Stelle habe ich in meinen Notizen etwas geschrieben, das sich später als falsch herausstellte. Dazu komme ich am Ende.

Zwei Lizenzen, und sie sind nicht dasselbe

Lies die beiden Fehlermeldungen oben noch einmal nebeneinander. Sie verlangen nicht das Gleiche. Redfish nennt namentlich DCMS. SAA nennt SFT-OOB-LIC oder SFT-DCMS-SINGLE, also eine von zweien.

Das ist der Punkt, an dem man leicht zu viel Geld ausgibt. Supermicro verkauft für diese Board-Generation zwei Lizenzen. Die kleine heißt Out of Band, kurz OOB. Die große heißt DCMS und kostet etwa das Fünffache. Wer nur die Fehlermeldung von Redfish liest, kauft DCMS. Wer die Fehlermeldung von SAA liest, merkt, dass es für seinen Zweck auch die kleine tut.

Nebenbei taucht dieselbe Lizenz an einer Stelle auf, die ich nicht erwartet hätte. Im Handbuch zum Board stehen bei zwei Sicherheitseinstellungen, nämlich beim Abschalten der vorderen und der hinteren USB-Anschlüsse, die Worte *available for configuration if the SFT-DCMS-SINGLE license is installed*. Es fehlen also nicht nur Management-Schnittstellen. Es fehlen zwei Menüpunkte im Setup selbst.

Warum man den Schlüssel nicht selbst baut

Bei dieser Recherche stößt man unweigerlich auf ein Werkzeug auf GitHub, das Supermicro- Produktschlüssel behandelt. Für die Generationen 9 bis 11 kann es welche erzeugen, denn dort war der Schlüssel ein berechneter Wert über die MAC-Adresse des Management-Controllers.

Für die zwölfte Generation, also mein Board, kann es das nicht, und das Projekt schreibt es selbst deutlich hin: dieses Format lässt sich mit dem Werkzeug prüfen, aber nicht erzeugen. Der Grund ist, dass Supermicro umgestellt hat. Der Schlüssel ist heute eine von Supermicro signierte Datei, und der Management-Controller prüft die Signatur gegen einen öffentlichen Schlüssel in seiner eigenen Firmware. Ohne den privaten Schlüssel des Herstellers entsteht da nichts Gültiges.

Mein Board bestätigt das Format auf Nachfrage:

Node Product Key Format..........JSON

Damit ist die naheliegende Abkürzung nicht moralisch, sondern rechnerisch verschlossen. Man muss die Frage also wirklich beantworten.

Der Kauf, und eine Überraschung beim Erzeugen

Gekauft habe ich im Supermicro eStore. 23,67 Euro netto, 28,17 Euro brutto. Bei der Bestellung wählt man das Mainboard-Modell aus einer Liste, und mein X12SPi-TF steht darin. Das ist erwähnenswert, weil mehrere Händlerbeschreibungen dieser Lizenz nur X10 und X11 nennen. Diese Texte sind veraltet.

Jetzt kommt der Teil, den ich falsch erwartet hatte. Ich dachte, ich kaufe und bekomme einen Schlüssel. So läuft es nicht. Man bekommt zunächst nur eine Zertifikatsnummer und die Aufforderung, den Schlüssel selbst zu erzeugen. Dafür meldet man sich im Store an, wählt die Bestellung aus und gibt an, für welches Gerät der Schlüssel gelten soll: entweder die MAC-Adresse des Management-Controllers oder die Seriennummer des Boards.

Die Bindung an die Hardware entsteht also nicht beim Verkauf, sondern beim Käufer, im Moment des Erzeugens. Das ist konsequent, denn Supermicro weiß beim Verkauf ja gar nicht, in welchem Gerät die Lizenz landen soll. Es heißt aber auch, dass man sich vertippen kann, und dann hat man einen Schlüssel für ein Gerät, das es nicht gibt.

Der Ablauf war zügig. Bestellbestätigung um 14:17, die Freigabe zum Erzeugen um 14:42, der fertige Schlüssel um 14:44, aktiv im Board um 14:46. Unter einer halben Stunde vom Klick bis zur Funktion, und die zugesagte Stunde war damit nicht zu optimistisch.

Der Schlüssel selbst ist eine kleine Textdatei, benannt nach der MAC-Adresse, mit einem JSON-Objekt darin: Lizenzname, Ausstellungsdatum und eine lange Signatur. Mehr ist es nicht.

Eine Kuriosität am Rande: die Mail mit dieser Textdatei trägt einen Ausfuhrhinweis der US-Regierung. Die Ware sei nur für das Bestimmungsland freigegeben und dürfe nicht weiterveräußert oder an Dritte weitergegeben werden. Das gilt hier für eine Datei von wenigen hundert Byte, deren einzige Wirkung darin besteht, in einem Verwaltungscontroller einen Menüpunkt sichtbar zu machen.

Aktivieren

Supermicro nennt vier Wege, den Schlüssel einzuspielen: die Weboberfläche des Management-Controllers, das Werkzeug SUM, die Verwaltungssoftware SSM und Redfish. Die Weboberfläche ist der ruhigste Weg, weil sie eine Bestätigungsseite hat, und bei einem Schlüssel, der genau einmal auf genau ein Gerät passt, will man die haben.

Wer es lieber skriptet, findet die passenden Endpunkte im Management-Controller, und die sind auch ohne Lizenz erreichbar:

POST /redfish/v1/Managers/1/LicenseManager/Actions/LicenseManager.ActivateLicense
POST /redfish/v1/Managers/1/LicenseManager/Actions/LicenseManager.ClearLicense
GET  /redfish/v1/Managers/1/LicenseManager/QueryLicense

Die Abfrage danach gibt genau den Inhalt der Datei zurück, die man hochgeladen hat. Der Controller speichert den Schlüssel also unverändert und prüft ihn bei Bedarf.

Das Kommando, auf das es ankommt

Nach der Aktivierung meldet SAA einen anderen Zustand:

Node Product Key Activated.......SFT-OOB-LIC
Feature Toggled On...............Yes
BMC Supports OOB BIOS Config.....Yes
BIOS Supports OOB BIOS Config....Yes

Und dasselbe Kommando, das vorher mit ExitCode 80 abgebrochen ist, läuft durch:

./saa -c GetCurrentBiosCfg --file bios-current.xml
File "bios-current.xml" is created.

Heraus kommt eine Datei von 260 Kilobyte mit 60 Menüs und 372 Einstellungen. Zum Vergleich: das offizielle Handbuch beschreibt 180 Optionen. Und die Datei enthält pro Eintrag mehr, als man im Setup sieht, nämlich zusätzlich den Auslieferungszustand und den Hilfetext:

<Setting name="Quiet Boot" checkedStatus="Unchecked" type="CheckBox">
  <Information>
    <DefaultStatus>Checked</DefaultStatus>
    <Help>Enables or disables Quiet Boot option</Help>
  </Information>
</Setting>

Der Auslieferungszustand ist der eigentliche Gewinn. Damit sehe ich auf einen Blick, welche Einstellungen an dieser Maschine vom Werkszustand abweichen, und das ist genau die Liste, die man nach einem BIOS-Update wiederherstellen will.

Zwei komplette Hauptmenüs standen darin, die ich beim Durchklicken schlicht nie geöffnet hatte. Und ein Untermenü, das ich in meinen Notizen als offene Lücke geführt habe, war vollständig enthalten. So viel zum Thema Handarbeit übersieht Dinge.

Was die Lizenz nicht freischaltet

Jetzt der ehrliche Teil, und der ist mir wichtiger als der Erfolg. Ich hatte vor dem Kauf drei Messungen aufgeschrieben, damit ich hinterher vergleichen kann. Drei von vier Erwartungen sind eingetroffen, und die vierte war die interessante.

Der Redfish-Endpunkt, mit dem alles anfing, antwortet weiterhin mit 403 und verlangt weiterhin DCMS. Die kleine Lizenz öffnet den Weg über SAA und eben nicht den über Redfish. Wer also ausdrücklich die Netzwerkschnittstelle braucht, etwa weil er hunderte Server ohne Betriebssystem konfigurieren will, kommt um die große Lizenz nicht herum.

Das hat eine Nebenwirkung, die ich nicht auf dem Zettel hatte. Der Firmware-Updater fwupd kann inzwischen mit Supermicros Redfish-Schnittstelle sprechen. Er sieht bei mir aber nur zwei von zwölf Firmware-Komponenten, und die beiden BIOS-Einträge fehlen. Der Grund ist, dass er zur Identifikation eines Geräts das Objekt lesen muss, das hinter genau diesem gesperrten Endpunkt liegt. Die Lizenz sperrt hier also nicht das Aktualisieren, sondern das Erkennen, und das Aktualisieren scheitert dann als Folge. Nach der Aktivierung sind es unverändert zwei von zwölf.

Und der dritte Weg, den ich vorher gar nicht gesehen hatte: fwupd sieht den BIOS-Speicher auch ganz direkt am Chipsatz, liest die Version korrekt aus und verweigert trotzdem den Dienst:

Internal SPI Controller (BIOS):
    Aktuelle Version: 2.5
    Probleme:         Device firmware has been locked

Das ist keine Lizenzsache, und daran hat sich erwartungsgemäß nichts geändert. Der Speicher ist auf Chipsatzebene schreibgeschützt, weil das Board mit aktivem Root of Trust läuft. Ein BIOS, das sich aus einem laufenden Betriebssystem heraus überschreiben lässt, wäre genau die Lücke, die dieser Schutz verhindern soll. Der Reflex sagt hier Hersteller blockiert Linux. Tatsächlich blockiert eine Sicherheitsfunktion, die ich selbst haben will.

Mein Denkfehler

Bleibt der Satz, den ich weiter oben angekündigt habe. In meinen Notizen stand nach der dritten Absage sinngemäß: drei unabhängige Wege, dieselbe Sperre, nicht erneut versuchen.

Gemessen hatte ich: ohne Lizenz gibt es keinen Weg. Aufgeschrieben hatte ich: es gibt keinen Weg. Und die dritte Möglichkeit, nämlich die Lizenz einfach zu kaufen, kam in meiner Aufzählung der drei gesperrten Wege überhaupt nicht vor, obwohl das Werkzeug sie mir wörtlich genannt hatte.

Dass drei Wege an derselben Sperre scheitern, beweist, dass es eine Sperre gibt. Es beweist nicht, dass sie unüberwindbar ist. Wenn eine Fehlermeldung den Namen der fehlenden Berechtigung nennt, ist das ein Hinweis und kein Schlusspunkt.

Lohnt sich das?

Für einen einzelnen Rechner zu Hause: nur, wenn man Freude daran hat. Ich hätte die Werte auch weiter abschreiben können, und der Screencast hat mir immerhin eine fünfzehnteilige Serie eingebracht, die aus einer XML-Datei nie entstanden wäre. Man liest eine Datei nicht so aufmerksam wie ein Menü, durch das man sich selbst klicken muss.

Für alles, was mehr als eine Handvoll Maschinen betrifft, sieht es anders aus. Konfiguration auslesen, versionieren, nach einem Update wieder einspielen und im Zweifel gegen einen bekannten Stand vergleichen, das ist bei 28 Euro pro Board keine Diskussion. Ärgerlich finde ich nur, dass man diese Rechnung überhaupt aufmachen muss. Es geht um das Auslesen der eigenen Einstellungen auf der eigenen Hardware.

Wenn du auf demselben Board sitzt und dieselbe Fehlermeldung vor dir hast, ist die Kurzfassung: für die Konfiguration reicht die kleine Lizenz, für Redfish brauchst du die große, und für Firmware-Updates brauchst du bei einem X12 mit Root of Trust vermutlich gar keine.

Siehe auch: BIOS erklärt, Teil 1: ein Serverboard als Workstation und der Weg ins Setup und die übrigen Folgen unter BIOS & Firmware.

Wenn dazu etwas unklar ist oder du es auf deinem Board anders erlebt hast, dürft ihr mich sehr gerne fragen.

BIOS erklärt, Teil 1: ein Serverboard als Workstation und der Weg ins Setup

Seit Ende Juli steht bei mir ein anderer Rechner unter dem Schreibtisch. Kein gekaufter, sondern einer aus Teilen die ohnehin schon da waren: ein Supermicro X12SPi-TF, also ein richtiges Serverboard, dazu ein Xeon Gold 5315Y und 256 GB registrierter ECC-Speicher. Die Grafikkarte ist aus der alten Workstation umgezogen, Gehäuse und Netzteil waren neu. Seitdem macht die Kiste genau das, was vorher eine deutlich ältere Dual-Socket-Maschine gemacht hat, nur leiser und mit mehr Luft nach oben.

Aptio Setup, Reiter Main, mit Systemdatum und Systemzeit

Beim Einrichten ist mir dann etwas passiert, das ich so nicht auf dem Zettel hatte. Ich saß im BIOS-Setup, bin die Seiten durchgegangen, und bei einigen Einstellungen dachte ich: kenne ich, habe ich schon hundertmal gesehen. Hätte mich in dem Moment jemand gefragt, was der Schalter denn nun genau tut, wäre ich ins Schwimmen gekommen. Also habe ich nachgeschlagen. Und dann noch mal. Irgendwann stand fest, dass daraus eine kleine Serie werden sollte.

Warum ein Server-BIOS eine andere Hausnummer ist

Aptio Setup, Reiter Advanced, mit der Liste der Untermenüs von Boot Feature bis Supermicro KMS Server Configuration

Ein normales Desktop-Board hat im Setup vielleicht drei Dutzend Schalter, die wirklich etwas bewirken. Der Rest ist Bootreihenfolge, Lüfterkurve und Beleuchtung. Bei diesem Board sieht das anders aus. Das Handbuch beschreibt rund 180 Einstellungen, bei denen man tatsächlich etwas auswählen kann, verteilt auf sieben Hauptmenüs und je nach Zweig bis zu fünf Ebenen tief. Dort stehen Dinge wie Patrol Scrub, Stale AtoS, UMA-Based Clustering oder IIO eDPC Support. Das sind keine Marketingbegriffe, das sind Schalter für Verhalten, das man verstanden haben muss, bevor man daran dreht.

Und genau da liegt für mich der interessante Punkt. Fast alles davon steht auf Auto oder auf dem Herstellerdefault, und das ist in den allermeisten Fällen auch völlig richtig so. Nur ist „steht auf Default“ eben keine Antwort auf die Frage, was eigentlich passiert, wenn man es umstellt. Diese Antwort hätte ich gern. Für jeden Schalter, der auf diesem Board etwas tut.

Wie man auf so einem Board überhaupt ins Setup kommt

Hier fängt der Unterschied zum Desktop schon an. Auf dem Board sitzt ein zweiter, kleiner Computer: der BMC, ein ASPEED AST2600. Der hat seine eigene Netzwerkbuchse, seine eigene IP-Adresse, sein eigenes Betriebssystem, und er läuft auch dann, wenn der eigentliche Rechner aus ist. Solange Strom am Netzteil anliegt, ist der BMC wach. Der BMC bekommt später eine eigene Folge, für heute reicht eine seiner Fähigkeiten: KVM over IP.

Das heisst konkret, ich brauche für das BIOS weder Monitor noch Tastatur am Rechner. Ich rufe im Browser die Weboberfläche des BMC auf, starte dort die Konsole, und sehe das Bild, das die Grafikausgabe gerade produziert. Inklusive POST, inklusive Setup, inklusive Bootloader. Tasten gehen den umgekehrten Weg. Der BMC hängt sich dafür als virtuelle USB-Tastatur und -Maus an den Rechner, und die tauchen unter Linux später auch brav in lsusb auf:

Bus 001 Device 003: ID 0557:9241 ATEN International Co., Ltd SMCI HID KM
Bus 001 Device 004: ID 0b1f:03ee Insyde Software Corp. RNDIS/Ethernet Gadget

Wer schon mal einen Server im Keller stehen hatte und sich gefragt hat, wie man da ohne Bildschirm ins BIOS kommt: so. Und weil das Bild ohnehin durchs Netz geht, kann man es auch gleich aufzeichnen. Genau das habe ich gemacht, und diese Aufnahme ist die Grundlage für alles, was in dieser Serie noch kommt.

Und dann wollte ich die Einstellungen einfach auslesen

Aptio Setup, Reiter Save and Exit, mit den Speicheroptionen, Save as User Defaults und der geschwärzten Boot-Override-Liste

Das war der Moment, in dem die Serie ihre eigentliche Rechtfertigung bekommen hat. Meine Idee war naheliegend: statt 200 Screenshots zu sortieren, hole ich mir die komplette Konfiguration als Datei. Moderne Boards können das, dafür gibt es Redfish, eine HTTP-Schnittstelle auf dem BMC. Ein Aufruf, und man hat jede Einstellung mit Ist-Wert und allen möglichen Werten sauber als JSON.

Der Aufruf ging auch durch. Nur nicht so, wie ich wollte:

GET /redfish/v1/Systems/1/Bios
403 Forbidden
MessageId: SMC.1.0.OemLicenseNotPassed
"Not licensed to perform this request. The following licenses DCMS were needed"

Also der Weg über das Kommandozeilenwerkzeug. Supermicro liefert dafür den SuperServer Automation Assistant mit, und der kennt genau den passenden Befehl. In-Band, also lokal auf der Maschine selbst, ohne Umweg über das Netzwerk:

./saa -c GetCurrentBiosCfg --file bios-current.xml

Ergebnis:

ExitCode                = 80
Description             = Node product key is not activated.
Error message:
        One of the node product key (SFT-OOB-LIC or SFT-DCMS-SINGLE) should be
    activated to execute this task.

Damit ist es amtlich. Das Board weigert sich, mir seine eigene Konfiguration herauszugeben, weil ich keine Lizenz gekauft habe. Nicht ändern, wohlgemerkt. Nur lesen. Der dritte Weg über die UEFI-Variablen im Kernel führt auch nicht weiter, da liegen zwar Blobs, aber ohne die Zuordnungstabellen des Boards sind das einfach nur Bytes.

Ich verstehe das Geschäftsmodell durchaus. Wer dreihundert Server ausrollt, will die Konfiguration automatisiert verteilen, und dafür Geld zu verlangen ist legitim. Nur bin ich eben nicht dreihundert Server, ich bin ein Schreibtisch, und das Auslesen dessen was ich selbst eingestellt habe fühlt sich nicht nach einem Premium-Feature an. Der Screencast über die Konsole ist der Weg drumherum, und ehrlich gesagt ist er für diese Serie sogar der bessere: ein Screenshot zeigt den Schalter so, wie er dir im Setup auch begegnet.

Was in der Serie kommt

Ich gehe das BIOS in Themenblöcken durch, nicht Menüseite für Menüseite. Jede Folge nimmt sich einen Bereich vor und beantwortet für die Schalter darin drei Fragen: was tut das, warum steht es bei mir so, und wann würde man es anders setzen. Grob in dieser Reihenfolge:

  • Boot Feature und Power, also alles rund um Einschalten, Watchdog und das Verhalten nach einem Stromausfall
  • Die CPU, einmal die Seite mit Kernen, Threads und Prefetchern, einmal die mit AES-NI, TME, SGX und den Sicherheitsfunktionen
  • Speicher, ECC, Patrol Scrub und die Frage warum mein DDR4-3200 mit 2933 läuft
  • NUMA und der Uncore-Bereich, der auf einer Single-Socket-Maschine erstaunlich viel Unsinn anzeigt
  • PCIe in drei Folgen: Slots und Lanes, dann Adressraum und Resizable BAR, dann die Fehlerbehandlung, wo es richtig spannend wird
  • Virtualisierung mit VT-d, IOMMU und VMD
  • Storage, Preboot-Netzwerk, Bootreihenfolge und Secure Boot
  • Zum Schluss der BMC selbst, das TPM und die Ereignisprotokolle

Die Folgen sind bewusst kurz gehalten. Ein Thema, einmal sauber durch, und nicht ein Wälzer den keiner zu Ende liest.

Ein Wort dazu, warum ich das überhaupt aufschreibe

Zwei Gründe. Der erste ist eigennützig: indem ich es aufschreibe, muss ich es verstanden haben. Ein Halbwissen überlebt keinen Absatz, in dem man erklären soll warum etwas so ist. Das ist der beste Lerneffekt den ich kenne.

Der zweite Grund ist etwas nachdenklicher. Dieses Wissen ist am Aussterben, und das meine ich ohne Larmoyanz. Wer heute Rechenleistung braucht, klickt sie in einer Cloud zusammen, und dort gibt es kein BIOS mehr, an das man herankäme. Wer eine Frage hat, fragt eine AI und bekommt eine Antwort, die meistens stimmt. Beides ist bequem und für die allermeisten Zwecke völlig ausreichend. Trotzdem muss es am Ende weiterhin Menschen geben, die wissen was unter der Abstraktion passiert. Wenn eine Maschine nicht bootet, hilft kein Terraform.

Und damit zum wichtigsten Teil: wenn ich mich irre, sag es mir bitte. Ich schreibe diese Serie auch, um dazuzulernen, und eine Korrektur ist dafür der schnellste Weg. Ich trage sie dann im jeweiligen Beitrag nach, mit Datum, und lasse sie nicht stillschweigend verschwinden. Gerade bei den Prozessor-Interna und beim PCIe-Teil wird es hier Leute geben, die tiefer drin stecken als ich.

Nächste Folge: Boot Feature und Power. Also die Frage, warum mein Rechner nach einem Stromausfall absichtlich aus bleibt, und was ein Interrupt aus dem Jahr 1981 heute noch in einem UEFI-Setup zu suchen hat.

Siehe auch

Habt ihr auf eurem Board schon mal versucht, die BIOS-Konfiguration maschinell auszulesen, und seid dabei auch gegen eine Lizenz gelaufen? Erzählt es mir gerne, ihr dürft mich jederzeit fragen.

Vier Monate von Bing nicht ausgeliefert: ein Schalter auf Domain-Ebene und 218 IndexNow-Einreichungen

Am 16. März 2026 hat Bing meine Seite ausgeliefert wie an jedem Tag davor. Am 17. März nicht mehr. Nicht schlechter platziert, nicht seltener, sondern praktisch gar nicht. Sieben Tage lang kein einziger Klick. Und es blieb nicht bei der Websuche: die Zitate in Copilot hörten am selben Tag auf. Knapp vier Monate später, am 9. Juli, ging es genauso abrupt wieder los.

Visualisierung des Bing-Indexierungsproblems: Nach zahlreichen IndexNow-Einreichungen bricht die Sichtbarkeit der Domain abrupt ein und erholt sich Monate später wieder.

Bevor jetzt jemand Mitleid entwickelt: Bing macht bei diesem Blog rund ein Prozent des Traffics. Der Ausfall hat mich nichts gekostet, aufgefallen ist er mir erst Wochen später, und wäre er nie repariert worden, wäre das hier trotzdem kein trauriger Beitrag geworden. Interessant ist er aus einem anderen Grund.

So ein sauberer binärer Ausfall ist selten zu sehen. Kein Abrutschen über Wochen, kein Rauschen, sondern über Nacht zu und Monate später über Nacht wieder auf. Und das Beste: man kann es selbst rekonstruieren. Die Exporte aus den Webmaster Tools plus das eigene Access-Log reichen aus, um den Zeitpunkt auf den Tag genau zu datieren und eine sehr wahrscheinliche Ursache einzukreisen. Ohne dass der Anbieter je erklärt hätte, was er eigentlich getan hat.

Was jetzt kommt, ist also keine Beschwerde, sondern eine Fehleranalyse an einem Fall, bei dem der Schaden bei null lag und die Datenlage ungewöhnlich gut ist.

Zwei Kanten, dazwischen vier Monate

Absolute Zahlen zu Besuchern veröffentliche ich hier nicht, die braucht die Geschichte auch nicht. Erzählt wird ein Verlauf, kein Niveau. Alles Folgende ist deshalb indexiert: der Durchschnitt der Impressions pro Tag im Zeitraum 01.11.2025 bis 16.03.2026 ist die Baseline und bekommt den Wert 100. Impressions sind dabei die Anzahl der Ausspielungen in den Ergebnislisten, Klicks das, was danach kommt. Ich halte die beiden konsequent auseinander, weil genau in ihrem Verhältnis der interessanteste Teil steckt.

MonatIndexKommentar
2025-0881normaler, langsam wachsender Verlauf
2025-0988
2025-10104
2025-11107
2025-12108Höchststand
2026-01105
2026-0291
2026-0344Einbruch am 17.03. mitten im Monat
2026-0424Teilerholung auf niedrigem Niveau
2026-0510zweiter Einbruch ab 16.05.
2026-067Tiefpunkt
2026-0755am 09.07. fällt der Schalter wieder um
2026-08 (5 Tage)74

Die Monatswerte glätten beide Kanten weg, weil sie mitten im Monat liegen. Nach Phasen sortiert wird es deutlicher. Gleiche Baseline, dazu die Klicks und die Klickrate:

PhaseImpressionsKlicksCTR
Baseline 01.11.2025 bis 16.03.2026 (136 Tage)1001002,75 %
17.03. bis 23.03.2026 (7 Tage)0,600 %
24.03. bis 15.05.2026 (53 Tage)21415,3 %
16.05. bis 08.07.2026 (54 Tage)6125,2 %
09.07. bis 05.08.2026 (28 Tage)69953,75 %
Stufendiagramm der Bing-Impressions und Klicks als Index gegen die Baseline 100. Am 17. März 2026 fällt die Kurve senkrecht auf nahezu null, bleibt vier Monate niedrig und springt am 9. Juli 2026 wieder nach oben.
Dieselben Zahlen als Bild. Beide Kanten sind senkrecht, dazwischen liegen vier Monate. Die Klicks (gestrichelt) kommen fast vollständig zurück, die Impressions nicht.

Die erste Kante liegt zwischen dem 16. und dem 17. März: von normalem Niveau auf 0,6 Prozent der Baseline. Das ist kein Einbruch mehr, das ist aus. Eine Woche lang blieb es dabei, ohne einen einzigen Klick.

Die zweite Kante liegt zwischen dem 8. und dem 12. Juli: innerhalb von vier Tagen mehr als das Sechsfache. Der Anstieg beginnt am 9. Juli, also an genau dem Tag, an dem die Lösungs-Mail aus dem Support eintraf. Dazu später mehr, das Detail ist hübscher, als es klingt.

Das sieht nicht aus wie ein Ranking

Das ist die zentrale Beobachtung, und sie steckt in der Form der Kurve, nicht in ihrer Höhe. Rankings rutschen. Wenn eine Seite an Gewicht verliert, fällt sie über Wochen durch die Positionen, einzelne Suchbegriffe halten sich länger als andere, das Ganze franst aus. Was hier passiert ist, franst nirgends aus. Es ist an einem Tag da und am nächsten weg, und vier Monate später umgekehrt.

Zu einem Filter auf Domain-Ebene passt so ein Muster ausgesprochen gut. Ich schreibe bewusst passt zu und nicht beweist, denn die Form einer Kurve ist ein Indiz und keine Diagnose. Aber sie legt fest, wonach man überhaupt suchen muss: nach etwas, das sich ein- und ausschalten lässt, und nicht nach etwas, das langsam an Gewicht verliert.

Dazu kommt ein zweiter Befund, der in dieselbe Richtung zeigt und den ich für den stärkeren halte. Die Webmaster Tools weisen die Zitate in Copilot getrennt von der Websuche aus, es sind zwei verschiedene Oberflächen mit unterschiedlichen Auswahlmechanismen. Beide gingen am 17. März taggleich auf null. Dass eine Seite zeitgleich aus der Ergebnisliste und aus den KI-Antworten verschwindet, passt schlecht zu einer Bewertung einzelner Seiten und gut zu einem gemeinsamen Gate, das eine Ebene darüber sitzt und die ganze Domain betrifft. Ein Ranking-Effekt müsste sich erst durch die eine Oberfläche arbeiten und dann durch die andere, und ganz sicher nicht innerhalb eines Tages.

Ein Nebenbefund aus der Phasentabelle, der mir gut gefällt: die Klickrate war in den Ausfallphasen am höchsten. 5,3 und 5,2 Prozent gegen 2,75 Prozent in der Baseline. Das klingt zunächst nach einer guten Nachricht und ist das exakte Gegenteil. Wenn fast nichts mehr ausgeliefert wird, überleben nur die allerspezifischsten Suchanfragen, und die klicken naturgemäß gut. Eine steigende CTR ist eben nur dann ein gutes Zeichen, wenn der Nenner stabil bleibt.

Was es alles nicht war

Bevor man eine Ursache benennt, sollte man die naheliegenden Kandidaten abräumen. Das ist der Teil, der aus einer Vermutung eine Untersuchung macht.

  • Kein Crawling-Problem. Der Bingbot kam die ganze Zeit unverändert vorbei, rund 80 Anfragen pro Tag, ganz überwiegend mit Status 200. Das steht im eigenen Access-Log und ist damit die eine Quelle, der ich in diesem Fall vollständig traue. Gecrawlt wurde also durchgehend. Ausgeliefert wurde nur nichts.
  • Spricht klar gegen eine Löschung aus dem Index. DuckDuckGo und Ecosia beziehen ihre Ergebnisse von Bing und lieferten während der ganzen Zeit weiterhin Treffer zu meiner Domain. Bing selbst zeigte also weniger als seine eigenen Syndication-Partner. Die Inhalte waren demnach nach wie vor im Index, nur bing.com hat sie nicht mehr herausgegeben. Das ist für mich das hübscheste Detail des ganzen Falls und spricht stark für ein Gating auf der Ausliefer- oder Vertrauensebene statt für einen Deindex.
  • Spricht gegen ein reines Nutzersignal-Problem. Wenn eine Seite wegen schlechter Klickraten oder Nutzerverhalten abgewertet wird, erwartet man, dass die Klicks wegbrechen. Hier verschwanden die Impressions. Ich will das Argument nicht überdehnen: ein hinreichend starkes Vertrauens- oder Demotionssignal kann durchaus auch Impressions kosten. Es schwächt die Nutzersignal-Hypothese, es räumt sie nicht ab.
  • Nichts an Technik gefunden. Canonicals sauber, Sitemap in den Webmaster Tools als Success verarbeitet, am Server wurde im relevanten Zeitraum nichts umgestellt. Das ist ausdrücklich ein „nichts gefunden“ und kein „ausgeschlossen“, aber die üblichen Verdächtigen sind es damit nicht.
  • Keine Inhalts- oder Risikoklassifikation. Eigener Test mit SafeSearch von streng bis aus: kein Unterschied. Wer hier mit Erwachsenenfiltern hantiert, wird an diesem Blog wenig Freude haben, aber prüfen kostet nichts.
  • Keine Link-Armut. Die Google Search Console zeigte im selben Zeitraum ein diverses, redaktionelles deutsches Linkprofil aus Fachforen, Fachblogs und Projektseiten. Es fehlten keine Links. Das Gate hat die Domain trotz vorhandener Verweise ignoriert.

Und dann ist da noch die Kontrollgruppe, die einem hier die halbe Arbeit abnimmt: Google lief die ganze Zeit unauffällig weiter. Google liefert bei diesem Blog ohnehin um Größenordnungen mehr Suchklicks als Bing, ich hätte einen Einbruch dort also sofort gesehen. Gleiche Inhalte, gleicher Server, gleiche Technik, gleiche Links, ein Anbieter ganz normal, der andere komplett zu. An der Seite selbst kann es damit kaum gelegen haben. Für dieses Argument braucht es keine einzige Zahl, und das ist auch gut so.

Der Fund: eine Spalte namens Source

Die Webmaster Tools bieten einen Export mit dem Namen IndexNow Submitted URLs. Ich habe ihn lange für eine Fleißliste gehalten und nie hineingeschaut. Er hat eine Spalte, die den ganzen Fall aufgeschlossen hat, und die heißt Source. Sie unterscheidet, wer eine URL eingereicht hat:

  • wordpress steht für das WordPress-Plugin, das ereignisgesteuert meldet, wenn ein Beitrag veröffentlicht oder aktualisiert wird.
  • none steht für einen direkten Aufruf der HTTP-Schnittstelle, also von Hand oder per Skript.

Der Export umfasst 339 Einreichungen zwischen dem 04.01.2025 und dem 06.08.2026. Davon tragen 121 die Quelle wordpress und 218 die Quelle none. Und jetzt kommt der Teil, bei dem ich kurz still geworden bin: alle 218 manuellen Einreichungen liegen in einem einzigen Fenster, nämlich zwischen dem 11.02.2026 und dem 18.03.2026. Davor ausschließlich das Plugin. Danach ausschließlich das Plugin. Das Fenster ist scharf abgegrenzt, ohne eine einzige Ausnahme.

DatumEinreichungenZeitfenster (CEST)
11.02.20269311:35 bis 11:55
21.02.20261
09.03.20263
11.03.20266
12.03.202610
13.03.20263
14.03.20269809:33 bis 15:00
18.03.20264

93 URLs in zwanzig Minuten. 98 URLs an einem einzigen Tag. 214 manuelle Einreichungen in 32 Tagen. Bei einem Blog mit rund 460 Beiträgen habe ich in fünf Wochen also grob die halbe Seite per Schnittstelle angeklopft. Und zwar durchweg alte, unveränderte Beiträge, an denen sich nichts getan hatte, außer dass ich sie gerne mal wieder frisch gecrawlt gesehen hätte.

Der Einbruch kam am 17.03.2026, also drei Tage nach dem zweiten Massen-Batch.

Was IndexNow ist und wofür ich es gehalten habe

IndexNow ist ein bewusst simpel gehaltenes Push-Protokoll. Statt darauf zu warten, dass irgendwann ein Crawler vorbeischaut, sagt die Seite der Suchmaschine aktiv Bescheid: diese URL hat sich geändert, schau noch mal rein. Technisch ist das ein Aufruf mit einer URL und einem Schlüssel, der als Textdatei im Web-Root liegt und die Domain ausweist. Bing, Yandex, Seznam, Naver und Yep hängen am selben Pool, eine Meldung erreicht also alle. Gedacht ist das Ganze als Ereignis-Signal: eine Änderung, eine Meldung.

Und genau hier liegt der Denkfehler, den ich rückblickend ziemlich sauber benennen kann. Das Ding sieht aus wie ein technischer Endpunkt zur Cache-Invalidierung. So habe ich es auch behandelt: Liste durchgehen, Schnittstelle anstoßen, fertig. Praktisch ist es aber ein Vertrauenskanal. Jede einzelne Einreichung trägt die implizite Behauptung „hier hat sich etwas geändert, das einen erneuten Besuch wert ist“. Zweihundert solcher Behauptungen für unveränderte Altbeiträge innerhalb von fünf Wochen sind aus Sicht des empfangenden Systems kein zu häufig aufgerufener Endpunkt, sondern ein Häufungsmuster, das nach Missbrauch aussieht. Völlig unabhängig davon, was ich mir dabei gedacht habe.

Dass das keine reine Rückschau-Weisheit ist, steht sogar in der Spezifikation. Die Liste der Antwortcodes kennt neben den üblichen Verdächtigen einen 429 Too Many Requests, und dahinter steht wörtlich potential Spam. Das Protokoll selbst denkt an dieser Stelle also bereits über Missbrauch nach, nicht über Serverlast. Nur: eine konkrete Obergrenze nennt die Dokumentation nirgends. Es gibt keine Zahl, gegen die man sich prüfen könnte, und einen 429 habe ich nie gesehen. Das Limit ist unsichtbar, bis man darüber ist, und dann meldet sich niemand.

Ein Detail passt hier noch gut hinein, auch wenn es die Kausalität nicht beweist: Google nimmt an IndexNow gar nicht teil. Der Kanal, über den ich hier zweihundertmal angeklopft habe, existiert bei dem einen Anbieter, der ausgefallen ist, und bei dem anderen, der unbeeindruckt weiterlief, schlicht nicht.

Wo die Rekonstruktion aufhört

Das muss klar dastehen, sonst wird aus einer ordentlichen Analyse eine Behauptung: Das ist eine sehr gut belegte Korrelation und kein Beweis. Microsoft hat die Ursache nie schriftlich benannt. Die spätere Lösungs-Mail verweist pauschal auf den Abschnitt Things to Avoid der Webmaster Guidelines und sonst auf nichts.

Was ich habe, ist zeitliche Nähe, ein scharf abgegrenztes Fenster, das exakt vor dem Ausfall endet, und keinen anderen plausiblen Auslöser im selben Zeitraum. Das ist die beste verfügbare Erklärung. Es ist nicht die bestätigte. Mit dieser Unterscheidung kann ich gut leben, sie macht den Fall nicht schwächer.

Offen bleibt auch der zweite Einbruch. Ab dem 16.05.2026 fiel der Wert noch einmal von Index 21 auf 6, und in diesem Zeitraum gab es überhaupt keine manuellen Einreichungen mehr. Plausibel wäre eine erneute automatische Bewertung, belegen kann ich das nicht. Das bleibt so stehen, weil Wegerklären hier nichts besser machen würde.

Der Support, oder: ein Lautsprecher ist keine Mailbox

Am 06.05.2026 habe ich ein Ticket aufgemacht, Nummer REQ00222409, Betreff sinngemäß „My site suddenly dropped in ranking“. Die automatische Bestätigung kam sofort und war erfreulich konkret: das Ticket werde einem Global Support Webmaster Engineer zugewiesen, eine Rückmeldung sei in 10 days zu erwarten.

Am 19.05.2026 habe ich nachgefragt, ganz konventionell per Antwort auf ebendiese Bestätigungsmail. Das Ergebnis war ein harter Bounce:

550 5.7.133 RESOLVER.RST.SenderNotAuthenticatedForGroup

Der Fehler ist immerhin eindeutig. Die Absenderadresse ist eine interne Microsoft-365-Verteilergruppe, die ausschließlich authentifizierte Sender aus dem eigenen Tenant annimmt. Der Kanal, über den mein Ticket bestätigt wurde, kann also grundsätzlich keine Antwort empfangen. Das ist keine Mailbox, das ist ein Lautsprecher.

Parallel dazu war die Support-Oberfläche in den Webmaster Tools für diese Property nie freigeschaltet, dort stand durchgehend „No pages found“. Es gab damit buchstäblich keinen Rückkanal. Danach passierte 64 Tage lang nichts, und ich habe das Ticket innerlich abgehakt.

Die Mail, der ich erst nach dem Header-Check geglaubt habe

Am 09.07.2026 um 21:23 Uhr CEST kam dann doch noch eine Mail auf demselben Ticket. Der Kern: the issue related to your site … has been resolved, die Erholung könne up to 2-3 weeks dauern, das Ticket werde geschlossen, Rückfragen bitte über ein Feedback-Formular. Der Einweg-Kanal kann also durchaus zustellen. Nur empfangen kann er nichts.

Eine Mail, die nach zwei Monaten Funkstille auftaucht und etwas Erfreuliches behauptet, ist erst mal eine Mail und keine Tatsache. Also habe ich die Header gelesen, bevor ich ihr geglaubt habe. DKIM, SPF und DMARC standen alle auf pass, die Absenderdomain fährt eine DMARC-Policy auf reject, und meine eigene Spam-Filterung hat die Nachricht mit deutlich negativer Bewertung und no action durchgewinkt. Also echt, kein Spoofing. In einem Blog, in dem ziemlich viel über Mail-Härtung steht, ist so ein Blick kein Sonderaufwand, sondern Reflex.

Und dann steht in derselben Mail noch eine Empfehlung: zur Beschleunigung des erneuten Crawlens möge ich doch IndexNow nutzen. Also genau das Werkzeug, dessen massenhafte Verwendung die beste verfügbare Erklärung für den ganzen Vorfall ist. Ich habe den Rat nicht befolgt. Das WordPress-Plugin macht seinen ereignisgesteuerten Job, und das reicht.

Was zurückkam und was nicht

Bei den Klicks ist die Erholung praktisch vollständig, Index 95 gegen die Baseline. Bei den Impressions liegt sie bei rund zwei Dritteln bis drei Vierteln, Index 69 über die ersten 28 Tage und 74 in den ersten Augusttagen. Die Klickrate liegt nach der Erholung mit 3,75 Prozent über dem Vorher-Wert. Ob die Impressions noch weiter steigen oder ob das jetzt der neue Normalzustand ist, weiß ich schlicht nicht.

Wichtig ist mir dabei eine Sache, die man leicht falsch herum erzählen könnte: die Wiederherstellung kam serverseitig. Es gibt keinen erkennbaren Zusammenhang mit irgendeiner Maßnahme von mir. Dass das Ticket den Ausschlag gegeben hat, passt zeitlich gut, belegt ist es nicht. Ich habe in diesen vier Monaten nichts repariert, weil es an meinem Ende nichts zu reparieren gab.

Und ein Punkt ist ausdrücklich ungeklärt: ob die Copilot-Zitate wieder da sind. Hier lassen sich zwei Datenquellen leicht verwechseln, deshalb sauber getrennt. Der taggleiche Ausfall am 17. März stammt aus früheren, tagesaufgelösten Exporten, die ich im Juni ausgewertet habe, und den halte ich für belastbar. Die aktuellen Reports zur KI-Nutzung summieren dagegen über das volle 24-Monats-Fenster und lassen sich nicht auf einen Zeitraum eingrenzen. Sie enthalten also überwiegend Zitate aus der Zeit vor dem Vorfall, und aus ihnen lässt sich weder eine Erholung noch ein Ausfall ableiten. Für diese Frage sind sie schlicht unbrauchbar, auch wenn sie auf den ersten Blick beruhigend aussehen. Kurz: das Aussetzen ist datiert, die Rückkehr ist ungeprüft.

Was Microsoft konkret umgestellt hat, ist ebenfalls unbekannt und wurde nie mitgeteilt.

Was hängen bleibt

Ein automatisches „ich habe hier etwas Neues“-Signal ist kein technischer Endpunkt, sondern ein Vertrauenskanal mit einem unsichtbaren Limit. Man benutzt es so, wie es gedacht ist: ereignisgesteuert, ausgelöst von einer echten Änderung, und nicht als Batch-Werkzeug für eine Liste alter Beiträge. Das ist die eine Sache, die ich vorher nicht so gesehen habe.

Ansonsten steht der Schalter wieder auf an, niemand hat je aufgeschrieben, was er eigentlich war, und da hier ohnehin nur ein Nebenkanal dranhing, ist das auch völlig in Ordnung. Die Verteilung dieses Blogs steht auf mehreren Beinen, und Bing war nie eines der tragenden. Schulterzucken, weitermachen.

Siehe auch

Hast du so ein Muster schon einmal an einer eigenen Domain gesehen, bei Bing oder woanders, und konntest es sauber datieren? Oder kennst du eine dokumentierte Obergrenze für IndexNow, die ich übersehen habe? Dann dürft ihr mich sehr gerne fragen.

Einen modernen OpenPGP-Schlüssel bauen: Ed25519, drei Unterschlüssel und die Härtung, die ihn geschwächt hat

Moin. Im letzten Beitrag ging es darum, wie ich meinen OpenPGP-Schlüssel an Domain, GitHub und Matrix gebunden habe, also um die Frage, woher eigentlich irgendwer wissen soll, dass dieser Schlüssel mir gehört. Ganz am Ende stand dort die Ankündigung eines zweiten Teils: wie das Ding überhaupt gebaut ist und wie sicher es wirklich ist. Der Teil ist jetzt hier.

OpenPGP-Schlüssel mit Ed25519-Hauptschlüssel, drei Unterschlüsseln und Warnung vor fehlenden Algorithmus-Präferenzen durch eine fehlerhafte 3DES-Härtung.

Der Beitrag hat zwei Hälften, und die kannst du unabhängig voneinander lesen. Teil A ist ein Rezept. Wenn du einen Schlüssel nach demselben Muster bauen willst, arbeitest du dich von oben nach unten durch, inklusive der kompletten GnuPG-Konfiguration, alles kopierbar und in der richtigen Reihenfolge. Teil B erklärt, was die einzelnen Bauteile bedeuten, und endet mit einer ehrlichen Einschätzung, wie sicher das alles am Ende ist.

Der eigentliche Grund, warum ich das aufschreibe, ist aber ein anderer. Zwischen „mein Schlüssel ist modern“ und „mein Schlüssel ist richtig konfiguriert“ liegt eine Lücke, und in die bin ich mit Anlauf hineingesprungen. Der Schlüssel ist Lehrbuch: Ed25519, ein Hauptschlüssel, der ausschließlich zertifiziert, drei getrennte Unterschlüssel, fünf Jahre Laufzeit, über sieben Kanäle veröffentlicht, dazu eine eID-Zertifizierung. Und trotzdem wurde er vom ersten Tag an schwächer angesprochen als der Schlüssel, den er ablöst.

Aufgefallen wäre das niemandem, und genau das ist der interessante Teil. Das einzige Symptom war eine Warnzeile auf dem Terminal von jemandem, der mir eine verschlüsselte Mail schreibt. Also auf einem Rechner, den ich nie zu Gesicht bekomme. Und die Ursache war ausgerechnet eine Härtungsmaßnahme, die ich in bester Absicht eingebaut hatte, um einen musealen Algorithmus loszuwerden. Härtung, die etwas leise verschlechtert, finde ich als Geschichte deutlich spannender als noch eine Schlüsselerzeugungsanleitung. Nachbauen kannst du das übrigens in zwei Minuten, das Labor dazu steht weiter unten.

Der Schlüssel, um den es geht

BestandteilWert
Hauptschlüsseled25519 0x893DE0CDDE986DEB, Verwendung [C], also nur zertifizieren
Fingerabdruck45FC D081 ADB5 4872 EA5B 06B9 893D E0CD DE98 6DEB
Signatur-Unterschlüsseled25519 0xD788641D8588A674
Verschlüsselungs-Unterschlüsselcv25519 0x429D03637892821A
Authentisierungs-Unterschlüsseled25519 0x22F2E3234664DDBE
UIDsSebastian van de Meer, dazu eine Foto-UID mit 10851 Byte
Gültigkeiterzeugt am 24.07.2026, läuft am 23.07.2031 ab
Vorgängered25519 0x5F279C362EEAB216, gültig bis 31.12.2026, zeichnet den neuen gegen
FremdzertifizierungGovernikus/eID 0x5E5CCCB4A4BF43D7, Level 3
UmgebungGnuPG 2.4.4, libgcrypt 1.10.3

Die Aufgabenteilung steht nicht nur in der Doku, die steht maschinenlesbar im Schlüssel selbst. Jede Bindungssignatur trägt ein Feld mit Schlüssel-Flags, und das sieht bei mir so aus:

primary  key flags: 01   certify
[S]      key flags: 02   sign
[E]      key flags: 0C   encrypt communications + encrypt storage
[A]      key flags: 20   authenticate

Teil A: den Schlüssel bauen

Teil A ist der Ablauf, den ich für richtig halte, und bis auf die markierten Stellen genau der, den ich gegangen bin. Dort, wo der Befehl unten besser ist als das, was ich damals getippt habe, steht ein Hinweis dazu.

Voraussetzungen

gpg --version
# gpg (GnuPG) 2.4.4
# libgcrypt 1.10.3

GnuPG 2.4.x ist die Annahme für den ganzen Beitrag. Zwei Dinge gleich vorweg, weil sie sonst später wehtun:

  • --quick-generate-key verweigert den Dienst, wenn die UID bereits auf einem anderen Schlüssel im Schlüsselbund existiert. Genau deshalb ist dieser Schlüssel über eine Parameterdatei mit --batch --gen-key entstanden. Das trifft dich zuverlässig genau dann, wenn du einen Schlüssel rotierst, also in dem Moment, in dem der alte noch da ist.
  • Nichts hier braucht eine Smartcard, und nichts hier braucht root.

Wenn du bei GPG ganz am Anfang stehst, sind die Grundlagen zum Signieren und Verschlüsseln hier im Blog der bessere Einstieg. Dieser Beitrag setzt voraus, dass du weißt, was ein öffentlicher Schlüssel und ein Unterschlüssel sind.

Erst aufräumen, dann anfangen

Und zwar wirklich vorher, nicht hinterher. Mein ~/.gnupg war in einem Zustand, den ich hier nur ungern zugebe.

chmod 700 ~/.gnupg
find ~/.gnupg -type f -exec chmod 600 {} +

# Erst alle GnuPG-Frontends schließen und die Daemons beenden, sonst löschst du
# unter Umständen echte, aktive Locks:
gpgconf --kill all

# Übriggebliebene Lock-Dateien abgestürzter Läufe. Bei mir waren es 93 Stück.
find ~/.gnupg -name '.#lk*' -delete

# Altlasten, die keine 2.4er Installation mehr braucht
mkdir -p ~/gnupg-attic-$(date +%F)
mv ~/.gnupg/{cert8.db,key3.db,secmod.db} ~/gnupg-attic-$(date +%F)/ 2>/dev/null

Ein ~/.gnupg mit 775 und Dateien mit 664 ist erschreckend verbreitet, und ich will das gar nicht größer machen als es ist: Auf einem Einzelplatzrechner ist das keine Katastrophe. Es wird aber genau in dem Moment eine, in dem das Home-Verzeichnis in ein Backup, in einen Container-Mount oder in einen synchronisierten Ordner wandert. Und das passiert schneller, als man denkt.

Die gpg.conf, komplett

Das ist die tatsächlich laufende Konfiguration, so wie sie bei mir liegt, mit ihren Originalkommentaren. Der Block mit dem Warnhinweis ist die wichtigste Stelle im gesamten Beitrag, warum, steht in Teil B.

# ---- UX / output ----
keyid-format 0xlong
with-fingerprint
with-subkey-fingerprint
utf8-strings

# ---- Key discovery (local first, then WKD/DANE; email-validated keyserver last) ----
auto-key-retrieve
auto-key-locate local,wkd,dane,keyserver
keyserver hkps://keys.openpgp.org

# ---- Strong defaults ----
# cipher-algo / digest-algo are intentionally NOT forced: forcing them would override
# the recipient's stated capabilities. personal-*-preferences below select strong
# algorithms interoperably instead.
cert-digest-algo SHA512

# ---- KDF hardening for passphrase-derived symmetric keys (gpg -c / key export) ----
s2k-mode 3
s2k-digest-algo SHA512
s2k-cipher-algo AES256
s2k-count 65011712

# ---- WARNING. Legacy ciphers: do NOT disable 3DES here ----
# An earlier version of this file carried:
#     disable-cipher-algo 3DES
#     disable-cipher-algo IDEA
#     disable-cipher-algo CAST5
#     disable-cipher-algo BLOWFISH
#     disable-cipher-algo TWOFISH
# The 3DES line alone silently strips ALL algorithm preferences from every key you
# generate while it is active, and it also blocks DECRYPTION of old archives.
# New encryption never selects a legacy cipher anyway, because
# personal-cipher-preferences lists AES only.

# ---- Privacy / minimal metadata ----
no-comments
no-emit-version
export-options export-minimal

# ---- Listing/verification quality-of-life ----
verify-options show-uid-validity
list-options show-uid-validity

# ---- Trust model ----
trust-model tofu+pgp

# ---- Your key ----
default-key 0x45FCD081ADB54872EA5B06B9893DE0CDDE986DEB
# hidden-encrypt-to 0x45FCD081ADB54872EA5B06B9893DE0CDDE986DEB   # optional, off

# ---- Local policy: what *you* prefer when sending ----
personal-cipher-preferences AES256 AES192 AES
personal-digest-preferences SHA512 SHA384 SHA256
weak-digest SHA1
force-ocb

# ---- Preferences baked into keys you create from here on ----
default-preference-list SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed

Zwei Zeilen darin sind Entscheidungen und keine Selbstverständlichkeiten, deshalb je ein Satz dazu.

  • force-ocb ist keine Präferenz, sondern eine Erzwingung. Es sorgt dafür, dass ausgehende Nachrichten die OCB-Variante aus der LibrePGP-Linie benutzen, und setzt sich dabei über das hinweg, was der Empfängerschlüssel ankündigt. Sehr alte oder anders implementierte Empfänger können solche Nachrichten nicht lesen. Das steht in einer gewissen Spannung zu dem Prinzip ein paar Zeilen weiter oben, wo ich cipher-algo bewusst nicht erzwinge. Für mich ist das in Ordnung, in einer Konfiguration zum Abschreiben würde ich die Zeile weglassen.
  • auto-key-retrieve holt unbekannte Schlüssel beim Prüfen automatisch nach. Sehr bequem, und es verrät dem Keyserver, welche signierten Nachrichten du wann liest. Für mich ein akzeptabler Tausch, bei einem anderen Bedrohungsmodell schaltest du das besser ab.

Dazu die beiden kleinen Geschwisterdateien. gpg-agent.conf:

enable-ssh-support
default-cache-ttl 180
max-cache-ttl 600
pinentry-program /usr/bin/pinentry-gnome3

Und dirmngr.conf:

honor-http-proxy
disable-ldap

Den Hauptschlüssel erzeugen

Ein Hauptschlüssel, der nur zertifiziert, über eine Parameterdatei. Die Zeile Preferences: war in meinem echten Lauf nicht drin, nimm sie mit. Sie kostet nichts und fängt genau den Fehler ab, um den es in Teil B geht. Das ist keine Vermutung, ich habe es nachgemessen: Mit dieser Zeile bleiben die Präferenzen selbst dann im Schlüssel, wenn die defekte Konfiguration aktiv ist. Die eigentliche Kontrolle bleibt trotzdem der Paket-Dump gleich darunter, denn eine Parameterdatei ersetzt nie die Prüfung des fertigen Artefakts.

cat > keyparams.txt <<'EOF'
Key-Type: eddsa
Key-Curve: Ed25519
Key-Usage: cert
Name-Real: Sebastian van de Meer
Name-Email: kernel-error@kernel-error.com
Expire-Date: 5y
Preferences: AES256 AES192 AES SHA512 SHA384 SHA256 ZLIB BZIP2 ZIP Uncompressed
%ask-passphrase
%commit
EOF

gpg --batch --gen-key keyparams.txt
shred -u keyparams.txt

Und jetzt sofort nachsehen, ob die Präferenzen auch wirklich im Schlüssel gelandet sind. Genau diese Prüfung fehlte bei mir im Juli, und deshalb steht sie hier direkt hinter der Erzeugung und nicht irgendwo weiter unten.

gpg --export kernel-error@kernel-error.com | gpg --list-packets | grep -E 'pref-|features'

Du willst dort pref-sym-algos, pref-hash-algos, pref-zip-algos und ein features mit gesetztem Bit 0x01 sehen. Wenn dort nur eine features-Zeile steht und sonst nichts, dann hör hier auf und lies erst den Abschnitt über den Defekt in Teil B. Dann stimmt etwas mit deiner Konfiguration nicht, und der Schlüssel trägt den Fehler ab sofort dauerhaft mit sich herum.

Zum shred oben noch ein Wort, weil es sonst falsche Sicherheit erzeugt: Es entfernt die sichtbare Arbeitsdatei, mehr nicht. Auf SSDs, auf ZFS, bei Snapshots und auf journalenden Dateisystemen ist damit überhaupt nicht garantiert, dass keine alten Blöcke mehr herumliegen. Solche Zwischendateien sollten deshalb von vornherein nur auf einem verschlüsselten Dateisystem entstehen.

Die drei Unterschlüssel

Interaktives --quick-add-key ist mir in einer nicht-interaktiven Shell mit einem /dev/tty-Fehler um die Ohren geflogen. Mit --batch läuft es durch.

FPR=45FCD081ADB54872EA5B06B9893DE0CDDE986DEB
gpg --batch --quick-add-key $FPR ed25519  sign 5y
gpg --batch --quick-add-key $FPR cv25519  encr 5y
gpg --batch --quick-add-key $FPR ed25519  auth 5y

Die Foto-UID

240×288 Pixel, Graustufen-JPEG, 10851 Byte. Halt das Bild klein, es reist in jedem vollständigen Export mit und ist der mit Abstand größte Posten in der Schlüsselgröße.

gpg --edit-key $FPR
> addphoto
> /pfad/zu/sebastian-photo-uid.jpg
> save

Eine Sache dazu, die man vorher wissen sollte: Keiner der drei Keyserver, die ich benutze, hat die Foto-UID nach dem Upload je wieder herausgerückt. Für jeden Keyserver da draußen lege ich die Hand nicht ins Feuer, für die drei, auf die es ankommt, schon. Das Bild überlebt damit nur auf den Kanälen, die ich selbst hoste. Damit ist die Foto-UID Dekoration und keine Funktion. Das ist ein völlig legitimer Grund, sie trotzdem mitzunehmen, man sollte sich nur nichts vormachen.

Backup und Widerrufszertifikat, bevor irgendetwas veröffentlicht wird

Zwei Dinge müssen existieren, bevor der Schlüssel irgendwo landet: ein Backup des geheimen Schlüssels und ein Widerrufszertifikat. Beides später nachzuholen ist der Klassiker, den man genau einmal bereut.

gpg --export-secret-keys --armor $FPR > secret-key-backup.asc
gpg --output revoke.asc --gen-revoke $FPR

Ein Widerrufszertifikat legt GnuPG bei der Erzeugung ohnehin schon selbst unter ~/.gnupg/openpgp-revocs.d/<FPR>.rev ab. Das Backup dagegen ist erst dann eines, wenn du es einmal zurückgespielt hast. Also rein damit in ein Wegwerf-GNUPGHOME und nachsehen, ob der geheime Hauptschlüssel dort auch wirklich auftaucht und nicht bloß die Datei lesbar war. Danach wandern beide auf Offline-Medien.

RESTORE=$(mktemp -d); chmod 700 "$RESTORE"
gpg --homedir "$RESTORE" --import secret-key-backup.asc
gpg --homedir "$RESTORE" --with-subkey-fingerprint --list-secret-keys $FPR
rm -rf "$RESTORE"

Wichtig, weil es ein sehr verbreitetes Missverständnis ist: Der s2k-*-Block aus der gpg.conf betrifft den passphrasenbasierten Schutz, also etwa gpg -c und den Export. Er sagt nichts darüber aus, wie der geheime Schlüssel in ~/.gnupg auf der Platte geschützt ist. Darum kümmert sich der gpg-agent mit eigenen Parametern, und bereits vorhandenes Schlüsselmaterial wird durch eine geänderte gpg.conf nicht rückwirkend neu verpackt. Wer die beiden verwechselt, glaubt an eine Härtung, die an dieser Stelle gar nicht wirkt. Was tatsächlich in deinem Backup steht, siehst du wie immer am Artefakt selbst, per gpg --list-packets secret-key-backup.asc.

Den Hauptschlüssel aus dem Alltag nehmen

Jetzt kommt der Schritt, der die ganze Aufteilung von oben überhaupt erst einlöst. Der Hauptschlüssel hat im Alltag nichts verloren. Gebraucht wird er nur, wenn ein Unterschlüssel verlängert, ersetzt oder widerrufen wird, wenn eine UID dazukommt oder wenn du einen fremden Schlüssel zertifizierst. Also fliegt sein geheimer Teil vom Arbeitsrechner herunter, und zwar genau jetzt, nachdem Backup und Widerrufszertifikat existieren und nicht vorher.

# nur die Unterschlüssel exportieren, ohne den geheimen Hauptschlüssel
gpg --export-secret-subkeys --armor $FPR > subkeys.asc

# das gesamte geheime Material aus dem Alltags-Keyring werfen
gpg --delete-secret-keys $FPR

# und danach ausschließlich die Unterschlüssel zurückholen
gpg --import subkeys.asc
shred -u subkeys.asc

Kontrolliert wird das an einem einzigen Zeichen:

gpg -K
# sec#  ed25519/0x893DE0CDDE986DEB
# ssb   ed25519/0xD788641D8588A674
# ssb   cv25519/0x429D03637892821A
# ssb   ed25519/0x22F2E3234664DDBE

Das Doppelkreuz hinter sec ist der ganze Punkt. Es bedeutet: GnuPG kennt den Hauptschlüssel, hat sein geheimes Gegenstück aber nicht mehr. Signieren, Entschlüsseln und Authentisieren laufen unverändert weiter, dafür sind die Unterschlüssel zuständig. Was nicht mehr geht, ist alles, was die Identität selbst betrifft, also neue Unterschlüssel anlegen, Laufzeiten verlängern, UIDs ergänzen und fremde Schlüssel zertifizieren.

Der geheime Hauptschlüssel liegt ab hier zusammen mit dem Widerrufszertifikat auf verschlüsseltem Wechselmedium. Wenn ich ihn brauche, kommt er ausdrücklich nicht zurück nach ~/.gnupg, sondern in ein temporäres GNUPGHOME, das hinterher wieder verschwindet.

Ein Detail dabei ist wichtig genug für einen eigenen Absatz, weil es sonst still danebengeht: Das Offline-Backup ist ein Schnappschuss. Es entstand oben, bevor der alte Schlüssel gegengezeichnet hat und bevor die Governikus-Zertifizierung da war. Diese Fremdsignaturen hängen am öffentlichen Teil, der im Alltags-Keyring liegt, nicht im Backup. Wer nur das Backup einspielt und dort arbeitet, exportiert hinterher einen Schlüssel, dem genau diese Signaturen fehlen. Also immer beides einspielen:

# aktuellen öffentlichen Stand samt Fremdsignaturen aus dem Alltags-Keyring mitnehmen
gpg --armor --export-options no-export-minimal --export $FPR > /tmp/pub-aktuell.asc

export GNUPGHOME=$(mktemp -d); chmod 700 "$GNUPGHOME"
trap 'rm -rf "$GNUPGHOME"' EXIT HUP INT TERM   # auch bei Abbruch aufräumen
gpg --import /media/offline/secret-key-backup.asc
gpg --import /tmp/pub-aktuell.asc

# hier die Arbeit am Schlüssel, etwa eine Laufzeit verlängern

gpg --armor --export-options no-export-minimal --export $FPR > /tmp/pub-neu.asc
rm -rf "$GNUPGHOME"; unset GNUPGHOME

Danach importierst du die aktualisierte öffentliche Hälfte in den Alltags-Keyring und rollst sie über alle Veröffentlichungskanäle aus. Das ist unbequem. Es soll unbequem sein, denn genau diese Unbequemlichkeit ist der Grund, warum der Hauptschlüssel selten angefasst wird und deshalb schwer zu verlieren ist.

Den alten Schlüssel den neuen gegenzeichnen lassen

Ein weicher Übergang statt einer harten Kante: Der alte Schlüssel bleibt bis zu seinem Ablauf gültig und zertifiziert den neuen. Wer dem alten Schlüssel schon vertraut, bekommt damit einen kryptografischen Pfad zum neuen, ohne mich irgendwo anrufen zu müssen.

gpg --default-key 0x5F279C362EEAB216 --sign-key $FPR

Die externe Zertifizierung über Governikus

Das ist ein eigenes Thema, deshalb hier nur zusammengefasst: Governikus ist nach meinem Stand vom August 2026 der einzige mir bekannte noch aktive Dienst in Deutschland, der OpenPGP-Schlüssel über die Online-Ausweisfunktion zertifiziert. Du schickst deinen öffentlichen Schlüssel hin, weist dich mit dem Personalausweis aus und bekommst eine Zertifizierung mit Level 3 zurück. Die Volksverschlüsselung fällt als Alternative aus, die macht ausschließlich S/MIME nach X.509, erzeugt die Schlüssel selbst und wird ohnehin eingestellt.

Strukturell wichtig für den Rest des Beitrags ist nur ein Punkt: Diese Zertifizierung ist eine Fremdsignatur. Und die Hälfte meiner Veröffentlichungskanäle wirft Fremdsignaturen weg. Warum, steht gleich.

Vier Exporte für vier Aufgaben

Das ist der Teil, den fast alle Anleitungen überspringen, und ausgerechnet hier fallen die echten Entscheidungen. Vier verschiedene Exporte desselben Schlüssels für vier verschiedene Jobs, mit den gemessenen Größen der Artefakte, die tatsächlich ausgeliefert werden:

ArtefaktBefehlGrößeInhalt
minimal, ASCIIgpg --armor --export $FPR16772 B1 UID plus Foto, 3 Unterschlüssel, 5 Signaturen
vollständig, ASCIIgpg --armor --export-options no-export-minimal --export $FPR17833 Bzusätzlich 3 Fremdsignaturen, also 8 Signaturen
WKD, binärgpg --no-armor --export-options no-export-minimal --export $FPR13108 Bwie vollständig, nur binär
DANE, binär minimalsiehe unten1298 B1 UID, kein Foto, 4 Signaturen

Alle vier Befehle habe ich gegen die live ausgelieferten Dateien geprüft, sie reproduzieren die Artefakte byteidentisch, sha256 stimmt jeweils überein. Du kannst sie also so übernehmen.

Und jetzt die fiese Falle. In meiner gpg.conf steht global export-options export-minimal. Export-Optionen summieren sich, sie ersetzen einander nicht. --export-options export-clean hebt export-minimal also nicht auf. Du bekommst weiterhin einen minimalen Export, still und ohne Warnung, mit weggeworfenen Fremdsignaturen. Nur die ausdrückliche Verneinung funktioniert:

gpg --export-options export-clean      --export $FPR | gpg --list-packets | grep -c '^:signature packet:'   # 5, immer noch minimal
gpg --export-options no-export-minimal --export $FPR | gpg --list-packets | grep -c '^:signature packet:'   # 8, korrekt

Das ist genau die Sorte Fehler, bei der du fest davon überzeugt bist, einen Schlüssel mit allen Signaturen veröffentlicht zu haben, während in Wahrheit die eID-Zertifizierung nie das Haus verlassen hat.

Die zweite Stolperfalle beim Export: export-minimal wirft zwar Fremdsignaturen weg, behält aber die Foto-UID. Für den DANE-Record muss auch das Bild raus, sonst wächst der Record von rund einem Kilobyte auf gute zwölf. Dieser Befehl hat den ausgelieferten Record mit 1298 Byte erzeugt:

gpg --no-armor --export-options export-minimal --export-filter keep-uid='mbox = kernel-error@kernel-error.com' --export $FPR > openpgpkey.bin

Zwei Anmerkungen noch zu den Formaten, weil sich in der 2.4er Reihe etwas geändert hat:

  • --print-dane-records gibt es nicht mehr. Der Nachfolger heißt --export-options export-dane.
  • Verwechsle die beiden Ausgaben aber nicht. export-dane liefert einen fertigen DNS-Präsentationsblock, also $ORIGIN, Kommentarzeilen und generische TYPE61-Rdata, bei mir 25642 Byte. Das ist kein binäres Schlüsselmaterial. Zum Lesen ist es praktisch, der ausgelieferte Record entstand aber aus dem binären Export oben plus base64 -w0 openpgpkey.bin. Beide Wege sind gültige Zonefile-Syntax, RFC 7929 definiert die base64-Präsentationsform und RFC 3597 die generische TYPE61-Form, BIND frisst beides. Der Einzeiler passte einfach besser in meine bestehende Zone.

Wo der Schlüssel liegt, und warum genau dort

Sieben Kanäle, und jeder existiert aus einem anderen Grund. Das „warum“ ist dabei die interessantere Hälfte:

KanalFormatWarum dieser Kanal
keys.openpgp.orgvoll hochgeladen, Server wirft Signaturen und Foto wegder einzige Keyserver, der die Mailadresse validiert, und der, den moderne Clients abfragen
keyserver.ubuntu.comvollklassischer SKS-Nachfolger, behält Fremdsignaturen
pgpkeys.euvollzweiter Klassiker, Redundanz
DANE / OPENPGPKEYminimal binär, 1298 BVertrauen hängt an DNSSEC statt an einem Signaturgraphen, muss klein bleiben, es ist ein DNS-Record
WKD advancedkein eigener Export, CNAME auf wkd.keys.openpgp.orgspiegelt keys.openpgp.org automatisch, lässt sich hier nicht selbst hosten
WKD direct (Apex)voll binär, 13108 Bselbst gehostet, deshalb die einzige Stelle mit Foto und Fremdsignaturen
security.txt und Direktdownloadvoll ASCII, 17833 Bauffindbar für Menschen und für Scanner

Zwei abgeleitete Namen tauchen dabei auf, und die werden regelmäßig falsch gebildet. Beide habe ich unabhängig nachgerechnet, sie stimmen mit dem überein, was ausgeliefert wird:

# WKD: z-base32 des SHA-1 vom lokalen Teil, ASCII-Großbuchstaben vorher klein gemacht
#   sha1("kernel-error") -> z-base32 -> 3gyjbxx9xfdggpkmx5qdd793xy431w5u
gpg --with-wkd-hash -k kernel-error@kernel-error.com

# DANE (RFC 7929): SHA-256 des kanonisierten UTF-8-Lokalteils, auf 28 Oktette gekürzt,
# hex-kodiert, dann ._openpgpkey.<domain>
#   70e1c7d87e825b3aba45e2a478025ea0d91d298038436abde5a4c2d0._openpgpkey.kernel-error.com

Zwei Präzisierungen, damit hier keine Regel steht, die breiter ist als die Spezifikationen: WKD bildet ASCII-Großbuchstaben auf Kleinbuchstaben ab, das ist kein allgemeines Unicode-Lowercasing. Und DANE übernimmt diese Abbildung nicht einfach, RFC 7929 hat seine eigene Kanonisierung des lokalen Teils. Bei mir fallen beide zusammen, weil kernel-error schon reines Kleinbuchstaben-ASCII ist. Genau deshalb ist mir der Unterschied nie begegnet. Wer einen Lokalteil mit Großbuchstaben oder Nicht-ASCII hat, darf die beiden auf keinen Fall gleichsetzen.

Und weil ich schon dabei bin, mich zu blamieren: Bei einem früheren Review habe ich DANE als „fehlt“ markiert, weil ich den z-base32-Namen aus WKD gegen das DNS geprüft habe. Also den falschen Namen. DANE lief die ganze Zeit. Wenn du deinen eigenen Aufbau prüfst, prüfe bitte den Namen, den die jeweilige Spezifikation vorschreibt, und nicht den, den du gerade im Kopf hast. Wie man einen OPENPGPKEY-Record überhaupt in die Zone bekommt und wie man die Zone dafür mit BIND signiert, steht hier im Blog.

Eine Konsequenz aus der Tabelle überrascht die Leute regelmäßig, deshalb schreibe ich sie deutlich hin:

Die Governikus-Zertifizierung ist nur auf den klassischen Keyservern und auf den selbst gehosteten Kopien sichtbar. keys.openpgp.org wirft sie weg, und gpg bevorzugt die WKD-advanced-Methode, die wiederum auf keys.openpgp.org zeigt. Der Kanal, den die meisten Clients benutzen, ist also ausgerechnet der mit den wenigsten Signaturen.

Prüfen wie ein Fremder

Der letzte Schritt in Teil A, und der wichtigste: Hol dir deinen eigenen Schlüssel so, wie es jemand tut, der dich nicht kennt. Einmal pro Suchpfad, jedes Mal in einem frischen Schlüsselbund.

# den Schlüssel holen wie ein Fremder, einmal pro Lookup-Pfad
for m in dane wkd keyserver; do
  GNUPGHOME=$(mktemp -d) gpg --auto-key-locate clear,$m --locate-external-keys kernel-error@kernel-error.com
done

# welche Präferenzen bewirbt der Schlüssel tatsächlich?
gpg --export $FPR | gpg --list-packets | grep -E 'pref-|features'

# welchen Cipher wählt ein Absender wirklich?
gpg --status-fd 1 -d message.gpg | grep DECRYPTION_INFO

Damit endet das Rezept. Ab hier geht es darum, was die Teile bedeuten, und um die Frage, wie sicher der Aufbau am Ende wirklich ist.

Teil B: was das alles bedeutet

Wenn du bis hierher gekommen bist, hast du ein funktionierendes Rezept. Was du noch nicht hast, ist ein Gefühl dafür, warum die Teile so und nicht anders geschnitten sind, und wo der Aufbau trotz allem nachgibt. Genau darum geht es jetzt, und zwar in dieser Reihenfolge: erst die Struktur, dann die Zahlen, dann der Fehler und am Ende die Rechnung ohne Schönfärberei.

Warum ein Hauptschlüssel, der nur zertifiziert

Der Hauptschlüssel hat genau eine Aufgabe: zu sagen, wer zu diesem Schlüssel gehört. Er signiert die UIDs und er signiert die Unterschlüssel. Er signiert nie eine Mail und entschlüsselt nie irgendetwas. Alles Operative ist delegiert:

[C] certify   Identitätsanker. Wird selten benutzt. Eine Kompromittierung ist
              nicht reparierbar, ein Verlust nur über das Offline-Backup.
[S] sign      Signieren im Alltag
[E] encrypt   Entschlüsseln im Alltag
[A] auth      Authentisierung, etwa als SSH-Schlüssel

Der Gewinn: Ein kompromittierter oder ausgedienter Unterschlüssel lässt sich widerrufen und ersetzen, ohne die Identität anzufassen, ohne die gesammelten Fremdsignaturen zu verlieren und ohne den Fingerabdruck zu ändern, der an sieben Stellen veröffentlicht ist. Dass [S] und [E] getrennt sind, hat noch einen zweiten Grund: Sie haben unterschiedliche Lebenszyklen. Einen alten Signaturschlüssel zu vernichten ist harmlos. Einen alten Verschlüsselungsschlüssel zu vernichten heißt, dass du an dein Archiv nicht mehr herankommst.

Das Ganze zahlt sich allerdings nur aus, wenn du auch die Konsequenz ziehst: Der volle Nutzen dieser Aufteilung stellt sich erst ein, wenn der Hauptschlüssel offline liegt. Genau deshalb liegt er hier offline, auf verschlüsseltem Wechselmedium und zusammen mit dem Widerrufszertifikat. Er kommt nur heraus, wenn ein Unterschlüssel verlängert, ersetzt oder widerrufen werden muss, und dann in ein temporäres GNUPGHOME und nicht zurück in den Alltags-Keyring. Auf der Arbeitsmaschine liegen ausschließlich die drei Unterschlüssel. Ein Hauptschlüssel, der nur zertifiziert, aber trotzdem neben dem Mailclient herumliegt, ist am Ende nur Kosmetik.

Der [A]-Unterschlüssel existiert übrigens, ist aber bewusst nicht im gpg-agent für SSH verdrahtet, sshcontrol ist leer. Ich habe ihn erzeugt, um mir die Option offenzuhalten. Mehr ist es im Moment nicht.

Ed25519 und X25519, was die Zahlen bedeuten

  • Ed25519 zum Signieren und X25519, in GnuPG als cv25519 bezeichnet, zum Verschlüsseln liegen beide bei rund 128 Bit klassischem Sicherheitsniveau. Das ist vergleichbar mit RSA-3072 und deutlich oberhalb von RSA-2048.
  • Sie sind dramatisch kleiner und schneller. Deshalb passt der komplette Schlüssel mit drei Unterschlüsseln, Foto und drei Fremdsignaturen immer noch in 13 KB.
  • Curve25519 ist fehlbedienungsresistent entworfen: keine Parameter, die man falsch wählen kann, keine Invalid-Curve-Fallen, deterministische Nonces bei Ed25519. Ein großer Teil der historischen Signatur-Unfälle in der Praxis kam aus genau den Ecken, die diese Kurve wegräumt.

Ein Punkt dazu, den du im Kopf behalten solltest, weil er gleich noch wichtig wird: 128 Bit klassische Sicherheit sind die Obergrenze dieses Schlüssels. Merk dir die Zahl. Sie ist der Grund, warum die AES-Geschichte weiter unten weniger dramatisch ist, als sie zunächst klingt.

Algorithmen-Präferenzen: was sie sind und wer sie liest

Dieses Konzept muss sitzen, sonst funktioniert die Geschichte danach nicht. Vorweg ein Satz, den viele nie gehört haben: Eine OpenPGP-Nachricht ist immer hybrid. Der asymmetrische Teil, also dein Ed25519- und X25519-Material, verpackt ausschließlich einen zufällig erzeugten symmetrischen Sitzungsschlüssel. Die eigentlichen Nutzdaten verschlüsselt ein symmetrisches Verfahren, in der Regel AES. Der Fehler, um den es gleich geht, saß in dieser symmetrischen Hälfte.

Ein OpenPGP-Schlüssel trägt nämlich nicht nur öffentliche Schlüssel spazieren. Die Selbstsignatur jeder UID enthält Subpakete, die ankündigen, was der Besitzer verarbeiten kann:

hashed subpkt 11 (pref-sym-algos:  9 8 7)      AES256, AES192, AES128
hashed subpkt 21 (pref-hash-algos: 10 9 8)     SHA512, SHA384, SHA256
hashed subpkt 22 (pref-zip-algos:  2 3 1 0)    ZLIB, BZIP2, ZIP, unkomprimiert
hashed subpkt 34 (pref-aead-algos: 2)          OCB, fehlt auf diesem Schlüssel
hashed subpkt 30 (features: 05)                Feature-Bits, siehe unten
hashed subpkt 23 (keyserver preferences: 80)   "no-modify"

Das features-Oktett ist die Stelle, an der ich mich in meinen eigenen Notizen vertan hatte, deshalb hier die Dekodierung. In der Linie, die GnuPG 2.4 implementiert, bedeuten die Bits des ersten Oktetts:

0x01  SEIPD-v1 mit Modification Detection Code (MDC)
0x02  AEAD
0x04  Unterstützung für das v5-Schlüssel- und Fingerabdruckformat

features 05 ist also 0x01 + 0x04, das heißt MDC und kein AEAD. features 07 wäre 0x01 + 0x02 + 0x04, und genau das bekommt ein Schlüssel, den GnuPG 2.4 mit Standardeinstellungen erzeugt. Der kaputte Zustand war features 04, also 0x04 ganz allein: weder MDC noch AEAD angekündigt.

Eine Versionsfußnote gehört dazu: Diese Bitbelegung und das Subpaket 34 stammen aus der LibrePGP-Linie, die GnuPG 2.4 umsetzt. RFC 9580 hat beides geändert, dort sind die AEAD-Präferenzen in Subpaket 39 umgezogen und das Feature-Modell wurde umgebaut. Diese Spaltung kommt weiter unten noch einmal zurück.

Wenn dir jemand etwas verschlüsselt, liest dessen GnuPG deine Präferenzliste und wählt daraus das stärkste Verfahren, das beide können. Das ist der ganze Mechanismus. Daraus folgen zwei Dinge, die den meisten Leuten gegen den Strich gehen:

  • Deine eigenen personal-cipher-preferences schützen dich nicht. Die bestimmen, was du verschickst. Was Leute dir schicken, bestimmt allein das, was dein veröffentlichter Schlüssel ankündigt.
  • Die Präferenzen stehen in einer Signatur. Sie zu ändern heißt, die Selbstsignatur neu auszustellen. Und damit ist jede veröffentlichte Kopie deines Schlüssels veraltet, bis du sie erneuerst. Bei mir sind das sieben Kanäle und eine Änderung an der DNS-Zone.

Und wenn ein Schlüssel gar nichts ankündigt? Dann greift der Rückfall. RFC 4880 schreibt dafür 3DES vor, den verpflichtend zu implementierenden Algorithmus. Moderne GnuPG-Versionen landen dort allerdings nicht mehr: Seit der 2.3er Reihe verschlüsselt gpg grundsätzlich nicht mehr mit 64-Bit-Blockchiffren, dafür müsstest du ausdrücklich --allow-old-cipher-algos setzen. Der Rückfall endet deshalb bei AES-128. Nicht kaputt, aber eben auch nicht das, was der Schlüssel bekommen sollte.

Der Defekt, gemessen

Ein x-beliebiger Absender, der meinem brandneuen Schlüssel etwas verschlüsselt, sah das hier:

gpg: WARNING: cipher algorithm AES not found in recipient preferences
gpg: AES.CFB encrypted data
[GNUPG:] DECRYPTION_INFO 2 7 0        # 7 = AES128

Derselbe Absender, wenn er dem Schlüssel von 2023 schreibt, den der neue gerade ablöst:

[GNUPG:] DECRYPTION_INFO 2 9 0        # 9 = AES256

Der neue Schlüssel hatte überhaupt keine pref-sym-algos, keine pref-hash-algos und keine pref-zip-algos, dazu features 04 statt 05. Er wurde also schwächer angesprochen als sein Vorgänger, lautlos, und der einzige Hinweis darauf erschien auf einem Terminal, das jemand anderem gehört.

Die Ursache, und eine Korrektur an mir selbst

In meinen Arbeitsnotizen stand als Ursache: „mit einer --batch --gen-key-Parameterdatei ohne Preferences:-Zeile gebaut“. Das ist falsch. Eine Parameterdatei ohne diese Zeile erzeugt völlig ordentliche Präferenzen aus den eingebauten Defaults von GnuPG. Nachgeprüft, es kommt das hier heraus:

pref-sym-algos: 9 8 7 2 · pref-aead-algos: 2 · pref-hash-algos: 10 9 8 11 2 · features: 07

Die echte Ursache saß in der gpg.conf. Ich habe die Juli-Konfiguration in einem Wegwerf-Schlüsselbund nachgestellt, der Defekt war sofort wieder da. Danach habe ich die Datei halbiert, bis eine einzige Zeile übrig blieb:

Konfiguration im TestErgebnis
Juli-gpg.conf, unverändertfeatures: 04, gar keine pref-*
ohne disable-cipher-algo 3DESpref-sym-algos: 9 8 7 2, features: 07
ohne alle disable-cipher-algo-Zeilenpref-sym-algos: 9 8 7 2, features: 07
ohne cipher-algo AES256weiterhin kaputt, features: 04
nur disable-cipher-algo 3DES vorhandenkaputt, features: 04
disable-cipher-algo 3DES plus explizite Preferences:-Zeilesauber, pref-sym-algos: 9 8 7, features: 05

disable-cipher-algo 3DES ist also notwendig und hinreichend, um den Defekt auszulösen. Eine Zeile, mehr nicht.

Der Mechanismus dahinter ist die eigentliche Pointe des ganzen Beitrags. 3DES ist in OpenPGP der verpflichtend zu implementierende Algorithmus, und in der Standardliste steht er auch sichtbar drin: pref-sym-algos: 9 8 7 2 endet auf der 2, und die 2 ist 3DES. Nimmst du GnuPG diesen einen Algorithmus lokal weg, verschwindet nicht nur er aus der Liste, sondern die Liste als Ganzes.

Beim Warum bleibe ich vorsichtig. Dass GnuPG intern keine gültige Liste mehr konstruieren kann und deshalb gar keine schreibt, ist die naheliegende Erklärung, beweisen lässt sie sich von der Kommandozeile aus nicht. Belegt ist der Auslöser, nicht der Code-Pfad dahinter. Der Effekt selbst ist dagegen eindeutig: Die Härtungszeile hat nicht einen schwachen Algorithmus aus der Liste entfernt, sie hat die komplette Liste entfernt. Und damit dafür gesorgt, dass Absender zurückfallen, und zwar auf etwas Schwächeres als das, was ich gerade weghärten wollte.

Das Labor zum Selbernachbauen

Zwei Minuten, kein echter Schlüssel wird angefasst, alles passiert in Wegwerf-Verzeichnissen unter /tmp. Wenn du mir nicht glaubst, ist das hier der schnellste Weg, es selbst zu sehen:

SP=$(mktemp -d)
mk() {  # $1 = Name, $2 = bad|good
  export GNUPGHOME="$SP/$1"; mkdir -p "$GNUPGHOME"; chmod 700 "$GNUPGHOME"
  [ "$2" = bad ] && echo "disable-cipher-algo 3DES" > "$GNUPGHOME/gpg.conf"
  cat > "$SP/p.txt" <<EOF
Key-Type: eddsa
Key-Curve: Ed25519
Key-Usage: sign
Subkey-Type: ecdh
Subkey-Curve: cv25519
Subkey-Usage: encrypt
Name-Real: Demo $1
Name-Email: $1@example.invalid
Expire-Date: 1y
%no-protection
%commit
EOF
  gpg --batch --gen-key "$SP/p.txt" 2>/dev/null
  gpg --export -a "$1@example.invalid" > "$SP/$1.asc"
}
mk nopref bad
mk withpref good

export GNUPGHOME="$SP/sender"; mkdir -p "$GNUPGHOME"; chmod 700 "$GNUPGHOME"
echo "auto-key-locate local" > "$GNUPGHOME/gpg.conf"
gpg -q --import "$SP"/*.asc
echo hi > "$SP/m.txt"
for r in nopref withpref; do
  gpg --trust-model always --yes -e -r "$r@example.invalid" -o "$SP/$r.gpg" "$SP/m.txt"
  printf '%-9s ' "$r:"
  GNUPGHOME="$SP/$r" gpg -q -d "$SP/$r.gpg" 2>&1 | grep -i 'encrypted data'
done

Bei mir kommt das hier heraus, und die eine Zeile Unterschied ist der ganze Defekt:

nopref:   gpg: WARNING: cipher algorithm AES not found in recipient preferences
          gpg: AES.CFB encrypted data
withpref: gpg: AES256.OCB encrypted data

Die Reparatur

Der Fix selbst ist unspektakulär, ein einziger Befehl im Editiermodus. Weil setpref die Selbstsignatur neu ausstellt, braucht er den Hauptschlüssel. Das Ganze läuft also einmal im temporären GNUPGHOME von weiter oben, mit dem Offline-Schlüssel und dem aktuellen öffentlichen Stand:

gpg --edit-key $FPR
> setpref AES256 AES192 AES SHA512 SHA384 SHA256 ZLIB BZIP2 ZIP Uncompressed
> y
> save

Danach steht da pref-sym 9 8 7, pref-hash 10 9 8, pref-zip 2 3 1 0 und features 05. Der Fingerabdruck bleibt unverändert, und die Governikus-Zertifizierung, die Gegensignatur des alten Schlüssels, die Foto-UID und sämtliche Ablaufdaten überleben. Das schreibe ich so ausdrücklich hin, weil viele Leute Angst vor setpref haben und glauben, es beschädige den Schlüssel. Tut es nicht. Was es tut: Es stellt die Selbstsignatur neu aus. Und damit müssen anschließend alle sieben Veröffentlichungskanäle aufgefrischt werden.

Zur Sicherheit gleich hinterher: default-preference-list in der gpg.conf festgenagelt, und die disable-cipher-algo-Zeilen sind endgültig raus.

Wie schlimm war es wirklich?

Hier will ich ehrlich sein statt dramatisch, sonst wird das hier auch nur wieder eine von diesen „ich habe einen Bug gefunden“-Geschichten.

AES-128 statt AES-256 ist real, aber überschaubar. AES-128 ist nicht gebrochen, es gibt keinen praktischen Angriff darauf. Und erinnere dich an die Zahl von weiter oben: Der asymmetrische Teil dieses Schlüssels liefert ohnehin rund 128 Bit klassische Sicherheit. Gemessen an der reinen Schlüsselsuche war AES-128 also nicht das schwächste Glied in der Kette, es hat lediglich mit dem Rest gleichgezogen. Über Implementierungsfehler, Seitenkanäle oder Protokollschwächen sagt dieser Vergleich nichts. Die faire Einordnung lautet: Das war ein Hygiene- und Signalisierungsfehler, keine ausnutzbare Schwachstelle. Bemerkenswert ist er, weil er lautlos war, automatisch passierte und von einer Härtungsmaßnahme verursacht wurde.

Das fehlende MDC-Bit sah schlimmer aus, als es war. features 04 heißt, dass das Bit 0x01 fehlte, mit dem ein Schlüssel Modification Detection ankündigt. Auf dem Papier lädt das einen Absender dazu ein, auf ein Paket ohne Integritätsschutz zurückzufallen, und das ist die Ecke, aus der EFAIL kam. Gemessen kam aber das hier heraus:

[GNUPG:] DECRYPTION_INFO 2 7 0
[GNUPG:] GOODMDC

Ein Absender mit GnuPG 2.4.x in Standardkonfiguration hat trotz des fehlenden Bits ein integritätsgeschütztes SEIPD-v1-Paket erzeugt und GOODMDC gemeldet. MDC ist dort schlicht immer an. Herauskommen aus dem Integritätsschutz muss man in dieser Version aktiv wollen, etwa über --rfc2440, das ausdrücklich den alten Modus ohne MDC erzeugt. Die tatsächliche Integritätslücke gegenüber einem 2.4.x-Absender mit Standardeinstellungen war damit null.

Diese Aussage gilt exakt so weit wie die Messung und keinen Meter weiter. Über andere Implementierungen oder ältere GnuPG-Versionen sagt sie nichts, und genau dort könnte ein fehlendes MDC-Signal im Prinzip sehr wohl noch eine Rolle spielen. „Kein aktueller Absender ist betroffen“ wäre schlicht gelogen, und irgendwer würde es nachprüfen.

Was wirklich Alarm verdient, ist keines von beiden für sich, sondern die Art des Versagens. Ein Schlüssel kann strukturell perfekt sein und trotzdem still auf den nackten Rückfallwert heruntergehandelt werden, ohne dass irgendwer etwas davon mitbekommt. Die einzige Diagnose erscheint auf einer Maschine, die dir nicht gehört. Weder gpg --list-keys noch --check-sigs noch irgendeine Keyserver-Seite zeigt dir das. Du musst dir Signatur-Subpakete ansehen, und das macht praktisch niemand.

Ein Punkt bleibt offen: AEAD

Der reparierte Schlüssel steht bei features 05 und hat keine pref-aead-algos. Gemessen bedeutet das:

AES256.CFB encrypted data      # reparierter Schlüssel, features 05
AES256.OCB encrypted data      # frisch erzeugter Schlüssel, features 07 plus pref-aead-algos: 2

setpref hat das MDC-Bit zurückgeholt, aber nie eine AEAD-Ankündigung ergänzt, weil der Schlüssel aus dem kaputten Zustand heraus repariert und nicht neu erzeugt wurde. Nachrüsten ginge, setpref … OCB liefert pref-aead-algos: 2 und features 07, auch das habe ich geprüft.

Ich lasse es trotzdem so. AES256-CFB mit MDC ist solide. Vor allem aber ist AEAD genau die Stelle, an der OpenPGP derzeit auseinanderläuft: Das AEAD von GnuPG 2.4 folgt der LibrePGP-Linie, RFC 9580 spezifiziert eine andere Konstruktion namens SEIPD v2. AEAD auf einem breit veröffentlichten Schlüssel anzukündigen bringt heute eine marginale Verbesserung und ein echtes Interoperabilitätsrisiko. Wer sich für die andere Baustelle im selben Themenfeld interessiert: Was in einem modernen Handshake steckt, habe ich am Beispiel X25519MLKEM768 auseinandergenommen. Dasselbe Argument gilt übrigens für force-ocb in meiner lokalen Konfiguration, das betrifft nur, was ich selbst verschicke, und ist eine bewusst etwas vorwärtsgewandte Entscheidung.

Ehrliche Gesamteinschätzung

  • Die Kryptografie ist in Ordnung. Ed25519, X25519, SHA-512 und AES-256 sind eine moderne, unaufgeregte Auswahl ohne bekannte praktische Schwächen, bei rund 128 Bit klassischer Sicherheit.
  • Die Struktur ist besser als der Durchschnitt. Hauptschlüssel nur zum Zertifizieren, getrennte Unterschlüssel, begrenzte Laufzeit von fünf Jahren, vorab erzeugtes Widerrufszertifikat und ein Backup, das nachweislich wiederherstellbar ist.
  • Das schwächste Glied ist kein Algorithmus. Es ist die Tatsache, dass die drei Alltags-Unterschlüssel auf einer dauerhaft laufenden Arbeitsmaschine liegen. Wer diese Maschine und die Passphrase bekommt, erzeugt bis zum Widerruf vollgültige Signaturen in meinem Namen und liest mein Archiv mit. Nach außen ist er also erst einmal ich. Was er nicht bekommt, ist der Identitätsanker: keine neue UID zertifizieren, keinen neuen Unterschlüssel binden, und der Fingerabdruck bleibt meiner. Ein kompromittierter Unterschlüssel fliegt raus und wird ersetzt, die Identität überlebt. Eine Smartcard würde den Rest weiter verkleinern, weil sich Schlüsselmaterial von der Karte nicht kopieren lässt. Sie löst es nicht: Steckt die Karte und ist sie entsperrt, wird eben auf der Karte signiert. Auf der Liste steht sie trotzdem.
  • Das Vertrauen in die Bindung ist geschichtet, nicht absolut. DNSSEC-signiertes DANE und selbst gehostetes WKD sind stark. Die Governikus-Zertifizierung ist die einzige echte Namens-Bindung. Das klassische Web of Trust trägt praktisch nichts mehr bei, die persönlichen Signaturen auf dieser Identität hängen an einem Schlüssel von 2011.
  • Post-Quantum: bewusst nicht adressiert. GnuPG 2.4.4 kann kein PQC, und die PQC-Arbeit in OpenPGP steckt noch im Entwurfsstadium mit praktisch keiner Interoperabilität. Curve25519 bietet gegen einen kryptografisch relevanten Quantencomputer keinerlei Widerstand, „heute sammeln, später entschlüsseln“ trifft also auf alles zu, was heute an diesen Schlüssel verschlüsselt wird. Ein hybrider Unterschlüssel lässt sich später ergänzen, ohne den Schlüssel neu zu bauen. Das ist eine der Auszahlungen des Unterschlüssel-Aufbaus von oben.

Was man daraus mitnimmt

Vier Punkte, und die gelten weit über OpenPGP hinaus:

  • Härtung kann schwächen. Einen verpflichtenden Basisalgorithmus zu entfernen hat die Verhandlung nicht verengt, sondern zerstört. Bevor du etwas abschaltest, das eine Spezifikation vorschreibt, sieh nach, was die Maschinerie drumherum macht, wenn es fehlt.
  • Prüfe das Artefakt, nicht den Befehl. gpg --batch --gen-key ist mit Rückgabewert 0 durchgelaufen und hat einen wunderschön aussehenden Schlüssel erzeugt. Sichtbar war der Defekt ausschließlich in --list-packets.
  • Konfiguration und Schlüssel sind nicht unabhängig. Der Schlüssel hat einen Defekt dauerhaft von der Konfiguration geerbt, die in den fünfzehn Sekunden aktiv war, in denen er entstand.
  • Manche Defekte sieht man nur von außen. Das Symptom erschien auf dem Terminal des Absenders. Teste deinen eigenen Schlüssel so, wie ein Fremder ihn benutzt. Genau dafür ist die Schleife mit mktemp -d und --locate-external-keys weiter oben da.

Und die Kurzfassung für alle, die nur bis hierher gescrollt haben: Ein moderner Schlüssel ist nicht automatisch ein korrekt konfigurierter Schlüssel. Der Unterschied steckt in Signatur-Subpaketen, die dir kein einziges Standardwerkzeug von sich aus zeigt.

Siehe auch

Wenn du deinen eigenen Schlüssel gerade nachgeprüft hast und dort etwas anderes steht als erwartet, oder wenn ich mich irgendwo irre, dann dürft ihr mich sehr gerne fragen.

« Ältere Beiträge

© 2026 -=Kernel-Error=-RSS

Theme von Anders NorénHoch ↑