[{"id":5453,"date":"2026-09-12T13:50:18","date_gmt":"2026-09-12T11:50:18","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5453"},"modified":"2026-09-12T13:50:18","modified_gmt":"2026-09-12T11:50:18","slug":"keyoxide-linux-app-flathub-android-matrix-kanal","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/09\/12\/keyoxide-linux-app-flathub-android-matrix-kanal\/","title":{"rendered":"Keyoxide: die Linux-App ist im Flathub, dazu ein Android-Update und ein neuer Matrix-Kanal"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">Im August ging es hier darum, wie ich meinen OpenPGP-Schl\u00fcssel \u00fcber Keyoxide an DNS, GitHub und Matrix gebunden habe. Seitdem hat sich beim Werkzeug selbst einiges getan, und das ist einen kurzen Nachtrag wert: es gibt jetzt eine Linux-App im Flathub, die Android-App wurde aktualisiert, und f\u00fcr Fragen rund um Keyoxide gibt es einen eigenen Matrix-Kanal.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Die Linux-App im Flathub<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bisher habe ich mein eigenes Profil und die meiner Kontakte immer im Browser aufgerufen. Jetzt gibt es daf\u00fcr eine eigenst\u00e4ndige Anwendung, installierbar direkt aus dem <a href=\"https:\/\/flathub.org\/en\/apps\/org.keyoxide.Keyoxide\" target=\"_blank\" rel=\"noopener\">Flathub<\/a>. Wichtig f\u00fcr die Einordnung: das ist ein Community-Paket, gepflegt vom Entwickler Berker und nicht offiziell vom Keyoxide-Projekt selbst herausgegeben, auf der Flathub-Seite steht das auch so. Unter der Haube steckt dieselbe Flutter-Codebasis wie in der Android-App, `keyoxide-flutter`.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Auf meinem Linux Mint installiert, sieht der Einstieg schlicht aus: ein Suchfeld f\u00fcr ein beliebiges Profil, und darunter die eigene Profilansicht mit allen best\u00e4tigten Identity-Claims.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-suche.png\"><img loading=\"lazy\" decoding=\"async\" width=\"417\" height=\"949\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-suche.png\" alt=\"Keyoxide-App unter Linux Mint als Flatpak, Startbildschirm mit dem Suchfeld f\u00fcr ein Profil.\" class=\"wp-image-5450\" style=\"width:260px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-suche.png 417w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-suche-132x300.png 132w\" sizes=\"auto, (max-width: 417px) 100vw, 417px\" \/><\/a><figcaption class=\"wp-element-caption\">Die Keyoxide-App aus dem Flathub, Startbildschirm mit Suchfeld.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-profil.png\"><img loading=\"lazy\" decoding=\"async\" width=\"419\" height=\"946\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-profil.png\" alt=\"Keyoxide-App zeigt das eigene Profil mit vier best\u00e4tigten Identity-Claims und dem \u00f6ffentlichen Schl\u00fcssel \u00fcber WKD.\" class=\"wp-image-5451\" style=\"width:260px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-profil.png 419w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-profil-133x300.png 133w\" sizes=\"auto, (max-width: 419px) 100vw, 419px\" \/><\/a><figcaption class=\"wp-element-caption\">Das eigene Profil in der App, alle vier Claims mit gr\u00fcnem Haken.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Inhaltlich \u00e4ndert das nichts an dem, was im August-Beitrag steht, DNS, GitHub und Matrix bleiben best\u00e4tigt, aber eine native App ist f\u00fcr den t\u00e4glichen Gebrauch angenehmer als jedes Mal die richtige URL zu tippen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Das Android-Update<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Parallel zur Linux-App wurde auch die Android-Version aktualisiert, <a href=\"https:\/\/codeberg.org\/Berker\/keyoxide-flutter\/releases\/tag\/2.4.4\" target=\"_blank\" rel=\"noopener\">Version 2.4.4<\/a> auf Codeberg. Gleicher Entwickler, gleiche Codebasis, zwei Plattformen aus einer Quelle.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der QR-Code, einmal nachgemessen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die App zeigt unter &#8222;Profile ID QR&#8220; einen QR-Code f\u00fcrs eigene Profil, darunter als Klartext den sogenannten Key Link, die WKD-Adresse des \u00f6ffentlichen Schl\u00fcssels. Bevor der hier landet, wollte ich wissen, ob der Code wirklich auf die richtige Datei zeigt und nicht auf einen veralteten Stand.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-qr-code.png\"><img loading=\"lazy\" decoding=\"async\" width=\"418\" height=\"947\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-qr-code.png\" alt=\"Keyoxide-App zeigt den Profile-ID-QR-Code mit dem Key-Link zur WKD-Datei des \u00f6ffentlichen Schl\u00fcssels.\" class=\"wp-image-5452\" style=\"width:260px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-qr-code.png 418w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/keyoxide-flatpak-qr-code-132x300.png 132w\" sizes=\"auto, (max-width: 418px) 100vw, 418px\" \/><\/a><figcaption class=\"wp-element-caption\">Profile ID QR mit dem Key Link im Klartext darunter.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zwei Dinge lassen sich hier unabh\u00e4ngig nachrechnen. Erstens der Pfad selbst: Web Key Directory bildet die Adresse aus einem SHA-1-Hash des lokalen Teils der E-Mail-Adresse, Z-Base-32-kodiert. F\u00fcr <code>kernel-error@kernel-error.com<\/code> ergibt <code>sha1(\"kernel-error\")<\/code>, Z-Base-32-kodiert, exakt <code>3gyjbxx9xfdggpkmx5qdd793xy431w5u<\/code>, also genau der Wert im Key Link. Zweitens der Inhalt: die Datei unter diesem Pfad l\u00e4sst sich herunterladen und mit <code>gpg --show-keys<\/code> auswerten, der Fingerabdruck darin ist <code>45FC D081 ADB5 4872 EA5B 06B9 893D E0CD DE98 6DEB<\/code>, derselbe wie im Screenshot und derselbe wie der seit Juli aktuelle Schl\u00fcssel. Der QR-Code zeigt also tats\u00e4chlich auf die richtige, aktuelle Datei, nicht auf einen Cache-Stand oder eine falsche Kodierung.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Ein Matrix-Kanal f\u00fcr Fragen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wer selbst mit Keyoxide anfangen will oder unterwegs h\u00e4ngen bleibt, findet jetzt einen eigenen Raum f\u00fcr allgemeine Fragen: <a href=\"https:\/\/matrix.to\/#\/#keyoxide:matrix.org\" target=\"_blank\" rel=\"noopener\">#keyoxide:matrix.org<\/a>. Ich lese dort mit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/02\/keyoxide-openpgp-schluessel-an-online-identitaeten-binden\/\" data-type=\"post\">Keyoxide: den OpenPGP-Schl\u00fcssel an Online-Identit\u00e4ten binden<\/a>, der urspr\u00fcngliche Beitrag mit allen vier Claims und der Geschichte zum zur\u00fcckgezogenen f\u00fcnften.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/02\/openpgp-schluessel-ed25519-bauen-gnupg-haertung-praeferenzen\/\" data-type=\"post\">Einen modernen OpenPGP-Schl\u00fcssel bauen<\/a>, das Rezept f\u00fcr genau den Schl\u00fcssel, der hier \u00fcber WKD verifiziert wird.<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eigene Erfahrungen mit Keyoxide, der Flatpak- oder der Android-App? Dann darfst du mich sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Der Keyoxide-Beitrag von August bekommt einen kurzen Nachtrag: die Linux-App liegt jetzt im Flathub, die Android-App wurde aktualisiert, und f\u00fcr Fragen gibt es einen eigenen Matrix-Kanal. Dazu der Faktencheck, ob der QR-Code in der App wirklich auf die richtige Datei zeigt.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[272,13,276],"tags":[74,377,134,371,445,301,446,372],"class_list":["post-5453","post","type-post","status-publish","format-standard","hentry","category-it-security","category-kernel-error-blog","category-self-hosting-infrastruktur","tag-dnssec","tag-encryption","tag-gpg","tag-infosec","tag-keyoxide","tag-matrix","tag-openpgp","tag-selfhosted","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5453"}],"version-history":[{"count":1,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5453\/revisions"}],"predecessor-version":[{"id":5454,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5453\/revisions\/5454"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5410,"date":"2026-09-11T10:00:00","date_gmt":"2026-09-11T08:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5410"},"modified":"2026-09-11T09:57:39","modified_gmt":"2026-09-11T07:57:39","slug":"quic-dual-stack-socket-df-bit-nginx-freebsd","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/09\/11\/quic-dual-stack-socket-df-bit-nginx-freebsd\/","title":{"rendered":"QUIC auf Dual-Stack-Sockets: das DF-Bit, das nginx f\u00fcr IPv4-Clients nie setzt"},"content":{"rendered":"<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-1024x576.png\" alt=\"nginx auf FreeBSD mit QUIC\/HTTP\/3: IPv6 ohne Router-Fragmentierung und IPv4 mit fehlendem DF-Bit auf einem Dual-Stack-Socket.\" class=\"wp-image-5411\" style=\"width:160px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-1024x576.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-300x169.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-768x432.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-1536x864.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx-676x380.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/09\/quic-nginx.png 1672w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Listener f\u00fcr IPv4 und IPv6 zusammen klingt nach der besseren L\u00f6sung. Ein Socket statt zwei, eine Konfigurationszeile weniger, ggf. ein Firewall-Regelwerk weniger zu pflegen. Bei QUIC auf diesem Blog lief das genau so, seit hier HTTP\/3 aktiviert ist. Am 9. September 2026 ist mir aufgefallen, dass genau dieser eine Socket seit l\u00e4ngerem daf\u00fcr sorgt, dass nginx f\u00fcr die H\u00e4lfte der Clients ein Bit im IP-Header nie setzt, das QUIC eigentlich zwingend braucht. Kein Absturz, keine Fehlermeldung, kein Eintrag im Error-Log. Nur ein messbar h\u00f6herer Anteil langsamer Verbindungen, der sich erst beim genauen Hinsehen als das entpuppt, was er ist.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dieser Beitrag ist eine klassische Root-Cause-Analyse. Bug gefunden, Ursache im nginx-Quellcode verifiziert, Ursache zus\u00e4tzlich im FreeBSD-Kernel nachvollzogen, gefixt, mit echten Produktionsdaten so gut wie m\u00f6glich belegt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Ein Socket f\u00fcr beides, das ist doch die naheliegende Wahl<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wer nginx mit QUIC betreibt, kennt die Direktive \u00fcblicherweise so:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">listen [::]:443 quic reuseport ipv6only=off default_server;<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein einziger IPv6-Socket mit <code>ipv6only=off<\/code> nimmt sowohl echte IPv6-Verbindungen als auch IPv4-Verbindungen an, dank IPv4-mapped-IPv6-Adressen (<code>::ffff:a.b.c.d<\/code>) nach <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc3493.html\" target=\"_blank\" rel=\"noopener\">RFC 3493<\/a>. Ein Socket statt zwei, eine Zeile Konfiguration weniger. Genau deshalb ist das die naheliegende Wahl, wenn man von TCP kommt, wo dieser Trick seit Jahrzehnten problemlos funktioniert.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei QUIC ist genau dieser eine Socket ein Problem, und zwar eines, das man im laufenden Betrieb nicht sieht. Zumindest nicht sofort und nicht \u00fcberall. Es m\u00fcssen schon ein paar Dinge zusammen kommen um das Problem selbst zu f\u00fchlen. F\u00fchlen? Eine lllaaaaannnngggggssssaaaaammmmeeeee Verbindung \u00fcber IPv4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Warum QUIC bei Fragmentierung empfindlicher ist als TCP \u00fcber TLS<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9000.html#section-14\" target=\"_blank\" rel=\"noopener\">RFC 9000, Abschnitt 14<\/a>, formuliert das ziemlich hart:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-default is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">UDP datagrams MUST NOT be fragmented at the IP layer. In IPv4, the Don&#8217;t Fragment (DF) bit MUST be set if possible, to prevent fragmentation on the path.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das ist keine Empfehlung, das ist ein MUST NOT direkt neben einem MUST. QUIC ermittelt seine eigene passende Paketgr\u00f6\u00dfe f\u00fcr einen Netzwerkpfad, entweder \u00fcber klassisches PMTUD (das auf ICMP-Antworten angewiesen ist und auf der IP-Ebene ansetzt, das DF-Bit ist Teil genau dieses Mechanismus) oder \u00fcber DPLPMTUD nach <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8899.html\" target=\"_blank\" rel=\"noopener\">RFC 8899<\/a>, das mit eigenen Sondierungspaketen arbeitet und ganz ohne ICMP auskommt. Wichtig ist hier nicht, welcher der beiden Mechanismen die Gr\u00f6\u00dfe ermittelt, sondern was beide voraussetzen: ein zu gro\u00dfes Paket muss sauber verworfen werden, mit oder ohne ICMP-Antwort, statt fragmentiert beim Empf\u00e4nger anzukommen. Wer sich jetzt an ICMPv6 f\u00fcr IPv6 erinnert f\u00fchlt, japp \ud83d\ude42<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Genau das geht in vielen Netzen schief, sobald ein Paket doch fragmentiert wird. Fragment-Filterung ist eine verbreitete Firmen- und Provider-H\u00e4rtung, gerade weil fragmentierte Pakete historisch f\u00fcr allerlei Unfug missbraucht wurden. Solche Netze werfen fragmentierte QUIC-Pakete einfach weg, ohne jede R\u00fcckmeldung. QUICs eigene Verlusterkennung kann das von gew\u00f6hnlichem Paketverlust nicht unterscheiden, im schlechtesten Fall wird daraus eine Serie von Retransmissions und Timeouts statt eines schnellen, sauberen Fehlers. Bei TCP \u00fcber TLS w\u00fcrde ein fragmentiertes Paket in den allermeisten Netzen einfach ankommen und zusammengesetzt werden, das ist dort seit jeher normaler Betrieb und niemanden juckt das DF-Bit besonders. Bei QUIC ist Fragmentierung der Fall, den das Protokoll bewusst ausschlie\u00dfen will, und genau der tritt hier auf.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Die Ursache, gefunden im nginx-Quellcode<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Fund steckt in <a href=\"https:\/\/github.com\/nginx\/nginx\/blob\/master\/src\/core\/ngx_connection.c\" target=\"_blank\" rel=\"noopener\"><code>src\/core\/ngx_connection.c<\/code><\/a>, in der Funktion <code>ngx_configure_listening_sockets()<\/code> (nicht zu verwechseln mit <code>ngx_open_listening_sockets()<\/code>, einer anderen Funktion in derselben Datei, die die Sockets \u00f6ffnet, aber nicht die MTU-Discovery-Optionen setzt). Diese Funktion l\u00e4uft beim Start und bei jedem Restart \u00fcber alle bereits gebundenen Listen-Sockets und setzt dort die passenden Socket-Optionen. Der entscheidende Ausschnitt, w\u00f6rtlich aus nginx 1.30.4:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#if (NGX_HAVE_IP_MTU_DISCOVER)\n\n        if (ls[i].quic &amp;&amp; ls[i].sockaddr->sa_family == AF_INET) {\n            value = IP_PMTUDISC_DO;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IP, IP_MTU_DISCOVER,\n                           (const void *) &amp;value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IP_MTU_DISCOVER) \"\n                              \"for %V failed, ignored\",\n                              &amp;ls[i].addr_text);\n            }\n        }\n\n#elif (NGX_HAVE_IP_DONTFRAG)\n\n        if (ls[i].quic &amp;&amp; ls[i].sockaddr->sa_family == AF_INET) {\n            value = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IP, IP_DONTFRAG,\n                           (const void *) &amp;value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IP_DONTFRAG) \"\n                              \"for %V failed, ignored\",\n                              &amp;ls[i].addr_text);\n            }\n        }\n\n#endif\n\n#if (NGX_HAVE_INET6)\n\n#if (NGX_HAVE_IPV6_MTU_DISCOVER)\n\n        if (ls[i].quic &amp;&amp; ls[i].sockaddr->sa_family == AF_INET6) {\n            value = IPV6_PMTUDISC_DO;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER,\n                           (const void *) &amp;value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IPV6_MTU_DISCOVER) \"\n                              \"for %V failed, ignored\",\n                              &amp;ls[i].addr_text);\n            }\n        }\n\n#elif (NGX_HAVE_IP_DONTFRAG)\n\n        if (ls[i].quic &amp;&amp; ls[i].sockaddr->sa_family == AF_INET6) {\n            value = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_DONTFRAG,\n                           (const void *) &amp;value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IPV6_DONTFRAG) \"\n                              \"for %V failed, ignored\",\n                              &amp;ls[i].addr_text);\n            }\n        }\n\n#endif\n#endif<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Beide Zweige pr\u00fcfen <code>ls[i].sockaddr-&gt;sa_family<\/code>, also die Adressfamilie des Listen-Sockets selbst, fest zum Zeitpunkt des Socket-Setups. Nicht die Familie des Ziels, an das ein bestimmtes Paket sp\u00e4ter geht. F\u00fcr <code>listen [::]:443 quic ... ipv6only=off;<\/code> ist <code>sa_family<\/code> immer <code>AF_INET6<\/code>, egal ob ein konkretes Paket an einen echten IPv6-Host oder an eine IPv4-mapped-Adresse geht. Der <code>AF_INET<\/code>-Zweig, der <code>IP_DONTFRAG<\/code> f\u00fcrs echte IPv4-Datagramm setzen w\u00fcrde, wird f\u00fcr diesen Socket schlicht nie erreicht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein kleines Detail f\u00fcr alle, die selbst nachlesen: der IPv6-Fallback-Zweig (das <code>#elif (NGX_HAVE_IP_DONTFRAG)<\/code> im letzten Block) ist mit demselben Feature-Makro abgesichert wie der IPv4-Zweig, nicht mit dem separat getesteten <code>NGX_HAVE_IPV6_DONTFRAG<\/code>. Auf FreeBSD \u00e4ndert das am Ergebnis nichts, hier sind beide Makros gesetzt, aber es ist eine kleine Ungenauigkeit im nginx-Quellcode selbst. So zumindest meine Interpretation, korrigiert mich gerne!<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der eigentliche Denkfehler dahinter: IPv6 hat gar kein DF-Bit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wer <code>IPV6_DONTFRAG<\/code> im Code sieht, k\u00f6nnte annehmen, dass damit dasselbe Problem f\u00fcr IPv6 gel\u00f6st wird wie <code>IP_DONTFRAG<\/code> f\u00fcr IPv4. Ist es nicht, und der Unterschied ist der eigentliche Kern dieses Bugs.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der IPv6-Header hat schlicht kein DF-Bit, weil er keines braucht. <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc8200.html#section-4.5\" target=\"_blank\" rel=\"noopener\">RFC 8200<\/a> verbietet Routern im Pfad die Fragmentierung von IPv6-Paketen grunds\u00e4tzlich, das steht w\u00f6rtlich in Abschnitt 4.5 (der auf Abschnitt 5 verweist): &#8222;unlike IPv4, fragmentation in IPv6 is performed only by source nodes, not by routers along a packet&#8217;s delivery path&#8220;. <code>IPV6_DONTFRAG<\/code> ist die tats\u00e4chliche <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc3542.html#section-11.2\" target=\"_blank\" rel=\"noopener\">RFC-3542-Option, Abschnitt 11.2<\/a> der Spezifikation, aber sie steuert nur, ob der sendende Host selbst \u00fcber einen Fragment-Extension-Header fragmentieren darf. Ein Problem, das aus Sicht von IPv6 gar nicht existiert, sobald man es nicht selbst provoziert.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>IP_DONTFRAG<\/code> auf der IPv4-Seite ist dagegen keine RFC-3542-Option, sondern eine BSD-Socket-Erweiterung au\u00dferhalb dieses Standards. Zwei Optionen, die im Code wie ein symmetrisches Paar aussehen, l\u00f6sen in Wirklichkeit zwei v\u00f6llig verschiedene Probleme.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und jetzt der Punkt, an dem beide Welten aufeinandertreffen. Ein <code>send()<\/code> auf diesem IPv6-Socket an eine IPv4-mapped-Zieladresse bringt am Ende ein echtes IPv4-Datagramm auf die Leitung, das ist Standardverhalten von Dual-Stack-Sockets nach RFC 3493. Dieses Datagramm braucht ein gesetztes DF-Bit im IPv4-Header, ein komplett anderes Feld als alles, was <code>IPV6_DONTFRAG<\/code> je anfasst. Der oben gezeigte Code-Pfad kommt an dieses Feld nie heran, weil er f\u00fcr einen <code>AF_INET6<\/code>-Socket ausschlie\u00dflich die IPv6-Variante der Option setzt. Kann man das also auch irgendwie ein legacy IPv4 Problem nennen?<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Warum ein einfacher Zusatz-Aufruf auf FreeBSD gar nicht helfen w\u00fcrde<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die naheliegende Reaktion: warum ruft nginx auf diesem Socket nicht einfach zus\u00e4tzlich <code>setsockopt(IPPROTO_IP, IP_DONTFRAG)<\/code> auf? Ich bin dem im FreeBSD-Kernel-Quellcode nachgegangen, und die Antwort ist, dass das auf FreeBSD gar nicht funktionieren w\u00fcrde.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Options-Handler f\u00fcr einen <code>AF_INET6<\/code>-Socket ist <code>ip6_ctloutput()<\/code> in <a href=\"https:\/\/cgit.freebsd.org\/src\/tree\/sys\/netinet6\/ip6_output.c\" target=\"_blank\" rel=\"noopener\"><code>sys\/netinet6\/ip6_output.c<\/code><\/a>, und der lehnt Optionen au\u00dferhalb von <code>IPPROTO_IPV6<\/code> rundweg ab:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">level = sopt->sopt_level;\n...\nif (level != IPPROTO_IPV6) {\n    error = EINVAL;\n    ...\n}<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein <code>setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, ...)<\/code> auf einem <code>AF_INET6<\/code>-Socket landet also mit <code>EINVAL<\/code> im Nichts. Das allein w\u00e4re schon Antwort genug, aber der zweite Teil ist genauso wichtig: wo genau setzt der Kernel das DF-Bit f\u00fcr einen IPv4-mapped-Send, der \u00fcber einen IPv6-Socket rausgeht? In <a href=\"https:\/\/cgit.freebsd.org\/src\/tree\/sys\/netinet\/udp_usrreq.c\" target=\"_blank\" rel=\"noopener\"><code>udp_usrreq.c<\/code><\/a>, in der Funktion, die das eigentliche IPv4-UDP-Paket zusammenbaut:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">if (inp->inp_flags &amp; INP_DONTFRAG)\n    ((struct ip *)ui)->ip_off |= htons(IP_DF);<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das DF-Bit wird nur gesetzt, wenn am PCB (dem Protocol Control Block der Verbindung) das Flag <code>INP_DONTFRAG<\/code> h\u00e4ngt. Und dieses Flag setzt ausschlie\u00dflich <code>ip_ctloutput()<\/code> in <code>ip_output.c<\/code>, wenn <code>IP_DONTFRAG<\/code> auf <code>IPPROTO_IP<\/code>-Ebene erfolgreich gesetzt wurde, also genau der Aufruf, der auf einem <code>AF_INET6<\/code>-Socket mit <code>EINVAL<\/code> scheitert. Der IPv4-mapped-Sendepfad selbst f\u00fchrt \u00fcbrigens tats\u00e4chlich \u00fcber genau diese Funktion: <code>udp6_send()<\/code> in <a href=\"https:\/\/cgit.freebsd.org\/src\/tree\/sys\/netinet6\/udp6_usrreq.c\" target=\"_blank\" rel=\"noopener\"><code>udp6_usrreq.c<\/code><\/a> erkennt eine v4-mapped-Zieladresse auf einem Dual-Stack-Socket, gibt die eigenen Locks frei und ruft direkt <code>udp_send()<\/code>, also dieselbe IPv4-UDP-Ausgabefunktion, die auch ein echter <code>AF_INET<\/code>-Socket benutzt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Damit schlie\u00dft sich der Kreis \/ Beisst die Katze sich in den Schwanz, ihr wisst schon. <code>INP_DONTFRAG<\/code> kann nur \u00fcber einen erfolgreichen <code>IP_DONTFRAG<\/code>-Aufruf auf einem echten <code>AF_INET<\/code>-Socket gesetzt werden, und ein <code>AF_INET6<\/code>-Socket kann diesen Aufruf laut <code>ip6_ctloutput()<\/code> gar nicht erst absetzen. Der Socket-Split ist auf FreeBSD damit nicht nur der pragmatische, sondern der einzig m\u00f6gliche Fix. Ein hypothetischer nginx-Patch, der einfach den fehlenden <code>setsockopt<\/code>-Aufruf erg\u00e4nzt, w\u00fcrde am Kernel selbst scheitern, bevor er \u00fcberhaupt etwas bewirkt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was sich zwischen FreeBSD und Linux unterscheidet, und was nicht<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Bug, wenn ich das so nennen darf, selbst sitzt im nginx-Quellcode und ist plattformunabh\u00e4ngig. Dieselbe <code>sa_family<\/code>-Abfrage wird auf Linux und FreeBSD gleich kompiliert und ist auf beiden gleicherma\u00dfen falsch.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was sich unterscheidet, ist welche Socket-Option-API \u00fcberhaupt zur Verf\u00fcgung steht. <code>IP_MTU_DISCOVER<\/code> mit dem Wert <code>IP_PMTUDISC_DO<\/code> ist eine Linux-Erweiterung (auch musl bringt das Makro mit, nicht nur glibc), auf FreeBSD schlicht nicht deklariert. FreeBSD bietet stattdessen <code>IP_DONTFRAG<\/code> und <code>IPV6_DONTFRAG<\/code>. nginx testet im <code>configure<\/code>-Lauf beide Varianten (<code>auto\/unix<\/code>, Feature-Tests f\u00fcr <code>NGX_HAVE_IP_MTU_DISCOVER<\/code>, <code>NGX_HAVE_IP_DONTFRAG<\/code>, <code>NGX_HAVE_IPV6_DONTFRAG<\/code>) und w\u00e4hlt zur Kompilierzeit die passende Variante. Auf dieser FreeBSD-Installation landet man im <code>IP_DONTFRAG<\/code>\/<code>IPV6_DONTFRAG<\/code>-Zweig, und genau der ist von dem oben beschriebenen Bug betroffen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was ich nicht verifiziert habe, weil mir daf\u00fcr ein Linux-Vergleichssystem fehlt (ich zu faul war eines aus dem Glas zu ziehen): ob sich das praktische Fehlerbild zwischen den beiden Kerneln tats\u00e4chlich unterscheidet, also wie stark Retransmissions ausfallen oder wie der jeweilige Kernel beim v4-mapped-Sendepfad mit dem DF-\u00c4quivalent umgeht. Linux&#8216; PMTUD-Verhalten f\u00fcr genau diesen Fall k\u00f6nnte sich abweichend verhalten. Ehrliches Fazit an dieser Stelle: der nginx-Bug ist universell im Quellcode, das konkret beobachtete Fehlerbild ist hier ausschlie\u00dflich f\u00fcr FreeBSD dokumentiert. Eine Aussage wie &#8222;genauso kaputt auf Linux&#8220; w\u00fcrde ich ohne echten Beleg, also Paketmitschnitt oder Kernel-Quellcode-Analyse auf einem Linux-System, nicht treffen. Vielleicht mache ich mir die M\u00fche noch&#8230;. Vielleicht \ud83d\ude00<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Fix: zwei Listener statt einem<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die L\u00f6sung ist, den einen Dual-Stack-Listener in zwei eigenst\u00e4ndige Listener zu splitten, damit IPv4-Clients einen echten <code>AF_INET<\/code>-Socket bekommen, f\u00fcr den der obere Codepfad tats\u00e4chlich greift:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">listen [::]:443 quic reuseport ipv6only=on default_server;\nlisten 443 quic reuseport default_server;<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Stolperstein dabei, den man sich leicht selbst stellt: ein einfaches zus\u00e4tzliches <code>listen 443 quic;<\/code> neben einem unver\u00e4nderten Dual-Stack-Socket kollidiert mit &#8222;Address already in use&#8220;, weil der alte Socket die IPv4-Wildcard-Adresse \u00fcber <code>ipv6only=off<\/code> ja schon abdeckt. <code>ipv6only<\/code> muss auf dem IPv6-Listener also explizit auf <code>on<\/code> gesetzt werden, sonst gibt es keinen sauberen Split, sondern nur einen Fehlstart. Je nachdem, wie &#8222;ordentlich&#8220; seine Konfiguration ist, kann es einen Moment dauern, alle diese Stellen zu finden, denn das muss f\u00fcr die komplette nginx Konfiguration angepasst sein!<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Erster Stolperstein: ein reload reicht nicht<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine reine <code>ipv6only<\/code>-\u00c4nderung an einem bestehenden QUIC-Socket erkennt nginx&#8216; Socket-Reuse-Logik bei einem <code>reload<\/code> nicht als neuen Socket. Bei mir blieb der alte Dual-Stack-Socket nach einem reinen <code>reload<\/code> laut <code>sockstat<\/code> weiter als <code>udp46<\/code> aktiv, trotz fehlerfrei gepr\u00fcfter Konfiguration (<code>nginx -t<\/code>) und trotz eines <code>nginx -T<\/code>-Dumps, der die neue Konfiguration bereits korrekt anzeigte. Ist bei solchen Dingen hin und wieder so, machen andere Dienste ja auch. Gro\u00dfe Dinge wie Socket, wird oft nur bei einem echten Neustart vom Deinst gesetzt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>nginx -t<\/code> pr\u00fcft Syntax und versucht, referenzierte Dateien zu \u00f6ffnen. Es sagt nichts dar\u00fcber aus, ob ein bereits offener, laufender Listen-Socket bei einem Reload tats\u00e4chlich neu gebunden wird. Erst ein vollst\u00e4ndiger <code>service nginx restart<\/code> hat die getrennten Sockets tats\u00e4chlich neu gebunden:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">sockstat -4 -l | grep 443   # nur udp4\nsockstat -6 -l | grep 443   # nur udp6<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nach dem Restart tauchte in der PROTO-Spalte ausschlie\u00dflich <code>udp4<\/code> und <code>udp6<\/code> auf, kein einziges <code>udp46<\/code> mehr auf Port 443. Das ist der eigentliche Beleg f\u00fcr den Reload-Stolperstein, keine Datei-Deskriptor-Analyse, nur der Blick auf einen laufenden Prozess. Das reiht sich in ein Muster ein, das mir auf dieser Infrastruktur schon \u00f6fter untergekommen ist: ein neues geladenes Modul, ein neuer <code>server_name<\/code> in einer bestehenden <code>reuseport<\/code>-Gruppe, jetzt ein <code>ipv6only<\/code>-Wechsel. Immer wieder dasselbe Grundmuster, ein Reload reicht bei bestimmten \u00c4nderungen an Listen-Sockets schlicht nicht. Aber ich wiederhole mich, hm?<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Zweiter Stolperstein: jeder vHost braucht den Split<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der erste Durchlauf hat nur den Default-Server-Socket gesplittet. Alle anderen vHosts mit eigenem <code>listen [::]:443 quic;<\/code>, aber ohne eigenes <code>listen 443 quic;<\/code>, hingen danach nur noch am neuen IPv6-only-Socket. IPv4-QUIC-Verbindungen zu diesen Domains landeten beim Default-vHost, weil die SNI-Zuordnung auf dem neuen IPv4-Socket schlicht ins Leere lief. Sichtbar wurde das am falschen ausgelieferten TLS-Zertifikat und an einer Default-Antwort statt der eigentlich erwarteten.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Lehre daraus: bei einem <code>reuseport<\/code>-Split m\u00fcssen alle Server-Bl\u00f6cke der Adressgruppe angepasst werden, nicht nur der Default-Server. Der IPv4-Listener eines jeden weiteren vHosts docken danach ohne <code>reuseport<\/code> und ohne <code>default_server<\/code> einfach an die schon bestehende Gruppe an:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">listen [::]:443 quic;\nlisten 443 quic;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was im Error-Log nicht zu finden ist<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das nginx-Error-Log auf Level <code>info<\/code>, die komplette 15-Tage-Rotation samt <code>.bz2<\/code>-Archiven, enth\u00e4lt keine einzige <code>[warn]<\/code>, <code>[error]<\/code> oder <code>[crit]<\/code>-Zeile mit QUIC-Bezug, weder vor noch nach dem Fix. Eine gezielte Suche nach den vier exakten Alert-Meldungen, die der oben zitierte Code beim Fehlschlagen eines <code>setsockopt<\/code>-Aufrufs loggen w\u00fcrde, kommt in der gesamten Historie null Mal vor. Vielleicht sollte ich das noch im Logging aufbohren, wenn m\u00f6glich?!<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das ist methodisch wichtig: diese Alert-Meldungen enthalten das Wort &#8222;quic&#8220; gar nicht. Ein reines <code>grep -i quic<\/code> im Error-Log h\u00e4tte einen echten Fehlschlag dieser Aufrufe also gar nicht gefunden, selbst wenn es einen gegeben h\u00e4tte. Erst die gezielte Suche nach dem exakten Meldungstext schlie\u00dft aus, dass die Socket-Optionen fehlgeschlagen sind. Sie wurden erfolgreich gesetzt, nur eben die falsche Option f\u00fcr die falsche Adressfamilie. Kein Aufruf-Fehler, sondern genau der beschriebene Bug.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Warum das Error-Log strukturell nicht mehr hergibt: nginx sendet das UDP-Datagramm erfolgreich an den Kernel, die Fragmentierung passiert danach im Netz, an einem Router oder einer Middlebox, oder schon beim sendenden Host selbst, falls das ausgehende Interface-MTU \u00fcberschritten wird. Davon bekommt der laufende nginx-Prozess in keinem Fall etwas mit. Wer nach diesem Bug im Error-Log sucht, wird nichts finden. Das ist erwartbar, nicht beruhigend, und ein blindes <code>grep quic<\/code> h\u00e4tte selbst einen echten Konfigurationsfehler an dieser Stelle \u00fcbersehen. Genau das ist der Punkt. Man sieht es am Server fast nur, wenn man auch einen tcpdump macht und auswertet. Denn die UDP Pakete gehen ja auf die Reise, sind sie nicht als &#8222;unzerteilbar&#8220; markiert, kann und wird auf dem Weg aber bestimmt irgendein Router oder eine Firewall anfangen die Pakete klein zu hacken und dann ist es &#8222;kaputt&#8220;.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Verifikation \u00fcber das Access-Log, und ihre Grenzen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">nginx zeigt IPv4-Clients, die \u00fcber einen Dual-Stack-Socket hereinkommen, im Access-Log als IPv4-mapped-Adresse (<code>::ffff:a.b.c.d<\/code>). Nach dem Fix kommen IPv4-QUIC-Clients \u00fcber einen echten <code>AF_INET<\/code>-Socket herein und erscheinen als reine IPv4-Adresse ohne dieses Pr\u00e4fix. Diese Darstellung allein beweist schon, durch welchen Socket eine Verbindung lief, unabh\u00e4ngig von jeder Performance-Messung, aber eben auch unabh\u00e4ngig von jeder direkten Fragmentierungs-Messung.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ausgewertet habe ich das Access-Log des Blogs (Format <code>goa_ext<\/code>, enth\u00e4lt <code>$request_time<\/code>), 13 Tage vor dem Fix plus den laufenden Tag danach, alle <code>HTTP\/3.0<\/code>-Requests, aufgeteilt nach Adressdarstellung:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular has-small-font-size\"><table><thead><tr><th>Zeitraum<\/th><th>Requests<\/th><th>\u00d8 request_time<\/th><th>Max<\/th><th>Anteil \u00fcber 1 s<\/th><\/tr><\/thead><tbody><tr><td>vorher, IPv4 \u00fcber kaputten Dual-Stack-Socket<\/td><td>12.537<\/td><td>0,461 s<\/td><td>130,3 s<\/td><td>5,33 %<\/td><\/tr><tr><td>nachher, IPv4 \u00fcber eigenen udp4-Socket<\/td><td>946<\/td><td>0,037 s<\/td><td>1,4 s<\/td><td>0,42 %<\/td><\/tr><tr><td>Kontrollgruppe, natives IPv6, vorher<\/td><td>17.320<\/td><td>0,113 s<\/td><td>67,3 s<\/td><td>1,86 %<\/td><\/tr><tr><td>Kontrollgruppe, natives IPv6, nachher<\/td><td>1.168<\/td><td>0,082 s<\/td><td>5,7 s<\/td><td>2,14 %<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die betroffene IPv4-Gruppe lag vorher bei 5,33 Prozent \u00fcber einer Sekunde, fast das Dreifache der IPv6-Baseline von 1,86 Prozent, und f\u00e4llt nach dem Fix auf 0,42 Prozent, unter die IPv6-Baseline von 2,14 Prozent im selben Zeitraum. Genau dieser Kontrollgruppen-Vergleich macht die Zahlen f\u00fcr mich aussagekr\u00e4ftig, als Korrelation, die zur quellcode-basierten Root-Cause-Analyse passt, nicht als eigenst\u00e4ndigen Fragmentierungsbeweis.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine zeitliche Punktprobe passt ins Bild: Restart am 9. September 2026 um 18:38:31 Uhr, letzter Request \u00fcber den kaputten Dual-Stack-Pfad um 17:56:46 Uhr, erster Request \u00fcber den neuen Pfad um 18:41:36 Uhr. Seit dem Restart lief keiner der 946 IPv4-HTTP\/3.0-Requests mehr \u00fcber die alte Darstellung. Das liegt plausibel am Restart-Zeitpunkt, mit einer L\u00fccke ohne Traffic dieser Art von 42 Minuten davor und 3 Minuten danach bis zum ersten neuen Request, aber es ist kein exakter Sekundentreffer und ich beschreibe es auch nicht so.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Genauso wichtig wie die Zahlen sind f\u00fcr mich ihre Grenzen, deshalb ein eigener Absatz daf\u00fcr statt eines Nebensatzes:<\/p>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li>Ein fehlendes DF-Bit erlaubt Fragmentierung, es erzwingt sie nicht. Ein Datagramm, das auf jedem Hop unter dem dortigen MTU bleibt, ist unabh\u00e4ngig vom DF-Bit unproblematisch. Das kann zu Fragmentierung f\u00fchren, es f\u00fchrt nicht automatisch dazu.<\/li>\n\n\n\n<li>Die Adressdarstellung im Access-Log beweist, welchen Socket nginx verwendet hat. Sie sagt nichts \u00fcber den DF-Bit-Zustand, die tats\u00e4chliche Paketgr\u00f6\u00dfe, die reale PMTU oder echte Fragmente auf der Leitung aus. Das gilt hier ohne Einschr\u00e4nkung, weil dieser vHost direkt am Internet h\u00e4ngt, ohne vorgeschaltete Proxy- oder Load-Balancer-Schicht, die <code>$remote_addr<\/code> umschreiben k\u00f6nnte.<\/li>\n\n\n\n<li>Der Vergleich von 13 Tagen vorher gegen einen Teil eines Tages nachher ist kein kontrollierter Leistungsvergleich. Trafficmix, Client-Geografie, Cache-Zustand, Backend-Last und Tageszeit unterscheiden sich zwangsl\u00e4ufig. Die Zahlen sind ein starkes Indiz, kein experimenteller Beweis.<\/li>\n\n\n\n<li>Die IPv6-Kontrollgruppe ist nicht vollst\u00e4ndig unver\u00e4ndert geblieben. Nur der Anteil \u00fcber einer Sekunde bleibt in vergleichbarer Gr\u00f6\u00dfenordnung. Mittelwert und Maximum verschieben sich durchaus, vermutlich schlicht durch allgemein geringere Last oder einen anderen Trafficmix zum jeweiligen Zeitpunkt.<\/li>\n\n\n\n<li>Eine Retransmission-Spirale statt eines schnellen Fails ist ein plausibles, aber kein garantiertes Symptom. Wie sich ein verlorenes, fragmentiertes QUIC-Paket konkret auswirkt, h\u00e4ngt von der Verlusterkennung und der PMTU-Implementierung der beteiligten QUIC-Stacks ab, auf Client- wie auf Serverseite.<\/li>\n\n\n\n<li>Ein wirklich abschlie\u00dfender Beweis br\u00e4uchte einen Vorher- und Nachher-Paketmitschnitt, der das DF-Bit direkt zeigt, im Idealfall mit einem kontrollierten Test gegen ein reduziertes MTU. Ohne den betrachte ich die Produktionszahlen als starke, durch eine Kontrollgruppe abgesicherte Korrelation, die zur quellcode-basierten Root-Cause-Analyse passt, nicht als direkten Fragmentierungsbeweis. Ein Mitschnitt von der Vorher-Seite liegt inzwischen vor, dazu der n\u00e4chste Abschnitt.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Ein Paketmitschnitt vom Tag vor dem Fix schlie\u00dft einen Teil der L\u00fccke<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nachtrag vom 12. September 2026: Der vorherige Abschnitt musste ohne einen echten Paketmitschnitt auskommen. Der liegt inzwischen vor. Zwei rund 41 Sekunden lange Aufzeichnungen vom 8. September 2026, einen Tag vor dem Fix, eine direkt auf dem Server, die andere gleichzeitig auf einem Notebook, das die Seite \u00fcber ein anderes Netz aufgerufen hat. Beide zusammen zeigen den echten HTTP\/3-Verkehr eines einzelnen Seitenaufrufs.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ausgewertet mit <code>tcpdump -r &lt;Datei&gt; -n -v<\/code>, ein <code>tshark<\/code> stand mir nicht zur Verf\u00fcgung. Jedes Paket tr\u00e4gt im IP-Header ein Identification-Feld, und das ist in beiden Mitschnitten innerhalb des Beobachtungsfensters durchgehend eindeutig, keine einzige Dopplung unter den mehr als dreitausend vom Server verschickten Paketen. Dar\u00fcber lassen sich einzelne Datagramme zwischen den beiden Aufzeichnungen zweifelsfrei einander zuordnen, nicht nur \u00fcber Summen vergleichen.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular has-small-font-size\"><table><thead><tr><th>Richtung<\/th><th>Beim Server verschickt<\/th><th>Beim Notebook angekommen<\/th><th>Abgleich<\/th><\/tr><\/thead><tbody><tr><td>Notebook zu Server<\/td><td>1.147, alle mit DF-Bit<\/td><td>1.147, alle mit DF-Bit<\/td><td>identisch, per ID gepr\u00fcft<\/td><\/tr><tr><td>Server zu Notebook, klein genug f\u00fcrs MTU<\/td><td>898, kein DF-Bit<\/td><td>898, dieselben IDs<\/td><td>0 Prozent Verlust<\/td><\/tr><tr><td>Server zu Notebook, fragmentiert, kein DF-Bit<\/td><td>2.162 erste Fragmente<\/td><td>878, per ID zugeordnet<\/td><td>1.284 fehlen, 59,4 Prozent<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Kein einziges der 1.147 Pakete vom Notebook zum Server fehlt das DF-Bit, kein einziges der 3.060 Pakete in der Gegenrichtung tr\u00e4gt eines. Das best\u00e4tigt den Fund aus dem Quellcode jetzt direkt auf der Leitung, nicht nur \u00fcber die Adressdarstellung im Access-Log. 2.162 dieser Server-Pakete sind schon beim Verlassen des Servers fragmentiert, jedes erste Fragment exakt 1500 Byte lang, also an der Ethernet-MTU gekappt. Von diesen 2.162 erscheinen im Notebook-Mitschnitt nur 878 wieder, eins zu eins \u00fcber die ID zugeordnet. 1.284 fehlen komplett, das sind 59,4 Prozent. Die 898 kleinen, unfragmentierten Antworten kommen dagegen alle an, ebenfalls per ID gepr\u00fcft und nicht blo\u00df \u00fcber die Summe verglichen. \u00dcber die ganzen 41 Sekunden verteilt liegt der fehlende Anteil in jedem F\u00fcnf-Sekunden-Fenster zwischen 51 und 68 Prozent. Kein Ausrei\u00dfer am Anfang oder Ende der Aufzeichnung, der auf einen Zeitversatz zwischen den beiden Mitschnitten hindeuten w\u00fcrde.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die zweite H\u00e4lfte jedes fragmentierten Datagramms taucht in keinem der beiden Mitschnitte auf. Vermutlich wurde mit einem Filter auf den UDP-Port aufgezeichnet, und ein IP-Fragment ohne den ersten Teil tr\u00e4gt keinen UDP-Header, an dem so ein Filter ansetzen k\u00f6nnte. Das l\u00e4sst offen, ob einzelne der 878 angekommenen ersten Fragmente trotzdem nie vollst\u00e4ndig zusammengesetzt werden konnten, weil ihre zweite H\u00e4lfte fehlte. 59,4 Prozent sind deshalb eine gemessene Untergrenze f\u00fcr den tats\u00e4chlichen Verlust, keine Obergrenze. Und es bleibt eine einzelne Sitzung, ein Client, ein Pfad, eine Zeitspanne von 41 Sekunden, kein Beleg f\u00fcr eine feste Verlustquote im ganzen Netz.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">So sieht der Unterschied im Rohformat aus, Adressen durch Platzhalter ersetzt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">12:01:29.758438 IP (id 0, offset 0, flags [DF], length 1280)\n    CLIENT.35577 > SERVER.443: UDP, length 1252\n12:01:29.771757 IP (id 51997, offset 0, flags [+], length 1500)\n    SERVER.443 > CLIENT.35577: UDP, length 1480<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dieser Mitschnitt stammt vom Tag vor dem Fix, ein gepaarter Mitschnitt von danach existiert aus dem echten Betrieb nicht. Der Versuch direkt nach dem Fix blieb leer, weil der daf\u00fcr verwendete Client kein echtes HTTP\/3 sprach und der Mitschnitt dadurch keinen QUIC-Verkehr enthielt. Die Vorher-Nachher-Zahlen aus dem Access-Log oben bleiben deshalb weiterhin die Grundlage f\u00fcr den gemessenen Effekt des Fixes. Was dieser Mitschnitt zus\u00e4tzlich liefert, ist der direkte Beleg f\u00fcr den Mechanismus dahinter: das fehlende DF-Bit und die Fragmentierung selbst sind jetzt nicht mehr nur aus dem Quellcode abgeleitet, sondern auf der Leitung beobachtet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Fazit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein einzelner Dual-Stack-Socket f\u00fcr QUIC ist offenbar kein Einzelfall, sondern eher ein wiederkehrendes Footgun-Muster \u00fcber verschiedene Implementierungen hinweg. <a href=\"https:\/\/seemann.io\/posts\/2025-02-19---ip-fragmentation\/\" target=\"_blank\" rel=\"noopener\">quic-go, die von Marten Seemann entwickelte QUIC-Bibliothek, musste DPLPMTUD auf \u00e4lteren macOS-Versionen abschalten<\/a>, weil sich das DF-Bit auf einem Dual-Stack-Socket dort schlicht nicht setzen lie\u00df, und dokumentiert das offen. <a href=\"https:\/\/github.com\/quic-go\/quic-go\/issues\/4105\" target=\"_blank\" rel=\"noopener\">Ein separates Issue im selben Projekt<\/a> beschreibt ein anderes, aber verwandtes Dual-Stack-Problem gerade auf FreeBSD (fehlgeschlagenes <code>sendmsg<\/code> f\u00fcr IPv4-mapped-Adressen), mit einem Verweis auf FreeBSDs eigene <a href=\"https:\/\/man.freebsd.org\/cgi\/man.cgi?query=inet6&amp;sektion=4\" target=\"_blank\" rel=\"noopener\"><code>inet6(4)<\/code>-Manpage<\/a>, die f\u00fcr <code>AF_INET6<\/code>-Sockets ganz grunds\u00e4tzlich empfiehlt, zwei getrennte Sockets zu verwenden, aus Sicherheitsgr\u00fcnden, unabh\u00e4ngig von QUIC. Die Empfehlung, IPv4 und IPv6 getrennt zu halten, existierte auf dieser Plattform also schon, bevor QUIC \u00fcberhaupt erfunden wurde. F\u00fcr den konkreten Fall hier hei\u00dft das, dass der Socket-Split kein Workaround f\u00fcr einen einzelnen nginx-Bug ist, sondern die R\u00fcckkehr zu der Architektur, die FreeBSD f\u00fcr genau diese Situation ohnehin vorsieht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Socket f\u00fcr beides bleibt bei TCP \u00fcber TLS ein legitimer, funktionierender Kompromiss. Bei QUIC ist er, zumindest auf dieser Kombination aus nginx und FreeBSD, ein stiller Bug, der sich weder im Error-Log noch im laufenden Betrieb meldet, sondern nur \u00fcber einen erh\u00f6hten Anteil langsamer Verbindungen bemerkbar macht. Wer selbst QUIC auf einem Dual-Stack-Listener betreibt, findet die Antwort auf die Frage, welchen Socket ein Client gerade benutzt, mit dem Access-Log-Trick oben in wenigen Minuten selbst heraus.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Siehe auch:<\/p>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/03\/07\/post-quantum-tls-fuer-nginx-x25519mlkem768-auf-freebsd-15\/\">Post-Quantum TLS f\u00fcr Nginx<\/a>, dieselbe nginx-Infrastruktur, ein anderer TLS-Konfigurationsstolperstein<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/17\/encrypted-client-hello-nginx-openssl-4-public-name\/\">ECH aktivieren<\/a>, mehr nginx-QUIC-Konfiguration auf demselben Server<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2022\/01\/13\/jetzt-mit-http-3-quic\/\">Jetzt mit HTTP\/3 und QUIC<\/a>, die Einf\u00fchrung von HTTP\/3 auf diesem Blog<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/07\/05\/php-shutdown-crash-freebsd-libc-rtld-posix-spawn\/\">Wenn PHP beim Aufr\u00e4umen stirbt<\/a>, eine andere Root-Cause-Analyse tief im FreeBSD-Userland<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Fragen, Einw\u00e4nde oder eigene Erfahrungen mit QUIC auf Dual-Stack-Sockets? Dann darfst du mich sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ein gemeinsamer IPv4\/IPv6-Socket f\u00fcr QUIC spart eine Listen-Zeile, bis auff\u00e4llt, dass das DF-Bit f\u00fcr die halbe Adressfamilie nie gesetzt wird. Root-Cause-Analyse im nginx-Quellcode, im FreeBSD-Kernel nachvollzogen und mit echten Zugriffszahlen belegt.<\/p>\n","protected":false},"author":1,"featured_media":5411,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[13,274,273,276],"tags":[443,28,21,42,379,23,22,77],"class_list":["post-5410","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kernel-error-blog","category-linux-bsd","category-netzwerke-protokolle","category-self-hosting-infrastruktur","tag-debugging","tag-freebsd","tag-http3","tag-ipv6","tag-networking","tag-nginx","tag-quic","tag-udp","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5410","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5410"}],"version-history":[{"count":16,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5410\/revisions"}],"predecessor-version":[{"id":5446,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5410\/revisions\/5446"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5411"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5410"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5410"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5410"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":4975,"date":"2026-09-04T10:00:00","date_gmt":"2026-09-04T08:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=4975"},"modified":"2026-09-04T07:32:05","modified_gmt":"2026-09-04T05:32:05","slug":"bios-erklaert-teil-6-ecc-patrol-scrub-ppr-memory-ras","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/09\/04\/bios-erklaert-teil-6-ecc-patrol-scrub-ppr-memory-ras\/","title":{"rendered":"BIOS erkl\u00e4rt, Teil 6: 256 GB ECC, ein Scrubber der jeden Tag putzt und ein Reparaturmechanismus im Speicherriegel"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">In dieser Folge geht es um <code>Advanced &gt; Chipset Configuration &gt; North Bridge &gt; Memory Configuration<\/code>. Wer die Serie bis hierhin nicht mitgelesen hat: alle Teile liegen unter <a href=\"https:\/\/www.kernel-error.de\/category\/bios-firmware\/\">BIOS &amp; Firmware<\/a>, und es lohnt sich nicht, sie in der Reihenfolge zu lesen. Jede Folge steht f\u00fcr sich.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was verbaut ist<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s236.png\" alt=\"Aptio Setup, Memory Topology, alle acht Module mit 2933 Megatransfers und 32 Gigabyte als Registered DIMM\" class=\"wp-image-4977\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s236.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s236-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s236-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s236-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">DIMMA1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMB1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMC1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMD1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMME1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMF1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMG1: 2933MT\/s ATP DRx4 32GB RDIMM\nDIMMH1: 2933MT\/s ATP DRx4 32GB RDIMM<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Acht Steckpl\u00e4tze, acht Module, jedes 32 GB. Das ist die Seite <code>Memory Topology<\/code>, und sie sagt in acht Zeilen mehr \u00fcber den Aufbau der Maschine als jedes Datenblatt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>RDIMM<\/code> heisst Registered DIMM. Zwischen dem Speichercontroller und den eigentlichen Speicherchips sitzt ein Registerbaustein, der Adress- und Steuersignale zwischenspeichert und neu ausgibt. Der Grund ist elektrisch: je mehr Chips an einem Kanal h\u00e4ngen, desto st\u00e4rker belastet das die Signalleitungen. Der Registerbaustein bricht diese Last auf. Deshalb gibt es in Servern acht Module pro Sockel und in Desktops vier, und deshalb kann man normale Desktop-Riegel hier nicht einsetzen. Der Preis ist ein Takt Latenz zus\u00e4tzlich.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>DRx4<\/code> beschreibt den Aufbau: Dual Rank, jeder Chip liefert 4 Bit. Zwei Ranks bedeutet, dass auf dem Modul zwei unabh\u00e4ngige Gruppen von Chips sitzen, zwischen denen der Controller umschalten kann. W\u00e4hrend eine Gruppe noch mit einer Anfrage besch\u00e4ftigt ist, kann er die andere schon ansprechen, was den Durchsatz verbessert.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und <code>2933MT\/s<\/code>, obwohl auf den Modulen 3200 steht. Das ist keine Fehlfunktion, sondern die CPU. Der Xeon Gold 5315Y ist die Einstiegsvariante der Ice-Lake-Serie, und Intel hat den Speichertakt an die Prozessorstufe gekoppelt. Die teureren Modelle fahren 3200, meiner eben 2933. Die Riegel k\u00f6nnten mehr, sie d\u00fcrfen nicht. Genau so meldet es auch das Betriebssystem:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dmidecode -t memory | grep -E \"Speed|Configured\"\n#   Speed: 3200 MT\/s\n#   Configured Memory Speed: 2933 MT\/s<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was das Modul kann, und was es tats\u00e4chlich tut. Der Unterschied sind rund acht Prozent Speicherbandbreite, die ich nicht bekomme. Verschmerzbar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">ECC, der Teil den alle kennen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Erstaunlicherweise gibt es auf dieser Seite gar keinen Schalter namens ECC. Bei registrierten Servermodulen ist Fehlerkorrektur keine Option, sondern eine Eigenschaft der Hardware. Sie ist da, sie ist an, fertig.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Falls es jemand nicht parat hat: ECC speichert zu je 64 Bit Nutzdaten 8 zus\u00e4tzliche Pr\u00fcfbits. Damit l\u00e4sst sich ein gekipptes Bit erkennen und reparieren, und zwei gekippte Bits lassen sich zumindest erkennen. Das ist der Unterschied zwischen \u201eim Log steht eine korrigierte Zeile&#8220; und \u201eirgendwo in einer Datei steht jetzt ein falsches Byte, viel Spass beim Suchen&#8220;.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bits kippen h\u00e4ufiger als man denkt. Kosmische Strahlung, Alphateilchen aus dem Geh\u00e4usematerial, schlicht Alterung. Bei 256 GB, die dauerhaft laufen, ist das keine theoretische Gr\u00f6sse mehr.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Patrol Scrub, und die Zahl aus dem Handbuch<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Patrol Scrub                      [Enable at End of POST]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das ist mein Lieblingsschalter auf dieser Seite. ECC korrigiert n\u00e4mlich nur dann, wenn jemand die betroffene Speicherstelle liest. Ein Bit in einem Speicherbereich, den seit drei Wochen niemand angefasst hat, kippt unbemerkt. Kippt in derselben Zeile ein zweites, ist der Fehler nicht mehr korrigierbar.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Patrol Scrub geht deshalb im Hintergrund den gesamten Speicher durch, liest jede Zeile, l\u00e4sst ECC pr\u00fcfen und schreibt sie korrigiert zur\u00fcck. Vorbeugendes Putzen, damit sich Einzelfehler nicht zu Doppelfehlern summieren.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das Handbuch nennt dazu eine Zahl, die man sonst nirgends findet:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-small-font-size wp-block-paragraph\">the IO hub reads and writes back one cache line every 16K cycles &#8230; roughly 64 GB of memory behind the IO hub is scrubbed every day<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine Cache-Zeile alle 16.000 Takte, macht etwa 64 GB pro Tag. Bei meinen 256 GB dauert ein kompletter Durchlauf also rund vier Tage. Das klingt langsam, ist aber genau der Punkt: der Scrubber soll im Hintergrund verschwinden und nicht die Speicherbandbreite auffressen, die eigentlich f\u00fcr Arbeit gedacht ist.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Enable at End of POST<\/code> heisst, dass er nach dem Selbsttest startet, also bevor das Betriebssystem l\u00e4uft, und dann durchgehend weiterarbeitet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">PPR: der Reparaturmechanismus im Riegel<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s235.png\" alt=\"Aptio Setup, Memory Configuration, mit Enhanced PPR, PPR Type, Speichertakt und den Untermen\u00fcs f\u00fcr Topologie und RAS\" class=\"wp-image-4978\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s235.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s235-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s235-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s235-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Enhanced PPR                      [Disable]\nPPR Type                          [Hard PPR]\nEnforce POR                       [POR]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">PPR steht f\u00fcr Post Package Repair, und dahinter steckt etwas, das mich beim Nachlesen ehrlich \u00fcberrascht hat: moderne DDR4-Module haben Ersatzzeilen eingebaut. F\u00e4llt eine Speicherzeile dauerhaft aus, kann die Firmware sie stilllegen und eine Reservezeile an ihre Stelle setzen. Der Riegel repariert sich also selbst, im laufenden Betrieb einer Maschine, ohne dass ihn jemand anfasst.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Hard PPR<\/code> bedeutet, dass diese Umleitung dauerhaft in den Riegel gebrannt wird und einen Neustart \u00fcbersteht. Die Alternative <code>Soft PPR<\/code> gilt nur bis zum n\u00e4chsten Ausschalten. Hart ist sinnvoller, kostet aber eine der wenigen Reservezeilen unwiderruflich.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Enforce POR<\/code> steht f\u00fcr Plan of Record und ist die Frage, ob das Board die von Intel freigegebenen Speicherparameter erzwingt oder ob es auch aggressivere Kombinationen zul\u00e4sst. Auf einem Serverboard mit 256 GB ECC ist die Antwort selbstverst\u00e4ndlich ja.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Data Scrambling for DDR4 [Enable]<\/code> weiter unten macht etwas ganz anderes, als der Name vermuten l\u00e4sst. Das ist keine Verschl\u00fcsselung. Die Daten werden vor dem Schreiben mit einem Pseudozufallsmuster verw\u00fcrfelt, damit auf den Datenleitungen keine langen Folgen identischer Bits entstehen. Solche Muster erzeugen St\u00f6rungen auf den Nachbarleitungen und einen unruhigen Stromverbrauch. Verw\u00fcrfeln macht das Signalbild gleichm\u00e4ssiger. Wer glaubt, sein Speicher sei damit verschl\u00fcsselt, irrt sich, daf\u00fcr w\u00e4re TME zust\u00e4ndig, und \u00fcber den habe ich in Teil 4 geschrieben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Memory RAS: das Men\u00fc das kleiner ist als erwartet<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s237.png\" alt=\"Aptio Setup, Memory RAS Configuration, mit Mirror Mode auf Disabled und der Schwelle f\u00fcr korrigierbare Fehler\" class=\"wp-image-4979\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s237.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s237-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s237-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s237-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Memory RAS Configuration Setup\nEnable Pcode WA for SAI PG        [Disabled]\nMirror Mode                       [Disabled]\nUEFI ARM Mirror                   [Disabled]\nCorrectable Error Threshold       512<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">RAS steht f\u00fcr Reliability, Availability, Serviceability, und ich hatte hier ehrlich gesagt mehr erwartet. In Intel-Dokumentation tauchen an dieser Stelle regelm\u00e4ssig Rank Sparing, ADDDC Sparing und PCLS auf. Auf diesem Board gibt es davon nichts, die Seite hat genau diese vier Zeilen und keinen Rollbalken.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Mirror Mode<\/code> ist die interessanteste Option, gerade weil sie aus ist. Speicherspiegelung schreibt jeden Wert doppelt, auf zwei verschiedene Kan\u00e4le. F\u00e4llt eine Stelle dauerhaft aus, \u00fcbernimmt die Kopie, ohne dass die Maschine stehenbleibt. Der Preis ist die H\u00e4lfte des Speichers: aus 256 GB w\u00fcrden 128.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr einen Datenbankserver, bei dem eine Stunde Ausfall Geld kostet, kann das die richtige Rechnung sein. F\u00fcr meine Workstation nicht. ECC f\u00e4ngt Einzelfehler ohnehin ab, Patrol Scrub verhindert dass sie sich anh\u00e4ufen, und wenn wirklich ein Modul stirbt, tausche ich es. 128 GB zu verschenken, um mir einen Neustart zu ersparen, ist mir zu teuer.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Correctable Error Threshold 512<\/code> ist die Schwelle, ab der die Firmware eine Speicherstelle als auff\u00e4llig meldet. Einzelne korrigierte Fehler passieren, das ist Normalbetrieb. 512 an derselben Stelle sind ein Muster.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Die Gegenprobe, und warum ich sie erst nachr\u00fcsten musste<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">An dieser Stelle kommt der Teil, bei dem ich mich selbst ertappt habe. Der ganze Aufwand oben, ECC, Scrubber, PPR, Fehlerschwellen, l\u00e4uft ins Leere, wenn niemand hinsieht. Linux erfasst Speicherfehler \u00fcber das EDAC-Subsystem, und das war bei mir aktiv:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ls \/sys\/devices\/system\/edac\/mc\/\n#   mc0  mc1  mc2  mc3<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Vier Speichercontroller, alle angebunden. Nur landeten die Ereignisse ausschliesslich im Kernel-Ringpuffer, und der ist nach einem Neustart leer. Ich h\u00e4tte also gemerkt, wenn gerade etwas passiert. Ich h\u00e4tte nie gemerkt, dass ein bestimmter Riegel seit Wochen langsam schlechter wird. Genau das ist aber der Fall, f\u00fcr den man ECC \u00fcberhaupt haben will.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nachger\u00fcstet ist das mit einem einzigen Paket:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">apt-get install rasdaemon\nras-mc-ctl --error-count\n#   CPU_SrcID#0_MC#0_Chan#0_DIMM#0   CE 0   UE 0\n#   ... (acht Zeilen)\nras-mc-ctl --summary\n#   No Memory errors.<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>rasdaemon<\/code> schreibt die Ereignisse in eine SQLite-Datenbank und f\u00fchrt sie pro Modul. CE sind korrigierte Fehler, UE nicht korrigierbare. Bei mir stehen beide auf null, und jetzt weiss ich das auch f\u00fcr die Vergangenheit und nicht nur f\u00fcr diesen Bootvorgang.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Wermutstropfen: die acht Z\u00e4hler heissen <code>MC#0_Chan#0_DIMM#0<\/code> und nicht <code>DIMMA1<\/code>. Das Paket bringt f\u00fcr dieses Board keine Zuordnungstabelle mit. Ich k\u00f6nnte eine schreiben, die Reihenfolge liegt nahe, aber ich habe es bewusst gelassen. Ein falsches Etikett ist schlimmer als gar keins, weil es einen im Fehlerfall an den falschen Steckplatz schickt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">N\u00e4chste Folge: NUMA auf einer Maschine mit genau einem Sockel, warum das trotzdem ein Thema ist, und was Uncore und UPI eigentlich sind.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/21\/bios-erklaert-teil-4-aes-ni-sgx-tme-ppin-extended-apic\/\">Teil 4 der Serie<\/a> (TME, die Speicherverschl\u00fcsselung die Data Scrambling nicht ist)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/06\/24\/zfs-special-vdev-slog-tiered-storage-hdd-flaschenhals\/\">ZFS special vdev und SLOG<\/a> (Datenintegrit\u00e4t eine Ebene h\u00f6her)<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Falls jemand von euch schon einmal ein PPR-Ereignis in freier Wildbahn gesehen hat, also einen Riegel der sich tats\u00e4chlich selbst repariert hat: das w\u00fcrde ich gerne h\u00f6ren. Ihr d\u00fcrft mich jederzeit <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Acht Riegel, 256 GB, und ein Men\u00fc voller Abk\u00fcrzungen. ECC kennt jeder vom H\u00f6rensagen, aber was macht eigentlich Patrol Scrub, was ist ein Post Package Repair, und warum laufen DDR4-3200-Module in dieser Maschine mit 2933? Teil 6 der BIOS-Serie, inklusive der Zahl aus dem Handbuch die man sonst nirgends findet.<\/p>\n","protected":false},"author":1,"featured_media":4976,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[452,13],"tags":[38,453,373,447,456,244,457,459],"class_list":["post-4975","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bios-firmware","category-kernel-error-blog","tag-bios","tag-ecc","tag-homelab","tag-intel","tag-supermicro","tag-sysadmin","tag-uefi","tag-xeon","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4975","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=4975"}],"version-history":[{"count":3,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4975\/revisions"}],"predecessor-version":[{"id":5400,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4975\/revisions\/5400"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/4976"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=4975"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=4975"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=4975"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":4969,"date":"2026-08-28T10:00:00","date_gmt":"2026-08-28T08:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=4969"},"modified":"2026-08-28T09:28:56","modified_gmt":"2026-08-28T07:28:56","slug":"bios-erklaert-teil-5-speedstep-turbo-c-states-hardware-p-states","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/28\/bios-erklaert-teil-5-speedstep-turbo-c-states-hardware-p-states\/","title":{"rendered":"BIOS erkl\u00e4rt, Teil 5: SpeedStep, Turbo, C-States und der Schalter der mir eine Woche R\u00e4tselraten erspart h\u00e4tte"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">Willkommen bei Teil 5. Die bisherigen Folgen findest du gesammelt unter <a href=\"https:\/\/www.kernel-error.de\/category\/bios-firmware\/\">BIOS &amp; Firmware<\/a>. Heute geht es um <code>Advanced > CPU Configuration > Advanced Power Management Configuration<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Advanced<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s225.png\" alt=\"Aptio Setup, Advanced Power Management Configuration, mit Power Technology auf Custom und sechs Untermen\u00fcs\" class=\"wp-image-4971\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s225.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s225-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s225-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s225-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Seite selbst ist kurz:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Power Technology                  [Custom]\nPower Performance Tuning          [OS Controls EPB]\nENERGY_PERF_BIAS_CFG Mode         [Balanced Performance]\n> CPU P State Control\n> Hardware PM State Control\n> Frequency Prioritization\n> CPU C State Control\n> Package C State Control\n> CPU T State Control<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die erste Zeile ist wichtiger als sie aussieht. <code>Power Technology<\/code> kennt neben <code>Custom<\/code> noch <code>Disable<\/code> und <code>Energy Efficient<\/code>, und solange sie nicht auf <code>Custom<\/code> steht, sind die sechs Untermen\u00fcs Dekoration: die Firmware setzt dann ihre eigenen Werte und \u00fcberschreibt, was man darunter einstellt. Wer sich also wundert, warum eine \u00c4nderung im C-State-Men\u00fc nichts bewirkt, sollte zuerst hier nachsehen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Power Performance Tuning<\/code> auf <code>OS Controls EPB<\/code> 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 <code>ENERGY_PERF_BIAS_CFG Mode<\/code> ist der Vorgabewert, den die Firmware setzt, solange sich niemand darum k\u00fcmmert, und <code>Balanced Performance<\/code> ist daf\u00fcr eine vern\u00fcnftige Mitte.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">P-States: die Frequenz<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s226.png\" alt=\"Aptio Setup, CPU P State Control, mit SpeedStep, Turbo Mode und der Tabelle der Speed-Select-Profile\" class=\"wp-image-4972\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s226.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s226-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s226-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s226-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">SpeedStep (P-States)              [Enable]\nAVX P1                            [Nominal]\nDynamic SST-PP                    [Disable]\nIntel SST-PP                      [Base]\nActivate SST-BF                   [Disable]\nConfigure SST-BF                  [Enable]\nEIST PSD Function                 [HW_ALL]\nTurbo Mode                        [Enable]\nCPU Flex Ratio Override           [Disable]\nCPU Core Flex Ratio               23<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00e4uft der Prozessor stur auf Basistakt, verbraucht im Leerlauf deutlich mehr und wird w\u00e4rmer, ohne daf\u00fcr irgendetwas zur\u00fcckzugeben. Der bleibt an.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Turbo Mode<\/code> ist die Gegenrichtung: die CPU darf \u00fcber 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\u00e4tigt beide Enden:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">lscpu | grep MHz\n#   CPU max MHz:  3600,0000\n#   CPU min MHz:   800,0000<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>EIST PSD Function<\/code> auf <code>HW_ALL<\/code> regelt, wer die Frequenzwechsel koordiniert. Bei <code>HW_ALL<\/code> macht das die Hardware f\u00fcr alle Kerne einer Dom\u00e4ne selbst, bei <code>SW_ALL<\/code> m\u00fcsste das Betriebssystem mitreden. Hardware ist hier schneller und weiss ohnehin mehr, das l\u00e4sst man so. ALso ich, \u00f6hm. joar. \ud83d\ude00<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Teil der mich \u00fcberrascht hat: Speed Select<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Mitten auf der Seite steht eine kleine Tabelle, die ich vorher noch nie in einem BIOS gesehen habe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Intel SST-PP            Base | Config 3 | Config 4\n  Core Count             08  |    06    |    04\n  Current P1 Ratio [0]   32  |    32    |    34\n  Package TDP (W)       140  |   125    |   115\n  Tjmax                 103  |   100    |   101<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das ist Intel Speed Select Technology, Variante Performance Profile. Die Idee dahinter: dieselbe CPU kann in mehreren fest definierten Betriebspunkten laufen, und man w\u00e4hlt beim Booten aus, welchen man haben m\u00f6chte. Meine hat drei. Im Auslieferungszustand <code>Base<\/code> sind es acht Kerne bei 3,2 GHz und 140 Watt. W\u00e4hlt man <code>Config 4<\/code>, bleiben vier Kerne \u00fcbrig, daf\u00fcr steigt der Basistakt auf 3,4 GHz und die Verlustleistung f\u00e4llt auf 115 Watt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Man tauscht also Kerne gegen Takt, und zwar nicht per Turbo-Zufall, sondern als garantierte Zusicherung. F\u00fcr Software, die pro Kern lizenziert wird, ist das bares Geld. F\u00fcr Lasten, die nicht parallelisieren, sind 200 MHz mehr Basistakt sp\u00fcrbar. Und f\u00fcr mich? Ehrlich gesagt nutzlos. Ich habe acht Kerne gekauft, weil ich acht Kerne wollte, und 200 MHz sind kein Argument daf\u00fcr, die H\u00e4lfte davon wegzuwerfen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Trotzdem finde ich es bemerkenswert, dass so etwas in einem BIOS steht, das ansonsten aussieht wie 2005. <code>SST-BF<\/code> daneben ist die verwandte Idee auf Kernebene: Base Frequency, einzelne Kerne bekommen dauerhaft mehr Takt, die \u00fcbrigen entsprechend weniger. Steht bei mir auf <code>Disable<\/code>, und das passt, denn daf\u00fcr m\u00fcsste ich meine Last gezielt auf die schnellen Kerne pinnen. Wer das tut, weiss warum. Wer es nicht tut, gewinnt nichts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Und hier lag die Antwort<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s227.png\" alt=\"Aptio Setup, Hardware PM State Control, Hardware P-States steht auf Disable\" class=\"wp-image-4973\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s227.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s227-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s227-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s227-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Hardware PM State Control\nHardware P-States                 [Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Untermen\u00fc mit genau einer Zeile, und diese Zeile hat mich mehr gefreut als der ganze Rest der Seite. Um zu erkl\u00e4ren warum, muss ich kurz ausholen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Seit dem Umbau ist mir aufgefallen, dass Linux die Frequenzsteuerung auf dieser Maschine anders macht als erwartet. <code>intel_pstate<\/code>, der Treiber der bei Intel-CPUs normalerweise das Kommando \u00fcbernimmt, l\u00e4uft hier im passiven Modus:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cat \/sys\/devices\/system\/cpu\/intel_pstate\/status        # passive\ncat \/sys\/devices\/system\/cpu\/cpu0\/cpufreq\/scaling_driver # intel_cpufreq\ncat \/sys\/devices\/system\/cpu\/cpu0\/cpufreq\/scaling_governor # schedutil<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Passiv bedeutet: der Treiber verh\u00e4lt sich wie ein gew\u00f6hnlicher cpufreq-Treiber und l\u00e4sst den Kernel-Governor entscheiden. Aktiv bedeutet, dass er die Entscheidung selbst trifft und dabei Hardware-Unterst\u00fctzung nutzt. Aktiv ist normalerweise der bessere Modus, weil die CPU schneller reagiert als jeder Governor es k\u00f6nnte.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -o hwp \/proc\/cpuinfo | head -1\n#   (keine Ausgabe)<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ich hatte das eine Weile f\u00fcr eine Eigenheit des Kernels gehalten, f\u00fcr eine Frage der Xeon-Variante, f\u00fcr irgendetwas Kompliziertes. Es war nichts davon. Es war dieser eine Schalter, zwei Men\u00fcebenen tief in einem Untermen\u00fc, das ich beim ersten Mal nicht ge\u00f6ffnet hatte. Ich sag doch, ich bin auch nur doof.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Werde ich es umstellen? Vermutlich ja, aber nicht heute und nicht ohne Messung. Auf der alten Maschine hatte ich \u00c4rger mit <code>schedutil<\/code>, der mir zu tr\u00e4ge 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">C-States: das Nichtstun<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s229.png\" alt=\"Aptio Setup, CPU C State Control, mit Monitor MWAIT, C6 Report und Enhanced Halt State\" class=\"wp-image-4974\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s229.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s229-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s229-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s229-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Enable Monitor MWAIT              [Enable]\nCPU C6 Report                     [Auto]\nEnhanced Halt State (C1E)         [Enable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">P-States regeln, wie schnell die CPU arbeitet. C-States regeln, wie tief sie schl\u00e4ft wenn sie gerade nichts tut. Je h\u00f6her die Nummer, desto mehr wird abgeschaltet und desto l\u00e4nger dauert das Aufwachen. Was auf meiner Maschine ankommt, steht im sysfs:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">POLL   0 us\nC1     1 us\nC1E    4 us\nC6   170 us<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00fcr einen Rechner unter dem Schreibtisch ist das vollkommen egal. F\u00fcr einen Paketfilter, der Latenz im einstelligen Mikrosekundenbereich garantieren soll, ist es das nicht, und genau deshalb steht in Tuning-Anleitungen f\u00fcr Netzwerkkram regelm\u00e4ssig, man solle die tiefen C-States abschalten.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Enable Monitor MWAIT<\/code> ist die Voraussetzung f\u00fcr den ganzen Mechanismus: MONITOR und MWAIT sind die beiden Befehle, mit denen ein Kern sagt \u201eweck mich, wenn sich diese Speicherstelle \u00e4ndert&#8220; und sich danach schlafen legt. Ohne sie bleibt nur die alte HLT-Schleife.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>Package C State [Auto]<\/code> eine Men\u00fcebene weiter ist die Steigerung davon: nicht mehr einzelne Kerne, sondern das ganze Paket samt Uncore und Speichercontroller. Das geht nat\u00fcrlich nur, wenn wirklich alle Kerne gleichzeitig nichts tun.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">T-States, und warum sie aus sind<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Software Controlled T-States      [Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der dritte Buchstabe im Bunde, und der unangenehmste. T-States drosseln nicht die Frequenz, sondern schieben Pausen ein: die CPU bekommt f\u00fcr einen Teil der Zeit schlicht keinen Takt. Das ist deutlich brutaler als ein P-State und eigentlich eine Notbremse f\u00fcr den Fall, dass es zu heiss wird.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Auf <code>Disable<\/code> heisst, dass das Betriebssystem diese Notbremse nicht selbst ziehen darf. Der thermische Schutz der Hardware bleibt davon unber\u00fchrt, der arbeitet unabh\u00e4ngig weiter. Bei Tjmax 103 Grad und CPU-Temperaturen um die 40 im Leerlauf ist das eine sehr theoretische Diskussion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Rest<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Frequency Prioritization\nRAPL Prioritization               [Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">RAPL ist Intels Mechanismus zur Leistungsbegrenzung. Mit <code>RAPL Prioritization<\/code> verteilt die Firmware ein knappes Leistungsbudget gezielt auf einzelne Kerne, statt alle gleichm\u00e4ssig zu drosseln. Das ergibt Sinn, wenn man dauerhaft am Powerlimit f\u00e4hrt und weiss, welche Kerne die wichtige Arbeit machen. Meine Maschine ist von beidem weit entfernt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was ich mitnehme<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der ganze Block ist erstaunlich gut eingestellt f\u00fcr ein Board, das im Auslieferungszustand von einem Serverhersteller kam. SpeedStep an, Turbo an, C-States an, T-States aus, das w\u00fcrde ich genau so einstellen. Ob da Supermicro oder Thomas Krenn sich M\u00fche gegeben hat?<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der einzige echte Fund ist <code>Hardware P-States [Disable]<\/code>. Und der ist vor allem deshalb sch\u00f6n, 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\u00fc. Ich musste nur hineinschauen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">N\u00e4chste 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?<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/14\/bios-erklaert-teil-3-cpu-kerne-threads-prefetcher\/\">Teil 3 der Serie<\/a> (Kerne, Threads und die f\u00fcnf Prefetcher)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/21\/bios-erklaert-teil-4-aes-ni-sgx-tme-ppin-extended-apic\/\">Teil 4 der Serie<\/a> (AES-NI, SGX und ein Schalter der nichts bewirkt)<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Falls jemand von euch <code>intel_pstate<\/code> im aktiven Modus gegen <code>schedutil<\/code> gemessen hat, auf einem Xeon und nicht auf einem Notebook: erz\u00e4hl mir davon, bevor ich es selbst ausprobiere. Ihr d\u00fcrft mich gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Diese Folge g\u00e4be es fast nicht, weil das Men\u00fc in meiner ersten Bildschirmaufnahme schlicht gefehlt hat. Ich musste noch einmal ins Setz. Daf\u00fcr stand darin die Antwort auf eine Frage, an der ich vorher eine Woche lang herumger\u00e4tselt habe: warum meine CPU eine Funktion nicht nutzt, die sie eigentlich beherrscht.<\/p>\n","protected":false},"author":1,"featured_media":4970,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[452,13],"tags":[38,347,373,447,456,244,457,459],"class_list":["post-4969","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bios-firmware","category-kernel-error-blog","tag-bios","tag-hardware","tag-homelab","tag-intel","tag-supermicro","tag-sysadmin","tag-uefi","tag-xeon","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4969","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=4969"}],"version-history":[{"count":7,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4969\/revisions"}],"predecessor-version":[{"id":5394,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4969\/revisions\/5394"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/4970"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=4969"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=4969"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=4969"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":4965,"date":"2026-08-21T10:00:00","date_gmt":"2026-08-21T08:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=4965"},"modified":"2026-08-20T16:30:52","modified_gmt":"2026-08-20T14:30:52","slug":"bios-erklaert-teil-4-aes-ni-sgx-tme-ppin-extended-apic","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/21\/bios-erklaert-teil-4-aes-ni-sgx-tme-ppin-extended-apic\/","title":{"rendered":"BIOS erkl\u00e4rt, Teil 4: AES-NI, SGX, TME und ein Schalter der nichts bewirkt"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">In der letzten Folge ging es um die obere H\u00e4lfte der CPU-Seite, also Kerne, Threads und Prefetcher. Die untere H\u00e4lfte ist die spannendere, weil dort keine Leistungsschalter mehr stehen, sondern Sicherheitsfunktionen. Und weil mindestens einer davon eine richtig gute Geschichte hat.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zur Erinnerung f\u00fcr alle die neu dazustossen: ich gehe hier das BIOS meines Supermicro-Serverboards durch, Themenblock f\u00fcr Themenblock, alle Folgen liegen unter <a href=\"https:\/\/www.kernel-error.de\/category\/bios-firmware\/\">BIOS &amp; Firmware<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">AES-NI, oder: der Grund warum es eine Karte gab<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s010.png\" alt=\"Aptio Setup, CPU Configuration, mit Hyper-Threading, den f\u00fcnf Prefetchern und AES-NI\" class=\"wp-image-4964\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s010.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s010-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s010-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s010-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">AES-NI                          [Enable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Schalter und dahinter steckt eine ganze \u00c4ra. AES-NI sind sechs zus\u00e4tzliche Prozessorbefehle, die AES in Hardware rechnen statt in Software. Der Unterschied ist keine Feinheit, er liegt grob bei einer Gr\u00f6ssenordnung, und obendrein sind die Hardwarebefehle immun gegen die Cache-Timing-Seitenkan\u00e4le, mit denen man Software-AES angreifen kann. Aber vor allem is et dann ers rischtig flott mit der Crypto.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ob die CPU es kann und darf, sieht man sofort im OS mit:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -o ' aes ' \/proc\/cpuinfo | head -1\n#    aes<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Warum es diesen Schalter \u00fcberhaupt gibt, ist eine berechtigte Frage. Man schaltet AES-NI ja nicht ab, weil man gern langsam verschl\u00fcsselt. 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\u00e4rung?<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">\u00dcber 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\u00fcsseln wollte, brauchte Hilfe. Heute ist der Schalter eine Formalie, damals war er die Grenze zwischen &#8222;geht&#8220; und &#8222;geht nicht&#8220;.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Virtualisierung und ein Schalter der Vertrauen heisst<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Intel Virtualization Technology [Enable]\nEnable SMX                      [Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der erste ist VT-x, im Handbuch etwas altmodisch <code>VMX<\/code> und in einer noch \u00e4lteren Fassung sogar <code>Vanderpool Technology<\/code> genannt, nach dem Codenamen von 2003. Ohne ihn l\u00e4uft keine Hardware-Virtualisierung, also weder KVM noch VirtualBox mit brauchbarer Geschwindigkeit. Bleibt selbstverst\u00e4ndlich an.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der zweite ist interessanter. SMX steht f\u00fcr Safer Mode Extensions und ist die Grundlage f\u00fcr 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, \u00e4hnlich wie Measured Boot mit einem TPM, nur eine Ebene tiefer.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dabei ist mir etwas aufgefallen, das ich nicht restlos erkl\u00e4ren kann. Im Setup steht SMX auf <code>Disable<\/code>. In <code>\/proc\/cpuinfo<\/code> steht das Flag <code>smx<\/code> trotzdem drin. Die naheliegende Deutung ist, dass das Flag die F\u00e4higkeit der CPU beschreibt und der BIOS-Schalter nur die Freigabe, dass CPUID also weiterhin &#8222;kann ich&#8220; meldet, w\u00e4hrend die Firmware &#8222;darfst du nicht&#8220; 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Speicherverschl\u00fcsselung, und warum SGX daran h\u00e4ngt<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Total Memory Encryption (TME)   [Disabled]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">TME verschl\u00fcsselt den kompletten Arbeitsspeicher. Der Schl\u00fcssel wird bei jedem Start neu erzeugt, liegt im Speichercontroller und verl\u00e4sst die CPU nie. F\u00fcr das Betriebssystem ist das unsichtbar, es merkt schlicht nichts davon.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wogegen hilft das? Gegen Angriffe, die physisch am Speicher ansetzen. Der Klassiker ist der Cold-Boot-Angriff, bei dem man die Riegel k\u00fchlt, aus der laufenden Maschine reisst und in einem anderen Ger\u00e4t ausliest, weil DRAM seinen Inhalt f\u00fcr Sekunden bis Minuten beh\u00e4lt. 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.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Cold Boot Attack | University of South Wales VeraCrypt Research Group\" width=\"676\" height=\"380\" src=\"https:\/\/www.youtube.com\/embed\/XfUlRsE3ymQ?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei mir steht es auf <code>Disabled<\/code>, und das ist der Werksdefault. Meine Maschine steht in meiner Wohnung, die Systemplatte ist ohnehin mit LUKS verschl\u00fcsselt, und das Angriffsmodell, gegen das TME hilft, setzt jemanden voraus der hier steht w\u00e4hrend die Kiste l\u00e4uft. Falls das passiert, habe ich gr\u00f6ssere Probleme. Reizvoll finde ich es trotzdem, denn die Kosten sind gering: die Verschl\u00fcsselung sitzt im Speichercontroller und macht sich in Messungen kaum bemerkbar.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Detail, \u00fcber das ich beim Lesen des Handbuchs gestolpert bin, erkl\u00e4rt den Aufbau des Men\u00fcs:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-small-font-size wp-block-paragraph\">*If the feature above is set to Enabled, the next five features are displayed*<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Erst wenn TME an ist, erscheinen die f\u00fcnf Zeilen darunter, und dazu geh\u00f6rt auch SGX. Die Enklaven-Technik h\u00e4ngt hier also an der Speicherverschl\u00fcsselung. Das ergibt Sinn, denn der gesch\u00fctzte 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\u00e4ngigkeit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">SGX, und wie Intel die UHD-Blu-ray auf dem PC beerdigt hat<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">SW Guard Extensions (SGX)       [Disabled]\nSGX Factory Reset               [Disabled]\nSGX Package Info In-Band Access [Disabled]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00fcsselt den Bereich und gibt ihn nur dem Code frei, der ihn angelegt hat. Diese Enklaven waren als Fundament f\u00fcr vertrauliche Berechnungen in fremden Rechenzentren gedacht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00fcsselaustausch 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\u00f6tzlich einen \u00e4lteren Prozessor.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ich finde das aus mehreren Gr\u00fcnden bemerkenswert. Erstens ist es ein sch\u00f6nes Beispiel daf\u00fcr, 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\u00fcr ihre legal gekauften Scheiben pl\u00f6tzlich keinen legalen Abspielweg mehr hatten, w\u00e4hrend der inoffizielle Weg weiterhin problemlos funktionierte. Kopierschutz eben.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00e4ndigkeit halber, im laufenden System taucht folgerichtig nichts davon auf:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -c ' sgx' \/proc\/cpuinfo    # 0\nls \/sys\/devices\/system\/cpu\/ | grep -c sgx    # 0<\/pre>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">46 Bit, wegen Hyper-V<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Limit CPU PA to 46 bits         [Enable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">PA steht f\u00fcr 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.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Begr\u00fcndung im Handbuch ist knapp und ziemlich entlarvend:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-small-font-size wp-block-paragraph\">Use this feature to limit the CPU physical address to 46 bits to support older hyper-v.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">\u00c4ltere Hyper-V-Versionen kamen mit breiteren Adressen nicht klar, also gibt es im BIOS eines Serverboards von 2021 einen Schalter, der die CPU k\u00fcnstlich einschr\u00e4nkt, damit eine bestimmte Microsoft-Software l\u00e4uft. Das ist Abw\u00e4rtskompatibilit\u00e4t in ihrer reinsten Form. Der Effekt l\u00e4sst sich direkt nachlesen:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -m1 'address sizes' \/proc\/cpuinfo\n#   address sizes : 46 bits physical, 57 bits virtual<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">46 physisch, wie eingestellt. Die 57 virtuellen Bit sind \u00fcbrigens eine ganz andere Baustelle, das ist Five-Level Paging, ebenfalls neu mit Ice Lake.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Eine Seriennummer im Prozessor<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"600\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s011.png\" alt=\"Aptio Setup, untere H\u00e4lfte der CPU Configuration, mit TME, SGX, PPIN Control und Extended APIC\" class=\"wp-image-4968\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s011.png 800w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s011-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s011-768x576.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/s011-676x507.png 676w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">PPIN Control                    [Lock\/Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">PPIN steht f\u00fcr Protected Processor Inventory Number, eine eindeutige Nummer pro physischer CPU. Gedacht ist sie f\u00fcr die Fehleranalyse in grossen Flotten: wenn ein Prozessor Speicherfehler meldet, will man wissen welcher es war, und zwar auch nachdem er l\u00e4ngst ausgebaut wurde.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine eindeutige, unver\u00e4nderliche Hardwarenummer ist allerdings genau die Sorte Datenpunkt, an der sich vor Jahren schon einmal die Gem\u00fcter 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\u00e4nkt auf Serverprozessoren.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Hier ist mir eine Abweichung aufgefallen. Das Handbuch nennt als m\u00f6gliche Werte:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-small-font-size wp-block-paragraph\">The options are Unlock\/Disable and Unlock\/Enable.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im Setup steht aber <code>Lock\/Disable<\/code>, ein Wert den das Handbuch gar nicht kennt. Passend dazu fehlt im laufenden System das Flag:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">grep -c intel_ppin \/proc\/cpuinfo    # 0<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Meine Lesart: <code>Lock<\/code> 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\u00fcr mich ist das der Wunschzustand, ich brauche keine Seriennummer und niemand sonst braucht sie von mir. Nur sch\u00f6n dokumentiert ist es eben nicht.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Und zum Schluss der Schalter, der nichts bewirkt<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Extended APIC                   [Disable]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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 \u00fcber Register statt \u00fcber den Speicher erlaubt. Auf <code>Disable<\/code> denkt man reflexhaft: aha, hier liegt Leistung brach, das schalte ich mal ein.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nur ist die Funktion l\u00e4ngst aktiv. Aus dem Kernel-Log meiner laufenden Maschine:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.\nDMAR-IR: Enabled IRQ remapping in x2apic mode\nx2apic enabled\nAPIC: Switched APIC routing to: cluster x2apic<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Grund ist eine Abh\u00e4ngigkeit, die man kennen muss: Interrupt-Remapping \u00fcber VT-d verlangt x2APIC. Sobald der Kernel die IOMMU mit Interrupt-Remapping hochf\u00e4hrt, schaltet er x2APIC selbst ein, unabh\u00e4ngig 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?<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ich lasse ihn deshalb bewusst in Ruhe, und ich schreibe das hier so ausf\u00fchrlich auf, weil er genau die Art Schalter ist, an dem man beim n\u00e4chsten Setup-Besuch wieder h\u00e4ngenbleibt und denkt, man h\u00e4tte etwas \u00fcbersehen. Hat man nicht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">N\u00e4chste Folge: Speicher. ECC, Patrol Scrub, und die Frage warum mein DDR4-3200 mit 2933 l\u00e4uft und das v\u00f6llig in Ordnung ist.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/14\/bios-erklaert-teil-3-cpu-kerne-threads-prefetcher\/\">Teil 3 der Serie<\/a> (Kerne, Threads und die f\u00fcnf Prefetcher)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/06\/intel-quickassist-8950-sccp-krypto-beschleuniger-gegen-moderne-cpu\/\">Intel QuickAssist 8950-SCCP<\/a> (Krypto in Hardware, als die CPU es noch nicht konnte)<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/07\/26\/x25519mlkem768-zerlegt-post-quantum-handshake-tls\/\">X25519MLKEM768 zerlegt<\/a> (wenn Verschl\u00fcsselung ins Detail geht)<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Weiss jemand genauer, warum das <code>smx<\/code>-Flag in <code>\/proc\/cpuinfo<\/code> auftaucht obwohl der BIOS-Schalter auf <code>Disable<\/code> steht? Ich habe dazu keine belastbare Quelle gefunden und w\u00fcrde es gern richtig verstehen. Ihr d\u00fcrft mich jederzeit <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Die untere H\u00e4lfte der CPU-Seite ist die interessantere: Hardware-Verschl\u00fcsselung, Speicherverschl\u00fcsselung, eine Enklaven-Technik die Intel aus den Consumer-CPUs geworfen und damit die UHD-Blu-ray auf dem PC get\u00f6tet hat, eine Seriennummer pro Prozessor, und ein Schalter auf `Disable`, dessen Funktion trotzdem aktiv ist.<\/p>\n","protected":false},"author":1,"featured_media":4966,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[452,13],"tags":[38,377,371,447,456,244,457,459],"class_list":["post-4965","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bios-firmware","category-kernel-error-blog","tag-bios","tag-encryption","tag-infosec","tag-intel","tag-supermicro","tag-sysadmin","tag-uefi","tag-xeon","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4965","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=4965"}],"version-history":[{"count":10,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4965\/revisions"}],"predecessor-version":[{"id":5376,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/4965\/revisions\/5376"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/4966"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=4965"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=4965"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=4965"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5320,"date":"2026-08-17T16:16:38","date_gmt":"2026-08-17T14:16:38","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5320"},"modified":"2026-08-17T16:16:38","modified_gmt":"2026-08-17T14:16:38","slug":"sd-karte-gesundheit-pruefen-linux-ohne-smart","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/17\/sd-karte-gesundheit-pruefen-linux-ohne-smart\/","title":{"rendered":"Kein SMART f\u00fcr SD-Karten: sieben Linux-Werkzeuge, die trotzdem verraten, wie gesund eine Karte ist"},"content":{"rendered":"<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux-1024x683.png\" alt=\"Transcend-8-GB-microSD-Karte im SD-Adapter und USB-Kartenleser neben Linux-Werkzeugen zur Pr\u00fcfung von Kapazit\u00e4t, Datenintegrit\u00e4t und Schreibleistung ohne SMART.\" class=\"wp-image-5321\" style=\"aspect-ratio:1.4992791994609649;width:143px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux-1024x683.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux-300x200.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux-768x512.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux-676x451.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sc-card-linux.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine alte 8-GB-microSD-Karte lag noch in der Schublade, entbehrlich genug f\u00fcr einen H\u00e4rtetest. Die Ausgangsfrage war simpel: ist die Karte noch gut, oder produziert sie im Hintergrund l\u00e4ngst stille Fehler? Bei einer SSD w\u00fcrde ich <code>smartctl -a<\/code> tippen und h\u00e4tte 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\u00fcr ist interessanter als die fehlende Zahl selbst.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Warum SMART hier nicht funktioniert<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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 \u00fcbernimmt zwar Wear-Leveling und Bad-Block-Mapping, aber wie er das im Detail tut und was er dar\u00fcber nach au\u00dfen preisgibt, ist herstellerspezifisch und komplett verschlossen. Es gibt zwar eine SD Health Status Extension, CMD56-basiert, die unterst\u00fctzt aber so gut wie kein Consumer-Werkzeug, und sie setzt einen nativen MMC-Host voraus, keinen USB-Kartenleser.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Wort noch zu TRIM, bevor der Eindruck entsteht, SD-Karten h\u00e4tten damit \u00fcberhaupt nichts zu tun: der Linux-MMC-Stack unterst\u00fctzt <code>discard<\/code>-artige Operationen f\u00fcr native <code>mmcblk<\/code>-Ger\u00e4te durchaus. Nur bringt das \u00fcber einen USB-Kartenleser nichts, weil die USB-Massenspeicher-\u00dcbersetzung diesen Pfad gar nicht durchreicht, das habe ich in diesem Test empirisch best\u00e4tigt. Und selbst auf einem nativen Host w\u00e4re das nur ein Erase-Hinweis an den Controller, keine standardisierte Health-R\u00fcckmeldung wie bei SSD-TRIM zusammen mit SMART.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Testkandidat und Aufbau<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr den Test kam eine Transcend Premium 8GB microSDHC zum Einsatz, Class 10, UHS-Speed-Class U1, in einem Samsung-Adapter auf volle SD-Gr\u00f6\u00dfe gesteckt und \u00fcber 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\u00fcnstigen 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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader-1024x768.jpg\" alt=\"MicroSD-Karte im Samsung-Adapter, gesteckt in einen schwarzen USB-Kartenleser auf einer K\u00fcchenarbeitsplatte\" class=\"wp-image-5318\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader-1024x768.jpg 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader-300x225.jpg 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader-768x576.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader-676x507.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-adapter-im-reader.jpg 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">So kam die Karte beim Testsystem an: microSD im Samsung-Adapter, gesteckt in den UGREEN-USB-Kartenleser.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Werkzeuge installieren<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bis auf <code>badblocks<\/code>, <code>smartctl<\/code>, <code>hdparm<\/code> und <code>dd<\/code>, die auf den meisten Systemen ohnehin vorhanden sind, kommt der Rest aus den Standard-Paketquellen, keine Drittanbieter-PPA n\u00f6tig:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">apt-get install -y f3 mmc-utils sdparm flashbench\napt-get install -y fio   # nachinstalliert, sobald der Schritt feststand<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 1: Identifikation, warum SMART eine Sackgasse ist<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ smartctl -a \/dev\/sdc\n\/dev\/sdc: Unknown USB bridge [0x05e3:0x0748 (0x1209)]\nPlease specify device type with the -d option.\n\n$ smartctl -a -d sat \/dev\/sdc\nRead Device Identity failed: scsi error unsupported scsi opcode\nA mandatory SMART command failed: exiting.\n\n$ smartctl --scan\n\/dev\/nvme0 -d nvme\n\/dev\/nvme1 -d nvme\n# sdc taucht gar nicht erst auf, smartd h\u00e4lt es f\u00fcr keinen unterst\u00fctzten Ger\u00e4tetyp\n\n$ sdparm -a \/dev\/sdc\nMODE SENSE(10): Malformed SCSI command\n    \/dev\/sdc: Generic   MassStorageClass  1209\n\n$ sdparm -i \/dev\/sdc\n    \/dev\/sdc: Generic   MassStorageClass  1209\nDevice identification VPD page:\n  Addressed logical unit:\n    designator type: T10 vendor identification,  code set: ASCII\n      vendor id: Generic\n      vendor specific: STORAGE DEVICE<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>smartctl<\/code> erkennt die Genesys-Logic-USB-Bridge gar nicht erst als unterst\u00fctzten Ger\u00e4tetyp. Erzwingt man SAT, also ATA-Kommandos \u00fcber SCSI getunnelt, scheitert das vollst\u00e4ndig, weil die Bridge-Firmware das Kommando schlicht nicht implementiert. Selbst der Ger\u00e4te-Scan von smartd listet <code>\/dev\/sdc<\/code> gar nicht auf. <code>sdparm<\/code> kommt einen Schritt weiter, eine simple SCSI-INQUIRY funktioniert, aber die gemeldete Identit\u00e4t bleibt vollst\u00e4ndig generisch: \u201eGeneric MassStorageClass&#8220;, \u201eSTORAGE DEVICE&#8220;. Weder Hersteller-ID noch Seriennummer noch irgendeine Form von Wear- oder Health-Daten sind \u00fcber diesen Pfad erreichbar. Das ist der konkrete, reproduzierbare Beleg daf\u00fcr, dass es f\u00fcr SD-Karten hinter einem USB-Leser kein SMART-\u00c4quivalent gibt.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ lsusb -v -d 05e3:0748\nBus 002 Device 007: ID 05e3:0748 Genesys Logic, Inc. All-in-One Cardreader\n  bcdUSB               3.10\n  idVendor           0x05e3 Genesys Logic, Inc.\n  idProduct          0x0748 All-in-One Cardreader\n  iSerial                 5 000000001209\n  bInterfaceClass         8 Mass Storage\n  bInterfaceSubClass      6 SCSI\n  bInterfaceProtocol     80 Bulk-Only\n  SuperSpeed USB Device Capability:\n    wSpeedsSupported   0x000e  (Full\/High\/SuperSpeed, bis zu 5Gbps Link)<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Leser meldet sich als \u201eAll-in-One Cardreader&#8220; von Genesys Logic, ein verbreiteter generischer Combo-Chip, USB-3.1-f\u00e4hig, spricht auf der SCSI-Ebene aber Bulk-Only Transport (BOT), nicht UAS. Wichtig f\u00fcr die Kausalit\u00e4t: BOT statt UAS ist f\u00fcr sich genommen nicht der Grund, warum SAT-Passthrough scheitert. Es gibt genug BOT-Bridges, die SAT unterst\u00fctzen, und genug, die es nicht tun, unabh\u00e4ngig 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\u00e4tzlich nicht k\u00f6nnten.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"1024\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label-768x1024.jpg\" alt=\"Transcend Premium 8GB microSDHC Class 10 U1 im Samsung-Adapter, liegend auf einem UGREEN-USB-Kartenleser mit blau leuchtender Status-LED\" class=\"wp-image-5319\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label-768x1024.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label-225x300.jpg 225w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label-676x901.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/sdcard-transcend-8gb-label.jpg 1050w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/a><figcaption class=\"wp-element-caption\">Der Aufdruck verr\u00e4t mehr als jedes Software-Tool: Transcend Premium, 8GB, Class 10, UHS-Speed-Class U1.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 2: Durchsatz mit dd und hdparm<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>hdparm -t<\/code> allein ergab rund 90 MB\/s gepuffertes Lesen, plausibel, aber es lohnt sich, mit echtem <code>O_DIRECT<\/code>-I\/O gegenzupr\u00fcfen, damit keine Bridge- oder Seiten-Cache-Effekte den Wert verf\u00e4lschen.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ hdparm -Tt \/dev\/sdc\n Timing cached reads:   24182 MB in  2.00 seconds = 12113.48 MB\/sec\n Timing buffered disk reads: 272 MB in  3.00 seconds =  90.66 MB\/sec\n\n# 512MB Zufallsnutzlast zuerst im tmpfs erzeugt, damit die CPU-Last von\n# \/dev\/urandom die eigentliche Messung nicht verf\u00e4lscht\n$ dd if=\/dev\/urandom of=\/root\/sdtest\/payload.bin bs=1M count=512\n536870912 Bytes (537 MB, 512 MiB) kopiert, 1,74754 s, 307 MB\/s\n\n# SCHREIBEN: Direct I\/O direkt auf das Rohger\u00e4t, kein Seiten-Cache beteiligt\n$ dd if=\/root\/sdtest\/payload.bin of=\/dev\/sdc bs=1M count=512 oflag=direct,sync\n536870912 Bytes (537 MB, 512 MiB) kopiert, 22,8654 s, 23,5 MB\/s\n\n# LESEN: erst flushen, dann Direct I\/O der gleichen 512MB zur\u00fcck\n$ blockdev --flushbufs \/dev\/sdc\n$ dd if=\/dev\/sdc of=\/root\/sdtest\/readback.bin bs=1M count=512 iflag=direct\n536870912 Bytes (537 MB, 512 MiB) kopiert, 6,28156 s, 85,5 MB\/s\n\n# Integrit\u00e4tscheck\n$ cmp \/root\/sdtest\/payload.bin \/root\/sdtest\/readback.bin\nMATCH: identical<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ergebnis: rund 23,5 MB\/s sequenzielles Schreiben, rund 85,5 MB\/s sequenzielles Lesen, mit byteidentischem R\u00fccklese-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\u00dft die <a href=\"https:\/\/www.sdcard.org\/consumers\/speed-class\/\" target=\"_blank\" rel=\"noopener\">Class-10- und U1-Mindestangabe<\/a> von 10 MB\/s locker, f\u00fcr U3\/V30 mit 30 MB\/s Minimum w\u00fcrde es nicht reichen, was exakt zum aufgedruckten Rating der Karte passt: Class 10, U1, keine U3- oder V30-Kennzeichnung.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 3: f3probe, ist die Kapazit\u00e4t echt?<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bevor der gro\u00dfe, langsame Test kommt, lohnt sich der schnelle: <a href=\"https:\/\/github.com\/AltraMayor\/f3\" target=\"_blank\" rel=\"noopener\">f3<\/a> (Fight Flash Fraud) bringt mit <code>f3probe<\/code> einen Kapazit\u00e4tsbetrugs-Check, der binnen weniger Minuten l\u00e4uft, statt die ganze Karte zu beschreiben.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ f3probe --time-ops \/dev\/sdc\n\nProbe finished, recovering blocks... Done\n\nGood news: The device `\/dev\/sdc' is the real thing\n\nDevice geometry:\n\t         *Usable* size: 7.31 GB (15333376 blocks)\n\t        Announced size: 7.31 GB (15333376 blocks)\n\t                Module: 8.00 GB (2^33 Bytes)\n\tApproximate cache size: 0.00 Byte (0 blocks), need-reset=no\n\t   Physical block size: 512.00 Byte (2^9 Bytes)\n\nProbe time: 1'46\"\n Operation: total time \/ count = avg time\n      Read: 29.32s \/ 4197116 = 6us\n     Write: 1'15\" \/ 4192321 = 18us\n     Reset: 0us \/ 1 = 0us<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Verdikt: eine echte Karte, keine F\u00e4lschung. Die angek\u00fcndigte Gr\u00f6\u00dfe, 8-GB-Modul mit 7,31 GB nutzbar, deckt sich mit der tats\u00e4chlich nutzbaren Gr\u00f6\u00dfe, die f3probe per bin\u00e4rer Suche gefunden hat, also dort, wo Schreibvorg\u00e4nge aufh\u00f6ren, korrekt anzukommen. <code>Approximate cache size: 0.00 Byte<\/code> ist ebenfalls ein gutes Zeichen: manche gef\u00e4lschten Karten t\u00e4uschen ihre Kapazit\u00e4t vor, indem sie eine kleine Menge echten Flashs als Write-Back-Cache benutzen, der Schreibvorg\u00e4nge \u00fcber die tats\u00e4chliche Kapazit\u00e4t hinaus vor\u00fcbergehend \u201eschluckt&#8220; und damit naive Benchmarks t\u00e4uscht. Diese Karte zeigt diesen Trick nicht. f3probe hat die f\u00fcr die Pr\u00fcfung benutzten Bl\u00f6cke danach wiederhergestellt, ohne <code>-n<\/code> 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\u00e4ndiger Schreib-Lese-Durchlauf, weil f3probe gezielt sucht statt jeden Block anzufassen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 4: f3write und f3read, der Haupttest<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ wipefs -a \/dev\/sdc\n$ parted -s \/dev\/sdc mklabel gpt mkpart primary ext4 0% 100%\n$ mkfs.ext4 -F -L SDTEST \/dev\/sdc1\n$ mount \/dev\/sdc1 \/mnt\/sdtest\n$ df -h \/mnt\/sdtest\n\/dev\/sdc1       7,2G     24K  6,8G    1% \/mnt\/sdtest\n\n$ cd \/mnt\/sdtest &amp;&amp; f3write .\nFree space: 7.10 GB\nCreating file 1.h2w ... OK!\n...\nCreating file 8.h2w ... OK!\nFree space: 16.46 MB\nAverage writing speed: 22.78 MB\/s\n\n$ f3read .\n                  SECTORS      ok\/corrupted\/changed\/overwritten\nValidating file 1.h2w ... 2097152\/        0\/      0\/      0\n...\nValidating file 8.h2w ...  176128\/        0\/      0\/      0\n\n  Data OK: 7.08 GB (14856192 sectors)\nData LOST: 0.00 Byte (0 sectors)\n\t       Corrupted: 0.00 Byte (0 sectors)\n\tSlightly changed: 0.00 Byte (0 sectors)\n\t     Overwritten: 0.00 Byte (0 sectors)\nAverage reading speed: 90.32 MB\/s<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Verdikt: sauber. Jedes Byte jeder der 8 Testdateien, 7,08 GB insgesamt, bis auf rund 16 MB Restplatz gef\u00fcllt, kam exakt so zur\u00fcck, wie es geschrieben wurde: 0 korrupte, 0 ver\u00e4nderte, 0 \u00fcberschriebene Sektoren. Die gemessenen Geschwindigkeiten, 22,78 MB\/s Schreiben und 90,32 MB\/s Lesen, decken sich eng mit dem rohen <code>dd<\/code>-Direct-I\/O-Benchmark aus Schritt 2, eine gute Gegenprobe, dass die Zahlen echt sind und kein Artefakt einer einzelnen Methode.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 5: badblocks, die Musterpr\u00fcfung<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ umount \/mnt\/sdtest\n$ badblocks -wsv \/dev\/sdc\n\nEs wird nach defekten Bl\u00f6cken gesucht (Lesen+Schreiben-Modus)\nVon Block 0 bis 7666687\nEs wird getestet Mit Muster 0xaa: ... 100% erledigt\nEs wird getestet Mit Muster 0x55: ... 100% erledigt\nEs wird getestet Mit Muster 0xff: ... 100% erledigt\nEs wird getestet Mit Muster 0x00: ... 100% erledigt\nDurchgang beendet, 0 defekte Bl\u00f6cke gefunden. (0\/0\/0 Fehler)<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Verdikt: 0 defekte Bl\u00f6cke, \u00fcber alle 4 Standard-Testmuster hinweg (0xAA\/01010101, 0x55\/10101010, 0xFF\/lauter Einsen, 0x00\/lauter Nullen, gew\u00e4hlt, um Stuck-at-0- und Stuck-at-1-Zellfehler ebenso wie Kopplungsfehler zwischen Nachbarbits zu erwischen). Gesamtlaufzeit f\u00fcr den kompletten Schreib-Lese-Vergleichs-Zyklus \u00fcber alle 4 Muster: rund 26 Minuten 47 Sekunden auf dieser 7,31-GiB-Karte, was zur Sch\u00e4tzung von rund 27 Minuten aus den fr\u00fcheren Durchsatzwerten passt, 4-mal Schreibdurchlauf plus Lesedurchlauf bei rund 23,5\/85 MB\/s.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>badblocks -w<\/code> ist ein linearer, positionsbasierter Test, <a href=\"https:\/\/sourceforge.net\/projects\/e2fsprogs\/\" target=\"_blank\" rel=\"noopener\">Teil von e2fsprogs<\/a>. In einem Durchgang schreibt er dasselbe Muster auf jeden logischen Block und liest es zur\u00fcck. Genau das ist der strukturelle Unterschied zu f3write\/f3read, und der Grund, warum badblocks kein Ersatz f\u00fcr f3 bei der Kapazit\u00e4tsbetrugsfrage ist: eine Karte, die Schreibvorg\u00e4nge still auf einen kleineren physischen Bereich zur\u00fcckf\u00fchrt, k\u00f6nnte in einem badblocks-Durchgang trotzdem das \u201erichtige&#8220; Muster zur\u00fcckliefern, weil ohnehin jeder logische Block identischen Inhalt bekommt, das Aliasing bliebe unsichtbar. f3write vermeidet das, indem es positionsabh\u00e4ngige Pseudozufallsdaten schreibt, sodass Wraparound oder Aliasing als Mismatch auff\u00e4llt. badblocks beantwortet daf\u00fcr eine engere, erg\u00e4nzende Frage: versagen bestimmte Bl\u00f6cke oder Zellen dabei, irgendein Muster zuverl\u00e4ssig zu speichern, was der einmalige, pseudozuf\u00e4llige Schreibdurchgang von f3 pro Karte nicht so systematisch pr\u00fcft, kein 0xAA\/0x55\/0xFF\/0x00-Stuck-Bit-Sweep.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 6: flashbench, ein Blick unter die Haube<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ flashbench -f -o \/root\/sdtest\/flashbench.out \/dev\/sdc\n$ cat \/root\/sdtest\/flashbench.out\n\n4MiB    28.5M\/s  28.3M\/s  26.7M\/s  27.6M\/s  23.4M\/s  28M\/s\n2MiB    26.6M\/s  28.2M\/s  26.7M\/s  28.1M\/s  26.8M\/s  28.1M\/s\n1MiB    26.6M\/s  28.8M\/s  26.4M\/s  28.4M\/s  26.6M\/s  27.3M\/s\n512KiB  26.7M\/s  28.2M\/s  26.9M\/s  28.9M\/s  26.8M\/s  28.3M\/s\n256KiB  26.7M\/s  28.2M\/s  26.6M\/s  27.8M\/s  27M\/s    28.8M\/s\n128KiB  26M\/s    28.5M\/s  26.7M\/s  28.3M\/s  26.8M\/s  28.3M\/s\n64KiB   26.9M\/s  28.8M\/s  26.8M\/s  28.3M\/s  26.7M\/s  27.9M\/s\n32KiB   12.6M\/s  12.7M\/s  13.1M\/s  12.6M\/s  12.9M\/s  12.8M\/s\n16KiB   5.78M\/s  5.91M\/s  5.87M\/s  5.82M\/s  5.82M\/s  5.86M\/s<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><a href=\"https:\/\/github.com\/bradfa\/flashbench\" target=\"_blank\" rel=\"noopener\">flashbench<\/a> <code>-f<\/code> (find-fat) liest am Ende der ersten paar Erase-Bl\u00f6cke zunehmend kleinere H\u00e4ppchen und misst die Zeit daf\u00fcr, auf der Suche nach dem Punkt, an dem die Lesegeschwindigkeit pl\u00f6tzlich einbricht. Die Grundidee: das deutet auf die interne Lese- beziehungsweise Page-Granularit\u00e4t 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\u00e4lt 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\u00e4lfte ein (rund 12,6 bis 13,1 MB\/s) und nochmal auf etwa ein F\u00fcnftel bei 16 KiB (rund 5,8 bis 5,9 MB\/s). Ein sauberes, reproduzierbares Signal, 6 Wiederholungen pro Zeile, alle konsistent.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Schlussfolgerung bleibt bewusst vorsichtig formuliert: das ist ein <strong>Performance-Knick bei 64 KiB auf diesem konkreten Reader-Controller-Pfad<\/strong>, konsistent mit einer gr\u00f6\u00dferen internen Leseeinheit oder FTL-Gruppierung, aber keine direkte, verifizierte Messung der tats\u00e4chlichen physischen NAND-Page-Gr\u00f6\u00dfe der Karte (typischerweise 8 bis 16 KiB, daf\u00fcr br\u00e4uchte es Herstellerdokumentation oder eine andere Messmethode). Die Daten sind mit einer bestimmten Erkl\u00e4rung konsistent, sie beweisen sie nicht.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 7: fio, bleibt die Schreibrate konstant?<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>dd<\/code> und f3write liefern Durchschnittswerte. <a href=\"https:\/\/github.com\/axboe\/fio\" target=\"_blank\" rel=\"noopener\">fio<\/a> mit einem Bandbreiten-Log pro Sekunde zeigt dagegen die Form des Schreibvorgangs \u00fcber 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.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ 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\n\nwrite: IOPS=26, BW=26.0MiB\/s (27.3MB\/s)(6144MiB\/236179msec)\nbw (KiB\/s): min=24576, max=27675, per=100.00%, avg=26645.27, stdev=457.38, samples=236<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Blick ins rohe Sekunden-Log lohnt sich, nicht nur die Zusammenfassung: 236 Einsekunden-Stichproben \u00fcber 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\u00f6hter Burst am Anfang, kein Absturz sp\u00e4ter.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">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\u00e4t nicht sichtbar wurde. Es k\u00f6nnte schlicht nicht genug Karte nach dem Puffer \u00fcbrig sein, um einen Absturz zu zeigen, oder, wahrscheinlicher bei einer reinen Class-10\/U1-Budgetkarte ohne A1\/A2- oder \u201eExtreme\/Pro&#8220;-Einstufung, sie implementiert gar kein dynamisches SLC-Caching. So oder so steht das praktische Ergebnis: die Schreibleistung war \u00fcber die gesamte Kapazit\u00e4t konsistent und vorhersagbar, nicht nach vorne verlagert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was f\u00fcr eMMC gilt, aber nicht f\u00fcr SD<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein paar Dinge, die in diesem Testlauf nicht zum Einsatz kamen, aber der Vollst\u00e4ndigkeit halber dazugeh\u00f6ren. <code>mmc extcsd read<\/code> und seine Lebensdauer-Sch\u00e4tzfelder, das N\u00e4chste an einer echten Health-Prozentangabe in diesem Umfeld, sind eine <strong>eMMC<\/strong>-Eigenschaft. Eine wechselbare SD- oder microSD-Karte ist kein eMMC, dieser Pfad stand hier ohnehin nicht zur Verf\u00fcgung, USB-Leser statt nativer MMC-Host. Ein nativer MMC-Host-Slot, etwa der eingebaute SD-Slot eines Laptops, w\u00fcrde dagegen mehr Identit\u00e4tsdaten offenlegen als ein USB-Leser: <code>\/sys\/block\/mmcblkX\/device\/<\/code> mit <code>cid<\/code>, <code>csd<\/code>, <code>scr<\/code>, <code>serial<\/code>, <code>manfid<\/code>, <code>name<\/code>, <code>oemid<\/code>, <code>preferred_erase_size<\/code> und <code>date<\/code>, der <a href=\"https:\/\/www.kernel.org\/doc\/html\/latest\/driver-api\/mmc\/mmc-dev-attrs.html\" target=\"_blank\" rel=\"noopener\">Kernel parst die Identit\u00e4tsregister der Karte direkt in sysfs<\/a>, 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.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und noch ein Missverst\u00e4ndnis vorweg: der <a href=\"https:\/\/www.sdcard.org\/downloads\/formatter\/\" target=\"_blank\" rel=\"noopener\">\u201eSD Card Formatter&#8220; der SD Association<\/a> ist ein Formatierungs- und Reset-Werkzeug, das die werkseitige Partitionierung wiederherstellt, nachdem andere Betriebssysteme sie durcheinandergebracht haben, kein Health-Check-Werkzeug.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Die Reihenfolge, wenn du das nachmachen willst<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Vom schnellsten und harmlosesten zum langsamsten und gr\u00fcndlichsten, ungef\u00e4hr so, wie dieser Test auch abgelaufen ist:<\/p>\n\n\n\n<ol class=\"wp-block-list has-small-font-size\">\n<li><code>lsusb -v<\/code>, <code>udevadm info<\/code>, <code>dmesg<\/code>. Leser und Karte identifizieren, Schreibschutz-Status vorab pr\u00fcfen.<\/li>\n\n\n\n<li><code>smartctl -a -d sat<\/code> und <code>sdparm -a<\/code>. Schneller, harmloser Versuch an Health- und Identit\u00e4tsdaten, meist scheitert das \u00fcber einen USB-Leser, und genau dieses Scheitern ist der Punkt, den man erkl\u00e4ren sollte.<\/li>\n\n\n\n<li><code>f3probe<\/code>. Schneller Kapazit\u00e4tsbetrugs-Check, f\u00fcr 8 GB etwa 2 bis 3 Minuten, minimal destruktiv, stellt die benutzten Bl\u00f6cke standardm\u00e4\u00dfig wieder her.<\/li>\n\n\n\n<li><code>f3write<\/code> plus <code>f3read<\/code>. Der Haupttest, vollst\u00e4ndige Schreib-Lese-Integrit\u00e4tspr\u00fcfung \u00fcber die gesamte Kapazit\u00e4t, braucht ein Dateisystem.<\/li>\n\n\n\n<li><code>badblocks -w<\/code>. Destruktive Vier-Muster-Pr\u00fcfung auf defekte Bl\u00f6cke, Rohger\u00e4t, kein Dateisystem n\u00f6tig. F\u00e4ngt Dinge, die f3 strukturell nicht kann, siehe Schritt 5.<\/li>\n\n\n\n<li><code>dd<\/code> mit <code>oflag<\/code>\/<code>iflag=direct<\/code> und\/oder <code>fio<\/code> mit Bandbreiten-Log. Durchsatzzahlen und, mit fio, die Form der Schreibrate \u00fcber die Zeit.<\/li>\n\n\n\n<li><code>flashbench -f -o DATEI<\/code>. Optional, f\u00fcr die Neugier, Hinweise auf die interne Lese-Granularit\u00e4t.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Fazit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr diese konkrete Karte: gesund, echt, keine Defekte gefunden, bei jedem Test, der \u00fcberhaupt in der Lage gewesen w\u00e4re, welche zu finden. Keine SMART-Werte erreichbar \u00fcber den USB-Leser, echte Kapazit\u00e4t ohne Cache-Trickserei, null Datenkorruption \u00fcber 7,08 GB, null defekte Bl\u00f6cke \u00fcber alle 4 Muster, rund 23 bis 27 MB\/s Schreiben und rund 85 bis 90 MB\/s Lesen, konsistent \u00fcber vier unabh\u00e4ngige Messmethoden, kein SLC-Cache-Absturz \u00fcber die volle Kapazit\u00e4t. Die Karte rei\u00dft ihre aufgedruckte Class-10\/U1-Angabe locker, f\u00fcr U3\/V30 w\u00fcrde es nicht reichen, was sie auch gar nicht behauptet.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der eigentliche Punkt reicht \u00fcber 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2021\/02\/24\/trim-fuer-ssds-im-zfs-pool-unter-linux-aktivieren\/\">TRIM f\u00fcr SSDs im ZFS-Pool unter Linux aktivieren<\/a>, der Gegenpart bei Laufwerken, die SMART und TRIM tats\u00e4chlich haben.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2017\/08\/08\/read-und-write-latency-mit-ioping-messen\/\">ioping: Read- und Write-Latency schnell messen<\/a>, ein weiteres schlankes Bordmittel f\u00fcr Speicher-Benchmarks.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2025\/09\/01\/kabelfaehigkeiten-kabelbrueche-finden-der-usb-kabeltester\/\">USB-Kabeltester: Kabelbr\u00fcche und Datenf\u00e4higkeiten schnell pr\u00fcfen<\/a>, wenn der Verdacht eher beim Kabel als bei der Karte liegt.<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Falls jemand einen zuverl\u00e4ssigeren Weg zur Health-Einsch\u00e4tzung einer SD-Karte unter Linux kennt, immer her damit. Und falls ihr selbst \u00f6fter mit fragw\u00fcrdigen Billigkarten zu tun habt, d\u00fcrft ihr mich zu dem Thema sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>SD-Karten liefern keine SMART-Werte, der Flash-Controller dahinter bleibt eine Blackbox. Was unter Linux trotzdem geht: Verhalten statt Register pr\u00fcfen, mit f3, badblocks, flashbench und fio. Ich habe das an einer echten, entbehrlichen 8-GB-Karte durchgespielt, mit allen Befehlen und der rohen Ausgabe.<\/p>\n","protected":false},"author":1,"featured_media":5321,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[278,13,274,280],"tags":[414,347,36,474,139,244,475,352],"class_list":["post-5320","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hardware-reparatur","category-kernel-error-blog","category-linux-bsd","category-tools-software","tag-benchmark","tag-hardware","tag-linux","tag-microsd","tag-storage","tag-sysadmin","tag-transcend","tag-usb","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5320","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5320"}],"version-history":[{"count":1,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5320\/revisions"}],"predecessor-version":[{"id":5322,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5320\/revisions\/5322"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5321"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5320"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5320"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5320"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5294,"date":"2026-08-17T08:00:00","date_gmt":"2026-08-17T06:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5294"},"modified":"2026-08-17T09:37:50","modified_gmt":"2026-08-17T07:37:50","slug":"encrypted-client-hello-nginx-openssl-4-public-name","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/17\/encrypted-client-hello-nginx-openssl-4-public-name\/","title":{"rendered":"ECH aktivieren: Encrypted Client Hello in nginx mit OpenSSL 4.0, sechs Domains und ein gemeinsamer Deckname"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">Der Servername im TLS-Handshake steht bis heute im Klartext im Netz, unabh\u00e4ngig davon, wie gut die DNS-Anfrage davor verschl\u00fcsselt war. Wer DNS over HTTPS oder DNS over TLS einsetzt, verbirgt den Lookup selbst, aber sobald der Browser die eigentliche Verbindung aufbaut, nennt das Client Hello die besuchte Domain im Server Name Indication Feld offen. Jeder Netzwerkbeobachter zwischen Client und Server sieht damit weiterhin, welche Website gerade aufgerufen wird, egal wie sauber DNS-Ebene und Transportebene sonst verschl\u00fcsselt sind.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-1024x576.png\" alt=\"Illustration zu Encrypted Client Hello (ECH): Ein sichtbarer ClientHelloOuter mit dem gemeinsamen Decknamen ech.kernel-error.de sch\u00fctzt die verschl\u00fcsselten Hostnamen mehrerer Domains auf einem nginx-Server mit OpenSSL 4.0.\" class=\"wp-image-5296\" style=\"aspect-ratio:1.7777576657761325;width:164px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-1024x576.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-300x169.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-768x432.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-1536x864.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4-676x380.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/ech-nginx-openssl4.png 1672w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Encrypted Client Hello schlie\u00dft genau diese L\u00fccke, indem der eigentliche Servername in einen zweiten, verschl\u00fcsselten Client Hello wandert. Der folgende Beitrag beschreibt, wie ECH auf dieser Infrastruktur aktiviert wurde: mit OpenSSL 4.0 als Voraussetzung, nginx als Ports-Build und sechs Domains, die sich einen gemeinsamen Deckname teilen. Alle Befehle, Konfigurationsausschnitte und Testausgaben stammen aus dem produktiven Rollout und sind gegengepr\u00fcft, aktuell laufen alle sechs sauber verifiziert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was Encrypted Client Hello eigentlich verbirgt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein ECH-Verbindungsaufbau enth\u00e4lt zwei Client Hellos statt einem. Das \u00e4u\u00dfere ist wie gewohnt unverschl\u00fcsselt, tr\u00e4gt aber nicht die echte Domain, sondern einen Deckname. Das innere steckt verschl\u00fcsselt im \u00e4u\u00dferen und enth\u00e4lt die tats\u00e4chlich angefragte Domain:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ClientHelloOuter: unverschl\u00fcsselt sichtbar, enth\u00e4lt public_name\nClientHelloInner: verschl\u00fcsselt, enth\u00e4lt die tats\u00e4chlich besuchte Domain<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Server entschl\u00fcsselt das innere Client Hello mit einem Schl\u00fcssel, den er vorher \u00fcber DNS ver\u00f6ffentlicht hat, w\u00e4hlt anhand der darin enthaltenen echten SNI den passenden vHost aus und antwortet als w\u00e4re nie ein Deckname im Spiel gewesen. F\u00fcr den Browser l\u00e4uft das komplett automatisch ab, sobald der Server ECH per DNS anbietet.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Voraussetzungen, bevor es losgeht<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li>OpenSSL 4.0 oder neuer (ECH ist seit dem stabilen 4.0.0-Release vom 14. April 2026 Bestandteil, im Alpha-\/Dev-Branch bereits ab 10.\/11. M\u00e4rz 2026 verf\u00fcgbar), alternativ BoringSSL<\/li>\n\n\n\n<li>nginx 1.29.4 oder neuer, mit der Direktive <code>ssl_ech_file<\/code> (hier im Einsatz: 1.30.4)<\/li>\n\n\n\n<li>eine DNSSEC-signierte Zone ist sinnvoll, aber keine normative ECH-Voraussetzung, dazu weiter unten mehr<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Auch der erste Punkt ist keine Formalit\u00e4t. Das <a href=\"https:\/\/redmine.lighttpd.net\/projects\/lighttpd\/wiki\/TLS_ECH\" target=\"_blank\" rel=\"noopener\">Lighttpd-Wiki f\u00fchrt ECH aktuell weiterhin als EXPERIMENTAL<\/a> und nennt dieselbe Voraussetzung, OpenSSL 4.0 oder BoringSSL. ECH ist \u00fcber die Server-Landschaft hinweg gesehen noch frisch, entsprechend lohnt vor dem eigenen Rollout ein Blick auf die aktuell installierte Softwareversion, nicht auf das, was vor einem halben Jahr noch galt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Umstieg auf OpenSSL 4.0 als Ausgangspunkt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">nginx l\u00e4uft in dieser Jail als Ports-Build, nicht als Repo-Paket, weil das Paket Brotli und headers_more deaktiviert ausliefert. Damit l\u00e4sst sich die verwendete OpenSSL-Hauptversion \u00fcber die SSL-Flavor-Einstellung in <code>\/etc\/make.conf<\/code> steuern und nginx anschlie\u00dfend dagegen neu bauen:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">jexec nginx pkg unlock -y nginx\n# \/etc\/make.conf: DEFAULT_VERSIONS+=ssl=openssl40 (vorher openssl35)\njexec nginx make -C \/usr\/ports\/www\/nginx missing\njexec nginx make -C \/usr\/ports\/www\/nginx -DBATCH build\njexec nginx make -C \/usr\/ports\/www\/nginx -DBATCH deinstall install\njexec nginx pkg lock -y nginx\njexec nginx service nginx restart<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein <code>reload<\/code> reicht an dieser Stelle ausdr\u00fccklich nicht. Die neuen Shared Libraries werden erst beim vollst\u00e4ndigen Neustart der Worker-Prozesse eingebunden, ein reload beh\u00e4lt die alten libssl-Bindings der laufenden Prozesse bei. Die neue Version zeigt sich danach direkt im Server-Build:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ nginx -V\nbuilt with OpenSSL 4.0.1 9 Jun 2026<\/pre>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">public_name ist die eigentliche Entscheidung<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>ssl_ech_file<\/code> ist keine Bastell\u00f6sung und kein Fork, sondern eine echte, in nginx-Mainline gemergte Direktive, sichtbar direkt im Quellcode unter <code>src\/http\/modules\/ngx_http_ssl_module.c<\/code> und <code>src\/event\/ngx_event_openssl.c<\/code>. Technisch nutzt sie die <code>OSSL_ECHSTORE<\/code>-API, die mit OpenSSL 4.0 dazugekommen ist.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der eigentlich entscheidende Wert ist <code>public_name<\/code>, der Name, der im unverschl\u00fcsselten ClientHelloOuter sichtbar bleibt. Ein Netzwerkbeobachter sieht ihn ohnehin, ECH hin oder her. Setzt man ihn auf die eigene, echte Domain, bringt die ganze \u00dcbung keinerlei Verschleierung: der Beobachter sieht exakt den Namen, den er auch ohne ECH gesehen h\u00e4tte, nur mit einem zus\u00e4tzlichen Handshake-Schritt drumherum.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Echten Schutz gibt es nur mit einem gemeinsamen <code>public_name<\/code> \u00fcber mehrere unabh\u00e4ngige Domains hinweg, dem Anonymit\u00e4tsset-Prinzip. Genau so macht es Cloudflare mit <code>cloudflare-ech.com<\/code> als Deckname f\u00fcr Millionen Kundendomains: wer nur sieht, dass jemand eine Verbindung zu <code>cloudflare-ech.com<\/code> aufbaut, wei\u00df damit nichts \u00fcber die konkret besuchte Seite unter den Millionen dahinter.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein gemeinsamer <code>public_name<\/code> reicht daf\u00fcr allein aber noch nicht. Im ClientHelloOuter steht neben dem <code>public_name<\/code> auch die <code>config_id<\/code> unverschl\u00fcsselt, ein einzelnes Byte, das die verwendete ECHConfig identifiziert. Nutzt jede Domain einen eigenen ECH-Schl\u00fcssel, hat jede Domain automatisch auch eine eigene <code>config_id<\/code>, und ein Beobachter kann die Domains trotz identischem Deckname allein anhand dieses Bytes unterscheiden. <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9849.html\" target=\"_blank\" rel=\"noopener\">RFC 9849<\/a> bringt es auf den Punkt: verwendet ein Server f\u00fcr verschiedene Servernamen unterschiedliche ECHConfig-Werte, hat jedes Anonymit\u00e4tsset effektiv Gr\u00f6\u00dfe 1, also gar keinen Effekt. Die Konsequenz f\u00fcr den eigenen Aufbau: alle teilnehmenden Domains m\u00fcssen denselben Schl\u00fcssel und dieselbe ECHConfig nutzen, nicht nur denselben Deckname.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Diese eine nginx-Instanz hostet bereits rund zwanzig unabh\u00e4ngige Domains auf derselben IP, darunter dieser Blog, eine Therapiepraxis-Website, eine Fotogalerie, private Familienseiten, Webmail und ein URL-Shortener. Die Voraussetzung f\u00fcr ein eigenes, kleines Anonymit\u00e4tsset war also schon da, es musste nur genutzt werden.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Umgesetzt wurde ein dedizierter Deckname, <code>ech.kernel-error.de<\/code>, ohne eigenen Inhalt. Sechs Domains teilen sich einen gemeinsamen ECH-Schl\u00fcssel und damit dieselbe ECHConfig, denselben <code>public_name<\/code> und dieselbe <code>config_id<\/code>: <code>www.kernel-error.de<\/code>, die Apex-Domain <code>kernel-error.de<\/code>, <code>webmail.kernel-error.de<\/code>, <code>bilder.kernel-error.de<\/code> und <code>kernel-error.org<\/code> als eigene, ebenfalls DNSSEC-signierte Zone. Die TLS-Zertifikats-Schl\u00fcssel der einzelnen Domains bleiben davon unber\u00fchrt getrennt, das ist ein anderes Schl\u00fcsselpaar als der ECH-HPKE-Schl\u00fcssel und f\u00fcr die Anonymit\u00e4tsset-Eigenschaft ohne Belang. <code>stammbaum.kernel-error.de<\/code>, ein CNAME auf <code>www<\/code>, erbt die ECH-Konfiguration automatisch per DNS-CNAME-Chasing, ganz ohne eigenen Eintrag.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schl\u00fcssel erzeugen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr die Schl\u00fcsselerzeugung braucht es das Port-OpenSSL unter <code>\/usr\/local\/bin\/openssl<\/code>, nicht das Base-System-OpenSSL von FreeBSD, dem fehlt der <code>ech<\/code>-Subcommand komplett:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">openssl ech -public_name ech.kernel-error.de -out \/usr\/local\/etc\/nginx\/ssl\/ech\/shared.kernel-error.pem<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das Ergebnis ist eine PEM-Datei mit zwei Bl\u00f6cken, dem privaten Schl\u00fcssel und der \u00f6ffentlichen ECH-Konfiguration:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n-----BEGIN ECHCONFIG-----\nAEb+DQBC8AAgACDKMT4bTYtGnUXKvOkeuYEWDaByMjijSRINkLQxIEHQQQAEAAEAAQATZWNoLmtlcm5lbC1lcnJvci5kZQAA\n-----END ECHCONFIG-----<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Base64-Block zwischen den ECHCONFIG-Markern ist exakt der Wert, der sp\u00e4ter als <code>ech=<\/code>-Parameter in den DNS-Eintrag wandert. Das PEM-Format selbst folgt keinem nginx- oder OpenSSL-eigenen Standard, sondern ist mittlerweile als <a href=\"https:\/\/www.rfc-editor.org\/info\/rfc9934\/\" target=\"_blank\" rel=\"noopener\">RFC 9934<\/a> festgeschrieben, hervorgegangen aus dem Internet-Draft <code>draft-farrell-tls-pemesni<\/code>, den auch das <a href=\"https:\/\/github.com\/defo-project\/ech-dev-utils\" target=\"_blank\" rel=\"noopener\">DEfO-Projekt in seinen ech-dev-utils dokumentiert<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Ein gemeinsamer Schl\u00fcsselspeicher f\u00fcr alle Domains<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei mehreren Domains mit gemeinsamem <code>public_name<\/code> geh\u00f6rt <code>ssl_ech_file<\/code> auf die <code>http{}<\/code>-Ebene in der <code>nginx.conf<\/code>, noch vor die vHost-Includes, nicht in die einzelnen vHost-Dateien:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ssl_ech_file \/usr\/local\/etc\/nginx\/ssl\/ech\/shared.kernel-error.pem;<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Grund liegt in der Reihenfolge des Handshakes. Die ECH-Entschl\u00fcsselung passiert, bevor nginx anhand der noch verschl\u00fcsselten echten SNI \u00fcberhaupt den passenden vHost ausw\u00e4hlen kann. In diesem Moment entscheiden die \u00e4u\u00dfere SNI, also der gemeinsame <code>public_name<\/code>, und die vom Client mitgeschickte <code>config_id<\/code>, welcher Schl\u00fcssel \u00fcberhaupt zum Entschl\u00fcsseln probiert wird. Ein Store auf <code>http{}<\/code>-Ebene stellt sicher, dass genau dieser eine, gemeinsame Schl\u00fcssel unabh\u00e4ngig vom initial per SNI getroffenen Serverblock verf\u00fcgbar ist, egal welche der sechs Domains die Verbindung eigentlich betrifft.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Ein eigenes Zertifikat f\u00fcr den Deckname<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Deckname selbst braucht ein eigenes Zertifikat mit ausschlie\u00dflich diesem einen SAN-Eintrag, kein Wildcard-Zertifikat, das nebenbei auch die echten Domains auflistet. Sonst verr\u00e4t schon das \u00e4u\u00dfere Zertifikat die Domain-Familie, selbst wenn die konkrete Domain im Handshake verschl\u00fcsselt bleibt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">certbot certonly --nginx -d ech.kernel-error.de --key-type ecdsa<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">X509v3 Subject Alternative Name:\n    DNS:ech.kernel-error.de<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der nginx-vHost f\u00fcr den Deckname bleibt inhaltsleer und erbt den Schl\u00fcsselspeicher automatisch von der <code>http{}<\/code>-Ebene:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">server {\n    listen      [::]:443 ssl;\n    http2 on;\n    server_name ech.kernel-error.de;\n    ssl_certificate     \/usr\/local\/etc\/letsencrypt\/live\/ech.kernel-error.de\/fullchain.pem;\n    ssl_certificate_key \/usr\/local\/etc\/letsencrypt\/live\/ech.kernel-error.de\/privkey.pem;\n    include              \/usr\/local\/etc\/nginx\/tls-default.conf;\n    location \/ { return 204; }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der DNS-Eintrag<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die ECH-Konfiguration wird nicht als eigener Record-Typ verteilt, sondern als zus\u00e4tzlicher <code>ech=<\/code>-SvcParam an der ohnehin schon vorhandenen HTTPS-Resource-Record angeh\u00e4ngt, und zwar identisch auf allen sechs teilnehmenden Domains. Der Bootstrap-Mechanismus daf\u00fcr steht in <a href=\"https:\/\/www.rfc-editor.org\/info\/rfc9848\/\" target=\"_blank\" rel=\"noopener\">RFC 9848<\/a>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">www  IN HTTPS  1 www.kernel-error.de. ( alpn=\"h3,h2\" ipv4hint=148.251.30.200\n     ipv6hint=2a01:4f8:262:4716::443\n     ech=\"AEb+DQBC8AAgACDKMT4bTYtGnUXKvOkeuYEWDaByMjijSRINkLQxIEHQQQAEAAEAAQATZWNoLmtlcm5lbC1lcnJvci5kZQAA\" )<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine DNSSEC-signierte Zone ist daf\u00fcr nicht normativ vorgeschrieben, aber sinnvoll: ohne Signatur kann ein Man in the Middle den <code>ech=<\/code>-Parameter einfach aus der Antwort entfernen, ein klassischer Downgrade auf Klartext-SNI, den ein validierender Resolver mit DNSSEC erkennt. Vor jeder Form von Downgrade sch\u00fctzt das trotzdem nicht: ein Angreifer kann die gesamte HTTPS\/SVCB-Aufl\u00f6sung unterdr\u00fccken statt sie zu manipulieren, und gegen dieses Denial hilft DNSSEC allein nicht.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Verifikation nach dem Rollout<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der naheliegende erste Test ist <code>openssl s_client<\/code> mit dem passenden ECH-Config-List-Wert. Ohne ein gesetztes CA-Bundle lassen sich ein echter ECH-Fehler und ein simpler Zertifikatsfehler dabei nicht unterscheiden, <code>-CAfile<\/code> ist deshalb Pflicht, nicht optional:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">openssl s_client -connect 148.251.30.200:443 -servername www.kernel-error.de -ech_config_list \"AEb+DQBC8AAgACDKMT4bTYtGnUXKvOkeuYEWDaByMjijSRINkLQxIEHQQQAEAAEAAQATZWNoLmtlcm5lbC1lcnJvci5kZQAA\" -CAfile \/etc\/ssl\/cert.pem<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Verify return code: 0 (ok)\nECH: success: 1\nECH: inner: www.kernel-error.de\nECH: outer: ech.kernel-error.de<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Derselbe Test l\u00e4uft identisch, mit demselben Base64-Wert und demselben Erfolg, auch f\u00fcr <code>kernel-error.de<\/code>, <code>webmail.kernel-error.de<\/code>, <code>bilder.kernel-error.de<\/code> und <code>kernel-error.org<\/code>, alle f\u00fcnf gegen dieselbe ECHConfig verifiziert.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr eine unabh\u00e4ngige Zweitmeinung von au\u00dfen eignet sich das externe Tool <a href=\"https:\/\/github.com\/Darkroom4364\/echcheck\" target=\"_blank\" rel=\"noopener\">echcheck<\/a>. Es pr\u00fcft nicht nur, ob der Handshake gelingt, sondern auch DNS-Ver\u00f6ffentlichung, verwendete Kryptografie, das innere Zertifikat und die Retry-Konfiguration einzeln:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ECHConfig aus dem DNS: \u2713\nfinale ECH-Version 0xfe0d: \u2713\nX25519, HKDF-SHA256, AES-128-GCM: \u2713\n\u00f6ffentlicher Name ech.kernel-error.de: \u2713\nechter ECH-Handshake: Accepted (TLS 1.3): \u2713\ng\u00fcltiges inneres Zertifikat: \u2713\ng\u00fcltige Retry-Konfiguration, Wiederholung erfolgreich: \u2713\nSNI Leakage: \u2713 (Einzel-SAN-Zertifikat f\u00fcr den Deckname)\nCertificate (outer): \u2713 (Einzel-SAN-Zertifikat f\u00fcr den Deckname)<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">In allen neun gepr\u00fcften Kategorien kommt ein Haken zur\u00fcck, auch bei den beiden Punkten, die erst nach dem Einzel-SAN-Zertifikat f\u00fcr den Deckname sauber wurden. Ein einmaliger CLI-Test sagt aber nichts dar\u00fcber, ob echte Clients die Funktion sp\u00e4ter auch tats\u00e4chlich nutzen. Daf\u00fcr lohnt sich ein erweitertes Log-Format, das den ECH-Status jeder einzelnen Verbindung mitschreibt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">log_format tls_extended\n  '$remote_addr - $remote_user [$time_local] '\n  '\"$request\" $status $body_bytes_sent '\n  '\"$http_referer\" \"$http_user_agent\" '\n  'proto=$ssl_protocol cipher=$ssl_cipher kx_curve=$ssl_curve '\n  'alpn=$ssl_alpn_protocol reused=$ssl_session_reused sni=$ssl_server_name '\n  'ech_status=$ssl_ech_status ech_outer_sni=$ssl_ech_outer_server_name';<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im laufenden Traffic zeigen sich damit bereits echte <code>ech_status=SUCCESS<\/code>-Verbindungen, neben <code>NOT_TRIED<\/code> f\u00fcr Clients ohne ECH-Unterst\u00fctzung und <code>GREASE<\/code> f\u00fcr Clients, die absichtlich eine leere ECH-Erweiterung mitschicken, um Middleboxen an das Feld zu gew\u00f6hnen. Genau dieser Log-Eintrag ist der eigentliche Erfolgsnachweis, nicht der einzelne CLI-Test.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">W\u00e4hrend dieser TLS-Arbeiten am selben Server ist au\u00dferdem noch ein kleinerer Nebenfund aufgetaucht: ein externer TLS-Scan meldete, dass der Server unter TLS 1.2 weiterhin SHA-224 als Hash-Funktion f\u00fcr die Signatur beim Schl\u00fcsselaustausch akzeptierte, obwohl die konfigurierten Cipher-Suiten ohnehin ausschlie\u00dflich ECDSA nutzen. <a href=\"https:\/\/www.rfc-editor.org\/info\/rfc9847\/\" target=\"_blank\" rel=\"noopener\">RFC 9847<\/a> stuft SHA-224 in der TLS-HashAlgorithm-Registry inzwischen als \u201eDiscouraged\u201c ein, w\u00e4hrend SHA-256, SHA-384 und SHA-512 dort als \u201eRecommended\u201c gef\u00fchrt werden. Ein guter Grund, es trotzdem weiter anzubieten, findet sich aber nicht, wenn SHA-256 und h\u00f6her ohnehin zur Verf\u00fcgung stehen. Unter TLS 1.3 stellt sich die Frage gar nicht erst, dort ist f\u00fcr SHA-224 kein Signature Scheme registriert, die Hash-Funktion l\u00e4sst sich strukturell nicht w\u00e4hlen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Cipher-Suite und Signaturalgorithmus f\u00fcrs Handshake sind zwei getrennte Stellschrauben, eine sauber konfigurierte Cipher-Suite sagt nichts \u00fcber die andere aus. Ohne explizite Vorgabe verwendet OpenSSL seine Default-Liste der SignatureAlgorithms, und die enth\u00e4lt SHA-224 weiterhin aus Kompatibilit\u00e4tsgr\u00fcnden:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">openssl s_client -connect 148.251.30.200:443 -servername www.kernel-error.de -tls1_2 -sigalgs \"ECDSA+SHA224\"<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Peer signing digest: SHA224\nNew, TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Fix ist eine Zeile in der gemeinsamen TLS-Konfigurationsdatei, damit gilt sie automatisch f\u00fcr alle vHosts, nur ECDSA-Varianten gelistet, passend zum bestehenden reinen ECDSA-Cipher-Setup:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"nginx\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ssl_conf_command SignatureAlgorithms ECDSA+SHA384:ECDSA+SHA256:ECDSA+SHA512;<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Auch hier reichte ein reload nicht, erst ein vollst\u00e4ndiger Neustart \u00fcbernahm die ge\u00e4nderte Liste, aus demselben Grund wie beim OpenSSL-Umstieg weiter oben. Danach lehnt der Server SHA-224 ab, SHA-256 funktioniert unver\u00e4ndert:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">SHA224: error:...:SSL routines:ssl3_read_bytes:tls alert handshake failure:...\nSHA256: Peer signing digest: SHA256, New, TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305<\/pre>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Fazit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">OpenSSL 4.0 war die technische Voraussetzung f\u00fcr diesen Umstieg, die eigentliche Entscheidung fiel aber woanders. Ein ECH-Setup mit dem eigenen Domainnamen als <code>public_name<\/code> w\u00e4re in wenigen Minuten fertig gewesen und h\u00e4tte am Ende nichts verschleiert. Der Wert liegt allein in einem gemeinsamen Deckname \u00fcber mehrere unabh\u00e4ngige Domains, und den gab es hier schon, bevor \u00fcberhaupt eine Zeile Konfiguration geschrieben wurde.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Firefox unterst\u00fctzt ECH seit Version 118, standardm\u00e4\u00dfig aktiv ist es seit Version 119, jeweils automatisch sobald ein Server es per DNS anbietet. Bei Chromium-basierten Browsern l\u00e4sst sich keine ebenso feste Versionsgrenze nennen, der Rollout l\u00e4uft dort gestaffelt \u00fcber Channel und Policy. Der Nutzer muss in beiden F\u00e4llen nichts einstellen, die Aushandlung l\u00e4uft beim ersten Verbindungsaufbau im Hintergrund. Wie schnell sich diese Versionsst\u00e4nde weiterentwickeln, l\u00e4sst sich hier nicht dauerhaft verl\u00e4sslich festschreiben, ein aktueller Blick in die jeweilige Browser-Dokumentation lohnt bei Zweifeln also immer.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wer selbst mehrere unabh\u00e4ngige Domains auf derselben IP betreibt, hat die Grundvoraussetzung f\u00fcr ein eigenes Anonymit\u00e4tsset damit h\u00e4ufig schon, ganz ohne Cloudflare oder einen anderen gro\u00dfen Anbieter dazwischen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/03\/07\/post-quantum-tls-fuer-nginx-x25519mlkem768-auf-freebsd-15\/\">Post-Quantum TLS f\u00fcr Nginx: X25519MLKEM768 auf FreeBSD 15 konfigurieren<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/07\/26\/x25519mlkem768-zerlegt-post-quantum-handshake-tls\/\">X25519MLKEM768 zerlegt: was in einem Post-Quantum-Handshake wirklich steckt<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2025\/09\/30\/https-rr-svcb-moderne-dns-records-schnellere-sicherere-verbindungen\/\">HTTPS RR und SVCB: Moderne DNS-Records f\u00fcr schnellere und sicherere Verbindungen<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2013\/08\/10\/dnssec-und-dns-based-authentication-of-named-entities-dane\/\">DNSSEC und DANE: TLS-Zertifikate mit TLSA-Records absichern<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei Fragen zum Setup, zur Wahl des Decknamens oder zum eigenen Anonymit\u00e4tsset d\u00fcrft ihr mich sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Der Servername im TLS-Handshake bleibt f\u00fcr jeden Netzwerkbeobachter sichtbar, selbst wenn DNS-Anfragen l\u00e4ngst verschl\u00fcsselt laufen. Mit OpenSSL 4.0 und einem gemeinsamen Deckname \u00fcber mehrere unabh\u00e4ngige Domains hinweg l\u00e4sst sich das schlie\u00dfen, wenn eine Entscheidung nicht falsch getroffen wird: der \u00f6ffentliche Name.<\/p>\n","protected":false},"author":1,"featured_media":5296,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[272,13,273,276],"tags":[79,74,365,28,384,36,23,228,378,372,65],"class_list":["post-5294","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it-security","category-kernel-error-blog","category-netzwerke-protokolle","category-self-hosting-infrastruktur","tag-dns","tag-dnssec","tag-ech","tag-freebsd","tag-hardening","tag-linux","tag-nginx","tag-openssl","tag-privacy","tag-selfhosted","tag-tls","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5294","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5294"}],"version-history":[{"count":4,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5294\/revisions"}],"predecessor-version":[{"id":5312,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5294\/revisions\/5312"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5296"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5294"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5294"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5294"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5271,"date":"2026-08-16T21:00:00","date_gmt":"2026-08-16T19:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5271"},"modified":"2026-08-16T12:39:52","modified_gmt":"2026-08-16T10:39:52","slug":"openpgp-karte-eingerichtet-curve-25519-scheitert-am-kartenlimit-die-unterschluessel-landen-trotzdem-in-hardware","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/16\/openpgp-karte-eingerichtet-curve-25519-scheitert-am-kartenlimit-die-unterschluessel-landen-trotzdem-in-hardware\/","title":{"rendered":"OpenPGP-Karte eingerichtet: Curve 25519 scheitert am Kartenlimit, die Unterschl\u00fcssel landen trotzdem in Hardware"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">Moin. Der letzte Beitrag endete mit einem Satz, der mir seitdem im Kopf herumspukt: \u201eNichts hier braucht eine Smartcard, und nichts hier braucht root.&#8220; Das stand in der Restrisiken-Liste des <a href=\"https:\/\/www.kernel-error.de\/2026\/08\/02\/openpgp-schluessel-ed25519-bauen-gnupg-haertung-praeferenzen\/\">Ed25519-Beitrags<\/a>, gleich neben dem Eingest\u00e4ndnis, dass eine Smartcard das gr\u00f6\u00dfte verbleibende Risiko zwar verkleinern, aber nicht aufl\u00f6sen w\u00fcrde. Genau diese L\u00fccke wollte ich mir ansehen: taugt eine OpenPGP-Karte daf\u00fcr, den Prim\u00e4rschl\u00fcssel offline vorzuhalten, statt ihn nur auf einem Cold-Storage-Stick liegen zu lassen?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-1024x576.png\" alt=\"OpenPGP-Smartcard mit drei hardwaregebundenen Unterschl\u00fcsseln f\u00fcr Signatur, Verschl\u00fcsselung und Authentifizierung; der Prim\u00e4rschl\u00fcssel bleibt separat offline gespeichert.\" class=\"wp-image-5274\" style=\"aspect-ratio:1.7777576657761325;width:126px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-1024x576.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-300x169.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-768x432.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-1536x864.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card-676x380.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-card.png 1672w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die kurze Antwort: nein, technisch geht das nicht. Eine OpenPGP-Karte kennt genau drei Schl\u00fcsselslots, Signatur, Verschl\u00fcsselung und Authentifizierung. F\u00fcr den Zertifizierungsschl\u00fcssel, also den, der die Unterschl\u00fcssel \u00fcberhaupt erst beglaubigt, gibt es keinen Slot. Der Prim\u00e4rschl\u00fcssel mit der F\u00e4higkeit <code>[C]<\/code> passt schlicht nicht drauf.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die l\u00e4ngere Antwort ist der eigentliche Grund f\u00fcr diesen Beitrag. Die Karte macht etwas anderes, mindestens genauso viel wert. Sie nimmt die drei Unterschl\u00fcssel auf, die du im Alltag tats\u00e4chlich benutzt, und macht sie hardwaregebunden. Nicht kopierbar, nicht extrahierbar, w\u00e4hrend der Prim\u00e4rschl\u00fcssel unver\u00e4ndert offline liegen bleibt. Diese Erwartungskorrektur geh\u00f6rt an den Anfang und nicht als Fu\u00dfnote irgendwo im Text, weil sie der Grund ist, warum dieser Beitrag neben dem Ed25519-Beitrag \u00fcberhaupt eine eigene Daseinsberechtigung hat.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"1024\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung-768x1024.jpg\" alt=\"Wei\u00dfer Versandumschlag mit Pinguin-Sticker, darunter die Chipkarte mit sichtbarem Goldchip.\" class=\"wp-image-5260\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung-768x1024.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung-225x300.jpg 225w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung-676x901.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/verpackung.jpg 1125w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/a><figcaption class=\"wp-element-caption\">So kommt eine OpenPGP-Karte an, hier eine blanko Smart Card V3.4 vom FLOSS-Shop. Der Chip schimmert schon durch den Umschlag.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Das Testsetup<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bevor irgendein echter Schl\u00fcssel in die N\u00e4he der Karte kommt, brauchte es eine Umgebung, in der ich ohne Risiko herumprobieren kann. Die Eckdaten:<\/p>\n\n\n\n<figure class=\"wp-block-table has-small-font-size\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Karte<\/strong><\/td><td>OpenPGP Smart Card V3.4, ID-1 blanko, <a href=\"https:\/\/www.floss-shop.de\/\" target=\"_blank\" rel=\"noopener\">FLOSS-Shop<\/a>, Serie 0000D961, Chip von ZeitControl<\/td><\/tr><tr><td><strong>Reader<\/strong><\/td><td>REINER SCT cyberJack pinpad(a), Baujahr 2008<\/td><\/tr><tr><td><strong>Demo-Schl\u00fcssel<\/strong><\/td><td>isoliertes GNUPGHOME, UID \u201eOpenPGP Card Demo card-demo@example.invalid&#8220;<\/td><\/tr><tr><td><strong>Warum isoliert<\/strong><\/td><td>Screenshots d\u00fcrfen ungeschw\u00e4rzt raus, und ich wollte mit factory-reset und wiederholten Fehlversuchen experimentieren k\u00f6nnen, ohne das eigentliche Setup zu gef\u00e4hrden<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Reader ist derselbe cyberJack pinpad(a), den ich mir vor einer Weile aus Neugier auseinandergenommen hatte. F\u00fcr diesen Beitrag z\u00e4hlt an ihm eigentlich nur eine Eigenschaft: er hat eine eigene Zifferntastatur samt Display, PINs gehen also nie \u00fcber die Tastatur deines Rechners und damit auch nie durch dessen Speicher.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Reader-Setup: zwei Fallstricke, bevor \u00fcberhaupt eine Karte drinsteckt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der erste Stolperstein kam, bevor ich \u00fcberhaupt eine Karte eingesteckt hatte. <code>scdaemon<\/code>, der Smartcard-Daemon von <a href=\"https:\/\/www.gnupg.org\/\" target=\"_blank\" rel=\"noopener\">GnuPG<\/a>, versucht standardm\u00e4\u00dfig, den internen CCID-Treiber zu benutzen. Der cyberJack spricht aber ein eigenes Vendor-Protokoll \u00fcber PC-SC, kein CCID, und scdaemon quittiert das mit einem schlichten \u201eNo such device&#8220;. Die L\u00f6sung steht in der Konfigurationsdatei, nicht auf der Kommandozeile:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">disable-ccid<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Zeile geh\u00f6rt in <code>scdaemon.conf<\/code>. Danach findet gpg den Reader \u00fcber den PC-SC-Layer, und pcscd \u00fcbernimmt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der zweite Stolperstein trat immer wieder auf, sobald ich scdaemon w\u00e4hrend der Arbeit neu gestartet oder abgeschossen habe. Der Reader blieb dann als \u201ebusy&#8220; h\u00e4ngen, pcscd quittierte jeden Zugriffsversuch mit <code>pcsc_connect: sharing violation<\/code>. Ein <code>gpgconf --kill scdaemon<\/code> allein hat das nie behoben. Zuverl\u00e4ssig geholfen hat nur ein Neustart des Dienstes selbst:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">systemctl restart pcscd<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Beide Punkte sind auf jeden anderen PC-SC-Reader mit eigenem Vendor-Protokoll \u00fcbertragbar, nicht nur auf den cyberJack, deswegen stehen sie hier als eigener Abschnitt.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"1024\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto-768x1024.jpg\" alt=\"REINER SCT cyberJack pinpad(a) Kartenleser mit Zifferntastatur, LEDs und eingesteckter OpenPGP-Karte.\" class=\"wp-image-5261\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto-768x1024.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto-225x300.jpg 225w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto-676x901.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pinpad-foto.jpg 1125w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><\/a><figcaption class=\"wp-element-caption\">Der cyberJack pinpad(a) mit eingesteckter Karte. Alles, was danach an PIN eingegeben wird, l\u00e4uft \u00fcber die Tasten hier und nicht \u00fcber die Rechnertastatur.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Erster Schritt: die Standard-PINs \u00e4ndern<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Karte liegt eine kleine Karteikarte bei, und die erkl\u00e4rt in aller Ruhe, warum der allererste Schritt kein optionaler ist:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte-1024x768.jpg\" alt=\"Beiliegende Karteikarte mit Standard-PIN 123456 und Admin-PIN 12345678 sowie dem Hinweis auf die Sperrfolgen.\" class=\"wp-image-5262\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte-1024x768.jpg 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte-300x225.jpg 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte-768x576.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte-676x507.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/pin-karte.jpg 1500w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Standard-PIN 123456, Admin-PIN 12345678, bei jeder blanko Karte identisch. Drei falsche PIN-Versuche sperren die Karte, drei falsche Admin-PIN-Versuche l\u00f6schen die Daten.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Standard-PIN und Admin-PIN sind bei jeder blanko Karte identisch, stehen also \u00f6ffentlich in jedem Handbuch. Wer sie nicht \u00e4ndert, hat effektiv gar keinen PIN-Schutz. In <code>gpg --card-edit<\/code> geht das \u00fcber <code>admin<\/code>, gefolgt von <code>passwd<\/code>:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"769\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog-1024x769.png\" alt=\"Pinentry-Dialog mit dem Hinweis, den Admin-PIN und den neuen Admin-PIN \u00fcber das Pinpad des Kartenlesers einzugeben.\" class=\"wp-image-5264\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog-1024x769.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog-300x225.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog-768x577.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog-676x508.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/admin-pin-dialog.png 1106w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Pinentry zeigt nur den Hinweis, das Pinpad des Readers zu benutzen. Der Admin-PIN selbst taucht auf dem Bildschirm nie auf.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"779\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog-1024x779.png\" alt=\"Pinentry-Dialog mit dem Hinweis, den PIN und den neuen PIN \u00fcber das Pinpad des Kartenlesers einzugeben.\" class=\"wp-image-5265\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog-1024x779.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog-300x228.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog-768x584.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog-676x514.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/user-pin-dialog.png 1098w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Derselbe Dialog f\u00fcr den normalen PIN. Der Retry-Z\u00e4hler danach steht bei drei von drei, unver\u00e4ndert gegen\u00fcber vorher.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wichtig an beiden Dialogen ist derselbe Punkt: der eigentliche PIN geht nie \u00fcber den Rechner, weder alt noch neu. Pinentry zeigt nur einen Hinweis an, die Eingabe passiert komplett am Pinpad. Genau daf\u00fcr gibt man das Geld f\u00fcr einen Reader mit eigener Tastatur aus, sonst k\u00f6nnte ein kompromittierter Rechner den PIN einfach mitschneiden.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Das erste Kartenlimit: Curve 25519 scheitert<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im jungfr\u00e4ulichen Zustand meldet die Karte f\u00fcr alle drei Slots <code>rsa2048<\/code>, keine Schl\u00fcssel gesetzt:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-vorher.png\"><img loading=\"lazy\" decoding=\"async\" width=\"624\" height=\"394\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-vorher.png\" alt=\"Terminalausgabe von gpg --card-status im jungfr\u00e4ulichen Zustand, Schl\u00fcsselattribute \u00fcberall rsa2048, keine Schl\u00fcssel gesetzt.\" class=\"wp-image-5263\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-vorher.png 624w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-vorher-300x189.png 300w\" sizes=\"auto, (max-width: 624px) 100vw, 624px\" \/><\/a><figcaption class=\"wp-element-caption\">Der Zustand direkt nach dem Auspacken. rsa2048 \u00fcberall, keine Schl\u00fcssel, die Referenz f\u00fcr alles, was danach kommt.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Weil der Ed25519-Beitrag genau davon handelte, war meine erste Idee naheliegend: die Kartenattribute per <code>key-attr<\/code> auf Curve 25519 umstellen, dieselbe Kurve wie beim Prim\u00e4rschl\u00fcssel. Curve 25519 steht im Men\u00fc sogar als Default. Die Karte lehnt sie trotzdem ab, mit Statuswort <code>6A80<\/code>, sowohl f\u00fcr den Signatur- als auch f\u00fcr den Verschl\u00fcsselungsslot:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"778\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler-1024x778.png\" alt=\"Terminalausgabe von gpg card-edit key-attr, Auswahl von Curve 25519 als Default endet mit Card error.\" class=\"wp-image-5266\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler-1024x778.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler-300x228.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler-768x584.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler-676x514.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-ed25519-fehler.png 1105w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">key-attr bietet Curve 25519 als Default an, aber die Karte quittiert die Umstellung mit Card error.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">T\u00fcckisch daran: scdaemon meldet bei den ersten beiden Versuchen im Terminal nicht einmal einen klaren Fehler, sondern l\u00e4uft einfach in die n\u00e4chste Abfrage weiter. Wer sich auf den Bildschirmtext verl\u00e4sst statt hinterher mit <code>card-status<\/code> nachzuschauen, merkt das Scheitern leicht gar nicht. Das FLOSS-Shop-Datenblatt zur Karte best\u00e4tigt im Nachhinein, warum: gelistet sind nur \u201eNIST\/ANSI&#8220; und \u201eBrainpool&#8220;, Curve 25519 taucht in der Aufz\u00e4hlung schlicht nicht auf. Funktioniert haben stattdessen NIST P-384 und Brainpool P-256:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg.png\"><img loading=\"lazy\" decoding=\"async\" width=\"902\" height=\"1024\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg-902x1024.png\" alt=\"Terminalausgabe von gpg card-edit key-attr, Auswahl von Brainpool P-256 f\u00fcr Signatur- und Verschl\u00fcsselungsschl\u00fcssel l\u00e4uft sauber durch, daneben der Pinentry-Dialog f\u00fcr den Admin-PIN.\" class=\"wp-image-5267\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg-902x1024.png 902w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg-264x300.png 264w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg-768x872.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg-676x768.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/key-attr-brainpool-erfolg.png 1141w\" sizes=\"auto, (max-width: 902px) 100vw, 902px\" \/><\/a><figcaption class=\"wp-element-caption\">Brainpool P-256 f\u00fcr Signatur- und Verschl\u00fcsselungsslot l\u00e4uft sauber durch, jede Umstellung verlangt erneut den Admin-PIN am Pinpad.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">F\u00fcr Signatur und Verschl\u00fcsselung bin ich bei Brainpool P-256 geblieben, den Authentifizierungsslot habe ich sp\u00e4ter aus einem eigenen Grund auf NIST P-384 umgestellt. Dazu gleich mehr, erst kommt aber ein zweites Kartenlimit, das mit Kurven gar nichts zu tun hat.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Admin-PIN-Timeout: kein Zufall, sondern ein hartes Zeitlimit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei den ersten Versuchen mit <code>key-attr<\/code> schlug die Admin-PIN-Eingabe am Pinpad immer wieder mit Statuswort <code>6400<\/code> fehl, ohne dass der PIN-Retry-Z\u00e4hler sich bewegte. Mein erster Verdacht war eine Race Condition zwischen scdaemon und dem Reader. Also habe ich mitgestoppt, wie lange die Eingabe am Pinpad tats\u00e4chlich dauert, bis das OK gedr\u00fcckt ist:<\/p>\n\n\n\n<figure class=\"wp-block-table has-small-font-size\"><table class=\"has-fixed-layout\"><thead><tr><th>Eingabedauer<\/th><th>Ergebnis<\/th><th>F\u00e4lle<\/th><\/tr><\/thead><tbody><tr><td>3 bis 5 Sekunden<\/td><td>SW 9000, Erfolg<\/td><td>8 von 8<\/td><\/tr><tr><td>7 bis 16 Sekunden<\/td><td>SW 6400, Fehler<\/td><td>14 von 14<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Keine Race Condition also, sondern ein hartes Zeitlimit von etwa f\u00fcnf bis sechs Sekunden f\u00fcr die PIN-Eingabe am Pinpad selbst. Der PIN-Retry-Z\u00e4hler bleibt bei einem Timeout unber\u00fchrt, du verlierst also keinen Versuch, nur Zeit. Einen Konfigurationsschalter dagegen habe ich nicht gefunden, ich habe die komplette Flag-Liste der cyberjack.conf durchsucht. Die Option <code>enable-pinpad-varlen<\/code> in <code>scdaemon.conf<\/code> senkt die Fehlerquote sp\u00fcrbar, weil sie variable PIN-L\u00e4ngen am Pinpad erlaubt statt auf eine feste L\u00e4nge zu warten, behebt das Problem aber nicht vollst\u00e4ndig. Praktisch hei\u00dft das: PIN vorher im Kopf bereithalten, z\u00fcgig eintippen, und bei einem Fehlschlag sofort denselben Schritt wiederholen statt lange zu \u00fcberlegen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Den Demo-Schl\u00fcsselsatz bauen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Mit den bekannten Kartenattributen im Kopf habe ich einen Demo-Schl\u00fcsselsatz gebaut, passend zugeschnitten: ein Ed25519-Prim\u00e4rschl\u00fcssel mit ausschlie\u00dflich der Zertifizierungsf\u00e4higkeit <code>[C]<\/code>, dazu drei Unterschl\u00fcssel in <code>brainpoolP256r1<\/code> f\u00fcr Signatur und Verschl\u00fcsselung. Der naheliegende Befehl daf\u00fcr scheitert allerdings:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">gpg --quick-add-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926 brainpoolP256r1 sign\ngpg: Wrong key usage<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bei NIST- und Brainpool-Kurven kennt der Quick-Befehl offenbar nur ECDH als Default-Verwendungszweck, eine Signaturf\u00e4higkeit lehnt er direkt ab. Der Umweg \u00fcber den ausf\u00fchrlichen Editiermodus funktioniert dagegen anstandslos, dort l\u00e4sst sich die Kurve explizit w\u00e4hlen und die F\u00e4higkeit im Nachhinein umschalten:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">gpg --expert --edit-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926\ngpg> addkey\nPlease select what kind of key you want:\n   (11) Existing key\nYour selection? 11\nPlease select which elliptic curve you want:\n   (6) Brainpool P-256\nYour selection? 6\nPossible actions for this ECDH key: Sign Encrypt\nCurrent allowed actions: Encrypt\n   (S) Toggle the sign capability\n   (Q) Finished\nYour selection? S\nYour selection? Q<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">So entstanden, in Reihenfolge, der Verschl\u00fcsselungs-, der Signatur- und vorl\u00e4ufig auch der Authentifizierungs-Unterschl\u00fcssel, alle drei in Brainpool P-256. Der fertige Satz vor dem eigentlichen \u00dcbertragen auf die Karte:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"260\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing-1024x260.png\" alt=\"Terminalausgabe von gpg list-secret-keys with-fingerprint, Ed25519-Hauptschl\u00fcssel plus drei Brainpool-P-256-Unterschl\u00fcssel f\u00fcr Verschl\u00fcsselung, Signatur und Authentifizierung.\" class=\"wp-image-5268\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing-1024x260.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing-300x76.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing-768x195.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing-676x172.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gpg-k-listing.png 1091w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ed25519-Prim\u00e4rschl\u00fcssel mit reiner Zertifizierungsf\u00e4higkeit, darunter drei brainpoolP256r1-Unterschl\u00fcssel. Noch alle im lokalen Schl\u00fcsselbund, noch keiner auf der Karte.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">keytocard f\u00fcr alle drei Unterschl\u00fcssel<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das eigentliche \u00dcbertragen l\u00e4uft f\u00fcr jeden Unterschl\u00fcssel einzeln \u00fcber <code>keytocard<\/code>, mit den \u00fcblichen Wiederholungen, wenn der Admin-PIN-Timeout wieder einmal zuschl\u00e4gt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">gpg --edit-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926\ngpg> key 1\ngpg> keytocard\nPlease select where to store the key:\n   (2) Encryption key\nYour selection? 2\ngpg> key 1\ngpg> key 2\ngpg> keytocard\nPlease select where to store the key:\n   (1) Signature key\nYour selection? 1<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Verifiziert wird das Ergebnis \u00fcber <code>gpg --card-status<\/code>. Alle drei Fingerabdr\u00fccke auf der Karte stimmen mit dem lokalen Schl\u00fcsselbund \u00fcberein, und dort steht jetzt <code>ssb&gt;<\/code> statt nur <code>ssb<\/code>, also der Hinweis, dass der private Schl\u00fcsselteil nicht mehr lokal liegt, sondern nur noch als Verweis auf die Karte:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"421\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final-1024x421.png\" alt=\"Terminalausgabe von gpg --card-status mit allen drei Unterschl\u00fcsseln auf der Karte, dazu der Schl\u00fcsselring-Abgleich mit ssb-Markierung f\u00fcr kartengebundene Schl\u00fcssel.\" class=\"wp-image-5269\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final-1024x421.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final-300x123.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final-768x315.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final-676x278.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/card-status-final.png 1395w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Alle drei Slots besetzt, alle drei Fingerabdr\u00fccke stimmen, und ssb&gt; markiert im Schl\u00fcsselbund, dass der private Teil nur noch auf der Karte existiert.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der SSH-Nachtrag: Brainpool kennt SSH nicht<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Den Authentifizierungs-Unterschl\u00fcssel wollte ich zus\u00e4tzlich als SSH-Schl\u00fcssel benutzen, daf\u00fcr exportiert gpg-agent ihn \u00fcber <code>gpg --export-ssh-key<\/code>. F\u00fcr den Brainpool-Auth-Unterschl\u00fcssel scheitert das mit \u201eUnknown elliptic curve&#8220;. Der Grund liegt nicht bei GnuPG: <a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc5656\" target=\"_blank\" rel=\"noopener\">RFC 5656<\/a>, der SSH-Standard f\u00fcr elliptische Kurven, kennt Brainpool schlicht nicht, nur <code>nistp256<\/code>, <code>nistp384<\/code> und <code>nistp521<\/code>. Also habe ich den Authentifizierungsslot noch einmal neu erzeugt, diesmal mit NIST P-384, w\u00e4hrend Signatur und Verschl\u00fcsselung bei Brainpool P-256 geblieben sind, und den neuen Unterschl\u00fcssel erneut per <code>keytocard<\/code> \u00fcbertragen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dabei sind mir zwei weitere Kleinigkeiten begegnet, beide nicht offensichtlich:<\/p>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li>Meine eigene <code>~\/.ssh\/config<\/code> mit einem globalen <code>Host * \/ IdentitiesOnly yes<\/code> blendet Card-Keys komplett aus, unabh\u00e4ngig davon, ob man den Agenten zus\u00e4tzlich per <code>-o IdentityAgent=...<\/code> explizit angibt. F\u00fcr einen sauberen Test half nur <code>-F \/dev\/null<\/code>, um die eigene Konfiguration ganz zu umgehen.<\/li>\n\n\n\n<li>gpg-agent verlangt den Keygrip des Auth-Unterschl\u00fcssels explizit in <code>sshcontrol<\/code>, sonst kommt \u201eagent refused operation&#8220;, und zwar noch bevor \u00fcberhaupt ein Pinpad-Prompt erscheint. Kein PIN-Fehler, sondern eine reine Allowlist-Sache, die sich leicht mit einem PIN-Problem verwechseln l\u00e4sst.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Drei Live-Tests, alle drei erfolgreich<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zum Schluss die eigentliche Nagelprobe, alle drei Unterschl\u00fcssel einzeln gegen die Karte getestet, mit dem Pinpad als einzigem Ort f\u00fcr die PIN-Eingabe.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Signatur.<\/strong> Eine Testnachricht clearsignen und gleich wieder verifizieren:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"271\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test-1024x271.png\" alt=\"Terminalausgabe eines clearsign- und verify-Durchlaufs mit der Karte, Ergebnis Good signature im ultimate Vertrauensstatus.\" class=\"wp-image-5270\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test-1024x271.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test-300x79.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test-768x203.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test-676x179.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/signatur-test.png 1156w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">clearsign fragt den PIN am Pinpad ab, verify best\u00e4tigt danach eine gute Signatur mit dem ECDSA-Schl\u00fcssel der Karte.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Verschl\u00fcsselung.<\/strong> Ein vollst\u00e4ndiger Roundtrip aus Verschl\u00fcsseln und Entschl\u00fcsseln, wieder mit PIN-Abfrage am Pinpad beim Entschl\u00fcsseln, der Klartext kommt danach unver\u00e4ndert zur\u00fcck:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">echo \"Test von der OpenPGP-Karte\" | gpg --encrypt --recipient card-demo@example.invalid | gpg --decrypt\ngpg: encrypted with brainpoolP256r1 key, ID ..., created ...\n      \"OpenPGP Card Demo (throwaway key for blog\/card demo, not for real use) &lt;card-demo@example.invalid>\"\nTest von der OpenPGP-Karte<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>SSH.<\/strong> Ein Login gegen den eigenen Rechner \u00fcber den Auth-Unterschl\u00fcssel, mit einem nur f\u00fcr den Test tempor\u00e4r erg\u00e4nzten und danach wieder entfernten Eintrag in <code>authorized_keys<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ssh-add -L\necdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQ... cardno:0005 0000D961\nssh -F \/dev\/null -o IdentityAgent=$(gpgconf --list-dirs agent-ssh-socket) kernel@localhost\n<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Login fragt am Pinpad nach dem PIN und ist danach ein ganz normales SSH-Login, der einzige Unterschied gegen\u00fcber einem Schl\u00fcssel auf der Festplatte ist der Prompt am Reader.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Fazit: was die Karte wirklich bringt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zur\u00fcck zur Ausgangsfrage. Nein, eine OpenPGP-Karte h\u00e4lt deinen Prim\u00e4rschl\u00fcssel nicht offline vor, weil sie keinen Zertifizierungsslot hat und ihn deswegen gar nicht aufnehmen kann. Wer genau das sucht, bleibt bei einem verschl\u00fcsselten Cold-Storage-Medium, wie im Ed25519-Beitrag beschrieben.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was die Karte stattdessen bringt, ist aus meiner Sicht mindestens genauso viel wert: die drei Unterschl\u00fcssel, mit denen du tats\u00e4chlich t\u00e4glich arbeitest, Signatur, Verschl\u00fcsselung und Authentifizierung, wandern hardwaregebunden auf ein St\u00fcck Plastik. Kein <code>keytocard<\/code> l\u00e4sst sich r\u00fcckg\u00e4ngig machen, kein Angreifer mit Zugriff auf deine Festplatte bekommt das Schl\u00fcsselmaterial zu fassen, denn es liegt dort gar nicht mehr. Der Prim\u00e4rschl\u00fcssel bleibt davon komplett unber\u00fchrt und weiterhin offline, genau da, wo er laut Restrisiken-Liste vom letzten Mal ohnehin hingeh\u00f6rt. Die Smartcard aus dem Satz \u201eNichts hier braucht eine Smartcard&#8220; wollte etwas anderes l\u00f6sen, als eine Karte l\u00f6sen kann. F\u00fcr das, was sie tats\u00e4chlich kann, w\u00fcrde ich sie nach diesem Test jederzeit empfehlen, nur eben mit einem echten statt einem Wegwerf-Demo-Schl\u00fcssel und mit etwas Geduld f\u00fcr den Admin-PIN-Timeout.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/02\/openpgp-schluessel-ed25519-bauen-gnupg-haertung-praeferenzen\/\">Einen modernen OpenPGP-Schl\u00fcssel bauen: Ed25519, drei Unterschl\u00fcssel und die H\u00e4rtung, die ihn geschw\u00e4cht hat<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/02\/keyoxide-openpgp-schluessel-an-online-identitaeten-binden\/\">Keyoxide: den OpenPGP-Schl\u00fcssel an Online-Identit\u00e4ten binden, vier gr\u00fcne Haken und ein rotes Kreuz<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2013\/09\/14\/der-sichere-gpg-schluessel\/\">Der sichere GPG-Schl\u00fcssel<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2009\/08\/19\/gpg\/\">GPG: E-Mails signieren und verschl\u00fcsseln mit GnuPG<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2012\/05\/27\/hinzufuegen-der-ssh-public-keys-zum-dns\/\">SSH Host Keys per SSHFP im DNS ver\u00f6ffentlichen<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2022\/10\/14\/cloudflare-deal-for-10-11-yubikeys\/\">Cloudflare Deal: g\u00fcnstige YubiKeys f\u00fcr 10 bis 11 USD im Oktober 2022<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wenn du selbst gerade zwischen Karten oder Kurven schwankst, oder wenn dir am Kartenlimit oder am PIN-Timeout noch ein Detail auff\u00e4llt, das ich \u00fcbersehen habe, dann d\u00fcrft ihr mich sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kann eine OpenPGP-Karte den Prim\u00e4rschl\u00fcssel aus dem Ed25519-Beitrag offline vorhalten? Kurz: nein, es fehlt der Zertifizierungsslot daf\u00fcr. Aber sie bindet die drei t\u00e4glich genutzten Unterschl\u00fcssel hardwaregebunden, inklusive Kartenlimit bei Curve 25519 und einem gemessenen PIN-Timeout am Pinpad.<\/p>\n","protected":false},"author":1,"featured_media":5274,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[272,13,274],"tags":[377,134,384,371,36,446,473,63],"class_list":["post-5271","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it-security","category-kernel-error-blog","category-linux-bsd","tag-encryption","tag-gpg","tag-hardening","tag-infosec","tag-linux","tag-openpgp","tag-smartcard","tag-ssh","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5271","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5271"}],"version-history":[{"count":2,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5271\/revisions"}],"predecessor-version":[{"id":5300,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5271\/revisions\/5300"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5274"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5271"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5271"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5271"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5242,"date":"2026-08-16T11:30:19","date_gmt":"2026-08-16T09:30:19","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5242"},"modified":"2026-08-17T09:36:45","modified_gmt":"2026-08-17T07:36:45","slug":"logitacker-feldversuch-unifying-tastatur-schluessel-mitgeschnitten","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/16\/logitacker-feldversuch-unifying-tastatur-schluessel-mitgeschnitten\/","title":{"rendered":"Teil 2: Funktastatur unter Linux \u201ehacken\u201c: AES-Key aus dem Pairing ableiten und Tastendr\u00fccke entschl\u00fcsseln"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">&#8222;Im n\u00e4chsten Teil wird nicht nur erkl\u00e4rt, sondern gemacht. Mit einem nRF52840-Dongle und Logitacker schneide ich den Kopplungsvorgang einer Unifying-Tastatur mit, leite daraus den Schl\u00fcssel ab und lese die Eingaben mit.&#8220; Das habe ich am Ende des letzten Beitrags geschrieben. Jetzt l\u00f6se ich es ein.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Kurz zur Erinnerung, falls du den ersten Teil nicht gelesen hast: Logitech-Funktastaturen mit Unifying-Empf\u00e4nger verschl\u00fcsseln die \u00dcbertragung mit AES, aber die Kopplung selbst, also der kurze Moment, in dem sich Tastatur und Empf\u00e4nger gegenseitig kennenlernen, hat eine nie gepatchte Design-L\u00fccke. Wer genau in diesem Moment mith\u00f6rt, kann daraus den Schl\u00fcssel ableiten. Das ist CVE-2019-13052, seit 2019 \u00f6ffentlich, von Logitech nie gefixt.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech-.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--1024x576.png\" alt=\"Logitech MX Keys mit Unifying-Empf\u00e4nger und nRF52840-Dongle beim Mitschneiden einer Funk-Kopplung mit LOGITacker\" class=\"wp-image-5245\" style=\"width:115px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--1024x576.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--300x169.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--768x432.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--1536x864.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech--676x380.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/logitech-.png 1672w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im ersten Teil stand das als Zeile in einer CVE-Datenbank und als Absatz in einer Offenlegung von Marcus Mengs. Diesmal nicht. Diesmal sitze ich mit einem Dongle f\u00fcr unter 30 Euro vor meiner eigenen MX Keys, schneide die Kopplung mit, und am Ende tippe ich auf der Tastatur, w\u00e4hrend eine Konsole live mitliest, was ich gerade dr\u00fccke.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Vorweg die Grenze des Beitrags, damit du nicht am Ende danach suchst: den abgeleiteten AES-Schl\u00fcssel zeige ich nirgends. Warum, erkl\u00e4re ich weiter unten. Die Kurzform: das war zum Zeitpunkt der Aufnahme ein aktives, funktionierendes Credential f\u00fcr meine echte Tastatur, kein historisches Artefakt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Dongle, und welches Werkzeug ich benutze<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im ersten Teil hatte ich einen GeeekPi-Dongle \u00fcber einen Amazon-Affiliate-Link verlinkt, als Empfehlung f\u00fcr genau dieses Vorhaben. Ehrlich gesagt bin ich am Ende nicht mit dem losgezogen. Zwischen dem Schreiben des ersten Beitrags und diesem Versuch lag noch eine Bestellung woanders, und am Ende kam das Board, das gerade greifbar war, nicht das verlinkte. In der Hand hatte ich stattdessen ein makerdiary nRF52840-MDK USB Dongle, anderer Hersteller, anderes Layout, aber derselbe Chip: ein Nordic nRF52840, auf dem Board direkt aufgedruckt mit <code>N52840 QIAADO 2330FM<\/code>. Kein Beinbruch, eher ein guter Aufh\u00e4nger f\u00fcr den Bezugsquellen-Block weiter unten, den ich mir sonst h\u00e4tte sparen k\u00f6nnen.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"729\" height=\"1024\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box-729x1024.jpg\" alt=\"Verpackung des makerdiary nRF52840-MDK USB Dongle mit aufgedrucktem Board-Layout und Pinbelegung.\" class=\"wp-image-5237\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box-729x1024.jpg 729w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box-214x300.jpg 214w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box-768x1079.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box-676x950.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-box.jpg 1000w\" sizes=\"auto, (max-width: 729px) 100vw, 729px\" \/><\/a><figcaption class=\"wp-element-caption\">Die Verpackung des makerdiary nRF52840-MDK USB Dongle, mit aufgedrucktem Board und den Pin-Bezeichnungen.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Werkzeug ist LOGITacker, urspr\u00fcnglich von mame82, also Marcus Mengs selbst, gebaut und heute unter RoganDawes auf GitHub aktiv gepflegt. Anders als etwa jackit l\u00e4uft hier keine Angriffslogik auf dem Laptop, LOGITacker ist ein Standalone-Ger\u00e4t. Die komplette Software steckt auf dem Dongle, bedient wird sie \u00fcber eine serielle Konsole per USB, der Laptop selbst sieht am Ende nur ein weiteres USB-Ger\u00e4t mit ein paar zus\u00e4tzlichen Schnittstellen. Vier Boards werden offiziell unterst\u00fctzt: der Nordic pca10059, zwei makerdiary-Boards, darunter genau das hier verwendete MDK Dongle, und ein Board von April Brother. Kein Zufallstreffer also. Das Board war von Anfang an ein sinnvoller Kauf f\u00fcr dieses Vorhaben, auch wenn es nicht das verlinkte war, und ich musste vor dem Kauf nicht einmal lange suchen, die Liste der unterst\u00fctzten Boards steht direkt im Repository.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Geflasht habe ich <code>logitacker_mdk_dongle.hex<\/code> aus Release <code>v0.2.3-beta<\/code>, Stand 17. Januar 2020 und bis heute die aktuellste verf\u00fcgbare Version. Kein Neubau aus dem Quellcode n\u00f6tig, an der zugrundeliegenden L\u00fccke hat sich seit 2020 nichts ge\u00e4ndert. Pfostenstifte liegen dem Board bei, werden f\u00fcr dieses Experiment aber nicht gebraucht. Flashen und Betrieb laufen komplett \u00fcber USB, kein L\u00f6tkolben in Sicht.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"558\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board-1024x558.jpg\" alt=\"Das makerdiary nRF52840-MDK USB Dongle neben den beiliegenden, losen Pfostenstiftleisten. Auf dem Chip steht der Aufdruck N52840 2330FM.\" class=\"wp-image-5238\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board-1024x558.jpg 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board-300x164.jpg 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board-768x419.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board-676x368.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-board.jpg 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Das Board selbst, mit lesbarem Chip-Aufdruck N52840 2330FM. Die Pfostenstifte liegen bei, werden f\u00fcr dieses Experiment aber nicht gebraucht.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Flashen, und wie ich den Bootloader falsch eingesch\u00e4tzt habe<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ich bin mit der Annahme reingegangen dass so ein Nordic-Board ein serielles DFU spricht, das man mit <code>nrfutil<\/code> bedient. Falsch gedacht. H\u00e4lt man beim Einstecken den Knopf auf dem Board, meldet es sich stattdessen als schn\u00f6des USB-Massenspeicherger\u00e4t:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ lsblk -o NAME,SIZE,LABEL,FSTYPE,MOUNTPOINT\nsda   32,1M UF2BOOT vfat \/media\/kernel\/UF2BOOT<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein UF2-Bootloader, keine Nordic-eigene serielle Schnittstelle. LOGITackers Release liefert f\u00fcr dieses Board aber nur eine <code>.hex<\/code>-Datei, keine fertige <code>.uf2<\/code>. Also musste ich die Datei erst mit Microsofts eigenem <code>uf2conv.py<\/code> umwandeln, und zwar mit der richtigen Familien-ID, die ich mir nicht ausgedacht, sondern gegen die mitgelieferte <code>uf2families.json<\/code> gepr\u00fcft habe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ grep -A2 NRF52840 uf2families.json\n    \"id\": \"0xada52840\",\n    \"short_name\": \"NRF52840\",\n\n$ python3 uf2conv.py -f 0xADA52840 -c -o logitacker_mdk_dongle.uf2 logitacker_mdk_dongle.hex\nConverted to uf2, output size: 470528, start address: 0x1000\nWrote 470528 bytes to logitacker_mdk_dongle.uf2\n\n$ cp logitacker_mdk_dongle.uf2 \/media\/kernel\/UF2BOOT\/<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Bootloader hat die Datei angenommen und das Laufwerk danach getrennt, das ist normales Verhalten beim Flashen. Was nicht normal lief: das Board hat sich anschlie\u00dfend nicht von selbst mit der neuen Firmware gemeldet. Erst ein einmaliges Aus- und wieder Einstecken hat es zur\u00fcckgebracht, und dann korrekt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ lsusb\nBus 001 Device 021: ID 1915:520c Nordic Semiconductor ASA Logitacker by MaMe82<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Vier neue Schnittstellen kamen dazu: seriell, Maus, Tastatur und HID-Rohdaten. Ein nettes Detail am Rand, die USB-Seriennummer tr\u00e4gt die Firmware-Version gleich mit, <code>v0.2.3-beta<\/code>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"911\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led-1024x911.jpg\" alt=\"Das nRF52840-MDK-Dongle im USB-Port eines Notebooks, mit durchgehend gr\u00fcn leuchtenden LEDs im Bootloader-Modus.\" class=\"wp-image-5239\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led-1024x911.jpg 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led-300x267.jpg 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led-768x683.jpg 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led-676x601.jpg 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/nrf52840-mdk-bootloader-led.jpg 1300w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Im UF2-Bootloader-Modus leuchten die LEDs des Dongles durchgehend gr\u00fcn.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Erstkontakt, und die Adresse stand quasi auf dem Karton<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Serielle Konsole auf, 115200 Baud, fertig. Der Standardmodus von Logitacker hei\u00dft discover, eine Art passiver Dauerscan, und der bringt schon etwas, bevor ich \u00fcberhaupt ein Kommando eingetippt habe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;info> LOGITACKER_PROCESSOR_DISCOVER: DISCOVERY: received valid ESB frame (addr C3:1F:D0:D1:07, len: 15, ch idx 0, raw ch 5, rssi 47)\n&lt;info> LOGITACKER_PROCESSOR_DISCOVER: discovered device is Logitech<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>C3:1F:D0:D1:07<\/code>. Das ist keine zuf\u00e4llige Adresse. Die USB-Seriennummer meines Tastatur-Empf\u00e4ngers, die sowohl <code>solaar show<\/code> meldet als auch auf dem Geh\u00e4use aufgedruckt steht, lautet <code>C31FD0D1<\/code>. Exakt dieselbe Bytefolge, als Pr\u00e4fix der Funkadresse. Ohne jeden Angriff, nur weil das Ger\u00e4t eingeschaltet ist, l\u00e4sst sich die Funkadresse eines Unifying-Empf\u00e4ngers aus der Seriennummer ablesen, die au\u00dfen auf dem Geh\u00e4use steht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ich habe kurz gebraucht, um das wirklich zu glauben, und den Empf\u00e4nger deshalb aus dem Rechner gezogen und die Seriennummer noch einmal mit der Lupe der Handykamera abfotografiert, im Vergleich zur Konsolenausgabe daneben. Kein Tippfehler, keine Verwechslung, dieselben acht Zeichen. F\u00fcr einen Angriff braucht es das ohnehin nicht, discover l\u00e4uft passiv und komplett ohne dass ich irgendetwas an Tastatur oder Empf\u00e4nger anfassen m\u00fcsste.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Drei Versuche, die nichts brachten, und der eine, der klappte<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"540\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-1024x540.png\" alt=\"Diagramm: zwei Kopplungsversuche \u00fcber die Easy-Switch-Taste blieben ohne Funkanfrage beim Dongle, der dritte Versuch per Aus- und Wiedereinschalten der Tastatur war beim ersten Mal erfolgreich.\" class=\"wp-image-5240\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-1024x540.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-300x158.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-768x405.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-1536x810.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche-676x356.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-koppelversuche.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Zwei Versuche \u00fcber die Easy-Switch-Taste brachten nichts, der dritte Versuch nach der dokumentierten Methode klappte sofort.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Um einen echten Kopplungsvorgang mitzuschneiden, muss erst einer stattfinden. Also den Mitschnitt-Modus starten und danach die schon gekoppelte MX Keys trennen, damit sie sich w\u00e4hrend laufendem Mitschnitt neu koppeln kann:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">LOGITacker (discover) $ pair sniff run\n&lt;info> LOGITACKER_RADIO: Channel hopping stopped\n&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: Sniff pairing on address BB:0A:DC:A5:75\n&lt;info> ESB_ILLEGALMOD: Using channel table 'Unifying pairing'\n&lt;info> ESB_ILLEGALMOD: New channel table with length 11<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ solaar unpair 1DA452CF\nUnpaired 1: MX Keys Keyboard (MX Keys) [408A:1DA452CF]\n\n$ solaar pair C31FD0D1\nPairing: turn your new device on (timing out in 30 seconds).<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Erster Versuch: Easy-Switch-Taste &#8222;1&#8220; gehalten, laut <code>solaar show<\/code> genau der Kanal, der f\u00fcr diesen Rechner reserviert ist. Die LED blinkte brav weiter. Beim Dongle kam in den vollen 30 Sekunden, die <code>solaar pair<\/code> \u00f6ffnet, nichts an:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: dongle on channel 44\n... (many more channel hops, receiver beacon tracked continuously) ...\n&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: Lost dongle in pairing mode, restart channel hopping<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zweiter Versuch, gleiche Methode, gleiches Ergebnis. <code>solaar pair<\/code> lief diesmal direkt in einen Timeout:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ solaar pair C31FD0D1\nsolaar: error: Exception: pairing failed: device timeout<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">An der Stelle dachte ich kurz, das Board oder die Firmware h\u00e4tten ein Problem, und habe testweise sogar den Kanal manuell auf den Standardwert zur\u00fcckgesetzt und noch einmal von vorne begonnen. Half nichts. Meine Deutung dazu, ausdr\u00fccklich eine Deutung und kein gemessener Fakt: das Dauerblinken der Easy-Switch-LED zeigt vermutlich nur an, dass dieser Kanal aktuell keinen Empf\u00e4nger kennt, keine aktive Funksuche. Analysiert habe ich das interne Verhalten der Tastatur nicht, daf\u00fcr fehlt mir der Einblick, und ich will hier nichts behaupten, was ich nicht gepr\u00fcft habe.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dritter Versuch, diesmal nach der Methode, die die Logitacker-Dokumentation f\u00fcr Unifying-Ger\u00e4te tats\u00e4chlich vorschreibt und die mit der Easy-Switch-Taste nichts zu tun hat: Tastatur ausschalten, Kopplungsfenster am Empf\u00e4nger \u00f6ffnen, Tastatur wieder einschalten. Erster Versuch nach dieser Methode, sofort erfolgreich:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: PAIR SNIFF data received on channel 5\n&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: PAIR SNIFF assigned C3:1F:D0:D1:08 as new sniffing address\n&lt;info> LOGITACKER_PAIRING_PARSER: Device name: MX Keys\n&lt;info> LOGITACKER_PAIRING_PARSER: Device RF address: C3:1F:D0:D1:08\n&lt;info> LOGITACKER_PAIRING_PARSER: Device serial: 1D:A4:52:CF\n&lt;info> LOGITACKER_PAIRING_PARSER: Device WPID: 0x408A\n&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: device automatically stored to flash\n&lt;info> LOGITACKER_PROCESSOR_PAIR_SNIFF: Sniffed full pairing, moving on with passive enumeration for C3:1F:D0:D1:08<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>solaar show<\/code> best\u00e4tigt unabh\u00e4ngig dieselbe Seriennummer <code>1DA452CF<\/code> und denselben WPID <code>408A<\/code> f\u00fcr dieselbe Tastatur, wobei die neu ersniffte Funkadresse <code>C3:1F:D0:D1:08<\/code> denselben Pr\u00e4fix tr\u00e4gt wie die passive Sichtung von vorhin, nur eine Adresse h\u00f6her.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was ich da wirklich in der Hand hatte, und was ich bewusst nicht zeige<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein erfolgreich mitgeschnittener Kopplungsvorgang liefert bei Logitacker f\u00fcnf Dinge: Ger\u00e4tename, Funkadresse, Seriennummer, WPID, und einen 16 Byte langen AES-Schl\u00fcssel. Die ersten vier stehen oben, alle unabh\u00e4ngig gegen Solaar gepr\u00fcft. Der Schl\u00fcssel steht hier nicht. Kein einziges Byte davon, auch keine erfundenen Beispielwerte, die nur wie ein Schl\u00fcssel aussehen w\u00fcrden.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Unterschied zur EK-Zertifikat-Entscheidung im TPM-Beitrag l\u00e4sst sich in einem Satz sagen: dort ging es um eine dauerhafte Ger\u00e4te-Kennung, eindeutig, aber f\u00fcr sich genommen nicht ausnutzbar. Hier ging es um ein Credential, das im Moment der Aufnahme aktiv g\u00fcltig war und echten Zugriff auf meine echte, gerade auf meinem Schreibtisch liegende Tastatur bedeutet h\u00e4tte. Direkt im Anschluss an den Mitschnitt aus dem n\u00e4chsten Abschnitt habe ich die Kopplung deshalb noch einmal neu gemacht, um den mitgeschnittenen Schl\u00fcssel ung\u00fcltig zu machen:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ solaar unpair 1DA452CF\nUnpaired 1: MX Keys Keyboard (MX Keys) [408A:1DA452CF]\n\n$ solaar pair C31FD0D1\nPairing: turn your new device on (timing out in 30 seconds).\nPaired device 1: MX Keys Keyboard (MX Keys) [408A:1DA452CF]<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wieder die Aus-und-Einschalt-Methode, wieder auf Anhieb erfolgreich. Der Schl\u00fcssel aus dem Mitschnitt ist damit nicht mehr der Schl\u00fcssel, der heute im Einsatz ist. Falls du dich fragst, ob das den Beweis im n\u00e4chsten Abschnitt irgendwie entwertet: nein, der wurde vorher aufgezeichnet, die Rotation kam erst danach.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Der Beweis: Tastenanschl\u00e4ge in Echtzeit entschl\u00fcsselt<\/h3>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"883\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-1024x883.png\" alt=\"Diagramm: vom Pairing-Handshake \u00fcber den mitgeschnittenen und abgeleiteten AES-Schl\u00fcssel bis zur Live-Entschl\u00fcsselung der Tastenanschl\u00e4ge, wobei der Schl\u00fcssel selbst nicht gezeigt wird.\" class=\"wp-image-5241\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-1024x883.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-300x259.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-768x662.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-1536x1325.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf-676x583.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/diagramm-mitschnitt-ablauf.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Der Ablauf vom mitgeschnittenen Handshake bis zum entschl\u00fcsselten Klartext. Der Schl\u00fcssel selbst taucht in diesem Beitrag nirgends auf.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nach dem erfolgreichen Mitschnitt wechselt Logitacker automatisch in einen passiven Mith\u00f6r-Modus. Ich habe angefangen, auf der frisch wieder gekoppelten MX Keys zu tippen, und die Konsole hat live mitgeschrieben. Vier Tastendr\u00fccke aus derselben Sitzung, jeder mit eigenem Rohframe und eigenem entschl\u00fcsseltem Ergebnis:<\/p>\n\n\n\n<figure class=\"wp-block-table has-small-font-size\"><table class=\"has-fixed-layout\"><thead><tr><th>Z\u00e4hler<\/th><th>Verschl\u00fcsselter Rohframe<\/th><th>Entschl\u00fcsselter Klartext-Report<\/th><th>Erkannte Taste<\/th><\/tr><\/thead><tbody><tr><td>46932812<\/td><td><code>00 D3 45 B3 46 90 36 E6 B0 93 46 93 28 12 00 00 00 00 00 00 00 ED<\/code><\/td><td><code>00 13 00 00 00 00 00 C9<\/code><\/td><td><code>P<\/code><\/td><\/tr><tr><td>46932814<\/td><td><code>00 D3 82 5E 0C 2F 89 D7 B2 86 46 93 28 14 00 00 00 00 00 00 00 65<\/code><\/td><td><code>00 17 00 00 00 00 00 C9<\/code><\/td><td><code>T<\/code><\/td><\/tr><tr><td>46932816<\/td><td><code>00 D3 C7 B6 18 D3 34 82 1E 3D 46 93 28 16 00 00 00 00 00 00 00 9D<\/code><\/td><td><code>00 37 00 00 00 00 00 C9<\/code><\/td><td><code>.<\/code><\/td><\/tr><tr><td>46932818<\/td><td><code>00 D3 68 E2 F1 7D 3C 6B D6 3D 46 93 28 18 00 00 00 00 00 00 00 A2<\/code><\/td><td><code>00 28 00 00 00 00 00 C9<\/code><\/td><td><code>ENTER<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nacheinander gelesen: <code>P<\/code>, <code>T<\/code>, <code>.<\/code>, <code>ENTER<\/code>. Kein vollst\u00e4ndiges Wort, sondern das Ende eines Satzes, den ich in dem Moment tats\u00e4chlich getippt habe, plus Enter. Genau das steht hier auch so, nicht sch\u00f6ngeredet zu mehr, als es ist.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die Rohframes in der Tabelle sind Chiffretext, unbedenklich zum Zeigen, reines Rauschen ohne den Schl\u00fcssel. Was daraus die Klartext-Reports macht, ist der Schl\u00fcssel aus dem vorigen Abschnitt, und der taucht hier nicht auf. Ich habe dabei ziemlich bewusst langsam getippt, fast schon ein bisschen albern vor dem eigenen Bildschirm, nur um in der Konsole klar zuordnen zu k\u00f6nnen, welche Zeile zu welchem Tastendruck geh\u00f6rt. Es hat trotzdem gedauert, bis ich wirklich geglaubt habe, was da steht.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was das nicht war<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eine kurze, ehrliche Einordnung. Dieser Versuch endet beim Mitlesen. Ich habe keine Einschleusung versucht, obwohl derselbe bekannte Schl\u00fcssel das technisch auch f\u00fcr gef\u00e4lschte, eingeschleuste Tastenanschl\u00e4ge erm\u00f6glicht h\u00e4tte, also f\u00fcr aktiven Angriff statt reinem Zuh\u00f6ren. Das ist die andere H\u00e4lfte von CVE-2019-13052, und die habe ich bewusst nicht angefasst. Nicht aus technischer Unf\u00e4higkeit, Logitacker kann das nachweislich, sondern weil eine aktive Einschleusung gegen die eigene Tastatur eine andere Kategorie Experiment ist als reines Zuh\u00f6ren, und weil das f\u00fcr den Beleg der L\u00fccke in diesem Beitrag nicht n\u00f6tig war.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was das f\u00fcr die Empfehlung aus Beitrag 1 bedeutet<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Im ersten Teil stand am Ende eine Rangfolge: Kabel f\u00fcr wirklich Geheimes, Logi Bolt oder sauberes Bluetooth f\u00fcrs B\u00fcro, vorhandenes Unifying behalten und pflegen, No-Name-Funktastaturen ersetzen. An dieser Rangfolge \u00e4ndert dieser Beitrag nichts. Was sich \u00e4ndert, ist der Status des Arguments dahinter. Die Kopplungs-L\u00fccke ist jetzt kein Datenbankeintrag mehr, sondern ein Ablauf, den ich mit handels\u00fcblicher Hardware f\u00fcr unter 30 Euro selbst gegen mein eigenes Ger\u00e4t durchgef\u00fchrt habe, an einem gew\u00f6hnlichen Nachmittag, ohne Speziallabor und ohne Vorwissen, das ich mir nicht selbst aus der Dokumentation h\u00e4tte holen k\u00f6nnen. Das praktische Risiko bleibt so klein wie im ersten Teil beschrieben, gebunden an den kurzen, seltenen Moment der Kopplung. Aber &#8222;das ist doch nur graue Theorie&#8220; tr\u00e4gt als Einwand jetzt nicht mehr, und genau das war f\u00fcr mich der eigentliche Grund, diesen zweiten Teil \u00fcberhaupt zu schreiben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was offen bleibt<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li>Warum die Easy-Switch-Taste keine Kopplungsanfrage ausl\u00f6st, bleibt meine Deutung des Dauerblinkens, keine best\u00e4tigte Analyse des Tastatur-internen Verhaltens. Sicher war ich einfach nur zu dumm die Tasten in der richtigen Reihenfolge zu dr\u00fccken. <em>gefettfingert<\/em><\/li>\n\n\n\n<li>Die aktive Einschleusungsh\u00e4lfte von CVE-2019-13052 habe ich mit dem bekannten Schl\u00fcssel nicht ausprobiert. Ob und wann daraus noch ein dritter Teil wird, ist offen. Das h\u00e4ngt nun also an euch. Wenn ich sehen wollte, wie ich einfach Text oder Kommandos einschleuse, sagt es mir \ud83d\ude42<\/li>\n\n\n\n<li>Ob sich derselbe Ablauf gegen andere Unifying-Tastaturen als meine MX Keys genauso zuverl\u00e4ssig reproduzieren l\u00e4sst, habe ich nicht gepr\u00fcft. Aber hey, wir sind uns nun wohl alle sicher, dass es klappen wird, oder?<\/li>\n\n\n\n<li>Die genaue Ursache der beiden ersten Kopplungsversuche, ob Timing, Tastatur-Firmware-Logik oder etwas Drittes, habe ich nicht bis auf die Funkebene nachverfolgt. <em>gefettfingert<\/em><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Bezugsquellen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der folgende Link ist ein Affiliate-Link (Werbung). Kaufst du dar\u00fcber, bekomme ich eine kleine Provision, f\u00fcr dich \u00e4ndert sich am Preis nichts.<\/p>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li>nRF52840-Dongle (vergleichbares Modell, nicht exakt das hier gezeigte Board): <a href=\"https:\/\/amzn.to\/3RDuzQ8\" target=\"_blank\" rel=\"sponsored nofollow noopener\">https:\/\/amzn.to\/3RDuzQ8<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Dongle in diesem Beitrag war tats\u00e4chlich ein makerdiary nRF52840-MDK USB Dongle, \u00fcber einen anderen Kanal bezogen. Der verlinkte Dongle tr\u00e4gt denselben Nordic-nRF52840-Chip, ist aber ein anderes Board von einem anderen Hersteller. Ob Logitacker dieses konkrete Modell offiziell unterst\u00fctzt, habe ich nicht gepr\u00fcft. Offiziell gelistet sind der Nordic pca10059, zwei makerdiary-Boards und ein Dongle von April Brother.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Siehe auch<\/h3>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/10\/funkmaus-funktastatur-sicherheit-unter-linux-logitech-unifying\/\">Die Maus funkt im Klartext<\/a>, der erste Teil mit der \u00dcbersicht zu Funkmaus- und Funktastatur-Sicherheit unter Linux.<\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2026\/08\/10\/tpm-2-0-unter-linux-tpm2-tools-measured-boot-attestierung\/\">TPM 2.0 unter Linux<\/a>, der andere Beitrag \u00fcber Hardware, die kaum jemand anschaut.<\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wenn ich irgendwo danebenliege, korrigiert mich gerne, dann lerne ich selbst etwas. Und die Frage an euch zum Schluss: W\u00fcrdet ihr f\u00fcr ein bisschen mehr Sicherheit auf Logi Bolt oder Kabel umsteigen, oder ist euch das Restrisiko bei einer gepflegten Unifying-Tastatur egal? Wenn ihr m\u00f6gt, d\u00fcrft ihr mich dazu sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nach der Theorie im ersten Teil jetzt der Praxistest: mit einem nRF52840-Dongle und Logitacker schneide ich die Kopplung meiner Logitech MX Keys mit und leite daraus den AES-Schl\u00fcssel ab. Zwei Versuche \u00fcber die Easy-Switch-Taste scheiterten, der dritte klappte sofort, und am Ende liest eine Konsole live mit, was ich gerade tippe.<\/p>\n","protected":false},"author":1,"featured_media":5245,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[272,13,274],"tags":[377,381,371,36,469,462,471,470,472,378,466,463],"class_list":["post-5242","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-it-security","category-kernel-error-blog","category-linux-bsd","tag-encryption","tag-hacking","tag-infosec","tag-linux","tag-logitacker","tag-logitech","tag-makerdiary","tag-nrf52840","tag-pairing","tag-privacy","tag-solaar","tag-unifying","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5242","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5242"}],"version-history":[{"count":7,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5242\/revisions"}],"predecessor-version":[{"id":5281,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5242\/revisions\/5281"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5245"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5242"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5242"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5242"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}},{"id":5215,"date":"2026-08-16T10:00:00","date_gmt":"2026-08-16T08:00:00","guid":{"rendered":"https:\/\/www.kernel-error.de\/?p=5215"},"modified":"2026-08-12T10:23:33","modified_gmt":"2026-08-12T08:23:33","slug":"sipgate-linux-softphone-nextcloud-kontakte-kalender","status":"publish","type":"post","link":"https:\/\/www.kernel-error.de\/2026\/08\/16\/sipgate-linux-softphone-nextcloud-kontakte-kalender\/","title":{"rendered":"sipgate unter Linux: ein Softphone, das Kontakte und Kalender aus der eigenen Nextcloud kennt"},"content":{"rendered":"\n<p class=\"has-small-font-size wp-block-paragraph\">Vor zw\u00f6lf Jahren habe ich hier beschrieben, wie der Familienkalender meiner Frau in die ownCloud gewandert ist. Vorher gab es ein B\u00fcchlein in ihrer Tasche und einen gro\u00dfen Kalender an der Wand, und beide waren selten einer Meinung. Seitdem liegen Termine und Kontakte unter eigener Kontrolle, und alles M\u00f6gliche greift darauf zu. Nur ein Ger\u00e4t hat es nie getan: das Telefon am Schreibtisch.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-large is-resized\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-1024x576.png\" alt=\"Zwei Karten nebeneinander: links ein Anruf als nackte Rufnummer ohne Adressbuch, rechts derselbe Anruf mit aufgel\u00f6stem Namen aus der eigenen Nextcloud.\" class=\"wp-image-5214\" style=\"width:146px;height:auto\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-1024x576.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-300x169.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-768x432.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-1536x864.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured-676x380.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-sipgate-featured.png 1672w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das hat mich lange nicht gest\u00f6rt, bis ich es einmal ausprobiert habe. Ein Anruf kommt rein, auf dem Bildschirm steht eine nackte Rufnummer, und ich sitze vor einem Rechner, auf dem 144 Kontakte mit genau dieser Nummer liegen. Das ist albern.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Mein sipgate-Konto ist alt. Wie alt genau, wusste ich selbst nicht mehr. In diesem Blog taucht sipgate erstmals im August 2009 auf, und 2015 habe ich beim Abschalten meiner alten 01801-Nummer geschrieben, ich h\u00e4tte sie \u201evor ? 13 ? Jahren&#8220; bekommen. Die Fragezeichen waren schon damals meine. Also habe ich einfach den Support gefragt, ausdr\u00fccklich nur aus Neugier. Die Antwort kam am n\u00e4chsten Morgen: angemeldet am 30. April 2005. Da sipgate 2004 mit den Basis-Accounts gestartet ist, bin ich also aus dem ersten Jahr dabei, gut zwanzig Jahre. Dass sich jemand die M\u00fche macht, so eine reine Neugier-Frage \u00fcberhaupt zu beantworten, finde ich bemerkenswert.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was ich wollte, war jedenfalls nichts Exotisches: ein Softphone unter Linux, das an diesem Konto h\u00e4ngt und beim Klingeln in mein eigenes Adressbuch schaut statt in gar keines. Dazu am besten noch die Kalender, damit ich beim Telefonieren sehe, ob der Termin, \u00fcber den gerade gesprochen wird, \u00fcberhaupt frei ist.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Weg dahin war \u00fcberraschend kurz, aber die naheliegenden Kandidaten f\u00fchren alle in die Irre. Deshalb schreibe ich beides auf.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Warum der naheliegende Weg nicht funktioniert<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der erste Reflex ist, das Softphone des Anbieters zu nehmen. Bei sipgate steht dazu im eigenen Hilfecenter, dass es unter Linux nicht installiert, sondern nur ausgef\u00fchrt wird, dass es <strong>kein 64 Bit<\/strong> kann, also weder auf x86_64 noch auf ARM l\u00e4uft, und dass der Support in K\u00fcrze eingestellt wird und keine Updates mehr kommen. Das ist eine erfrischend ehrliche Ansage, hilft aber nicht weiter.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der zweite Reflex hei\u00dft Linphone, und das ist die Falle, in der ich am l\u00e4ngsten gesteckt habe. Linphone kann CardDAV wirklich, die Funktionen stecken vollst\u00e4ndig in der Bibliothek <code>liblinphone<\/code>. Nur hat die Oberfl\u00e4che der paketierten Version keinen einzigen Schalter daf\u00fcr, dort gibt es bei den Adressbuchquellen ausschlie\u00dflich LDAP. In die Oberfl\u00e4che kam CardDAV erst mit Version 6.0, und die liegt in <strong>keinem einzigen Linux-Distributionsrepo<\/strong>. Ein Blick auf <a href=\"https:\/\/repology.org\/project\/linphone-desktop\/versions\" target=\"_blank\" rel=\"noopener\">Repology<\/a> zeigt f\u00fcr <code>linphone-desktop<\/code> gerade einmal AUR bei 6.1.2, alles andere h\u00e4ngt bei 4.x oder 5.x, Debian unstable bei 5.2.6. Der Hersteller selbst liefert f\u00fcr Linux ausschlie\u00dflich AppImages aus, ohne ver\u00f6ffentlichte Pr\u00fcfsumme und ohne eingebettete Update-Information. Wer sich nicht um Aktualisierungen k\u00fcmmern will, ist damit an der falschen Adresse.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Blink f\u00e4llt aus, weil es zwar ein ausgereifter SIP-Client ist, beim Adressbuch aber nur Google Contacts kennt. GNOME Calls w\u00e4re technisch ein Weg, ist aber f\u00fcr Telefone gebaut und als Desktop-Softphone d\u00fcnn.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nebenbei: die Frage wird gestellt. Im Nextcloud-Forum steht ein Thread vom August 2025 mit genau diesem Wunsch, und der Fragesteller verweist darin auf einen \u00e4lteren Thread zum selben Thema, der ohne Ergebnis blieb. Die Antworten waren eine kommerzielle L\u00f6sung und ein Umweg \u00fcber die FRITZ!Box. Was ich jetzt benutze, kam in keinem der beiden vor.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Die Kette, die tats\u00e4chlich funktioniert<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Trick besteht darin, dass das Softphone gar nicht mit der Nextcloud sprechen muss. Unter Linux gibt es daf\u00fcr l\u00e4ngst eine Zwischenschicht, und die hei\u00dft evolution-data-server. Sie ist auf so gut wie jedem Desktop installiert, weil Kalender- und Kontaktanwendungen darauf aufsetzen. Und sie spricht CalDAV und CardDAV von Haus aus.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Nextcloud\n  |\n  +-- GNOME Online Accounts     Provider owncloud, CalDAV und CardDAV\n       |\n       +-- evolution-data-server\n            |\n            +-- Softphone (GOnnect, Adressbuchquelle \"EDS\")<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das Softphone ist damit austauschbar, und die Zugangsdaten der Cloud liegen genau an einer Stelle statt in jeder Anwendung noch einmal. Als Softphone nehme ich <strong><a href=\"https:\/\/flathub.org\/apps\/de.gonicus.gonnect\" target=\"_blank\" rel=\"noopener\">GOnnect<\/a><\/strong> von der GONICUS GmbH, einem Open-Source-UC-Client, den es als Flatpak auf Flathub gibt. Er kann als Adressbuchquellen LDAP, CardDAV, CSV und eben evolution-data-server.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dass er \u00fcber Flathub kommt, war f\u00fcr mich das Ausschlusskriterium gegen alles andere. Ein AppImage m\u00fcsste ich von Hand nachziehen, ein Flatpak l\u00e4uft mit <code>flatpak update<\/code> einfach mit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 1: Die Nextcloud in die Online-Konten h\u00e4ngen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Unter Linux Mint findest du das unter \u201eOnline-Konten&#8220;, dahinter steckt <code>gnome-online-accounts-gtk<\/code>. Dort legst du ein Konto vom Typ <strong>Nextcloud<\/strong> an, tr\u00e4gst die Adresse deiner Instanz, deinen Benutzernamen und ein App-Passwort ein und hakst Kalender und Kontakte an.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"716\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-1024x716.png\" alt=\"Der Dialog Online-Konten unter Linux Mint mit einem eingerichteten Nextcloud-Konto, bei dem Kalender, Kontakte und Dateien eingeschaltet sind.\" class=\"wp-image-5210\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-1024x716.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-300x210.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-768x537.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-1536x1074.png 1536w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud-676x473.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-online-accounts-nextcloud.png 1616w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ein Konto vom Typ Nextcloud in den Online-Konten, Kalender und Kontakte angehakt. Ab hier kennt der ganze Rechner die Daten, nicht nur ein Programm.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Nimm daf\u00fcr wirklich ein App-Passwort aus den Sicherheitseinstellungen deiner Nextcloud und nicht dein normales. Falls du sp\u00e4ter den Zugriff eines einzelnen Rechners zur\u00fcckziehen willst, geht das damit mit einem Klick, ohne dass du \u00fcberall sonst neue Zugangsdaten eintragen musst.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wenn es geklappt hat, sieht die Konfiguration hinterher so aus:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[Account account_1735125820_0]\nProvider=owncloud\nUri=https:\/\/cloud.example.org\/remote.php\/webdav\nCalendarEnabled=true\nCalDavUri=https:\/\/cloud.example.org\/remote.php\/dav\nContactsEnabled=true\nCardDavUri=https:\/\/cloud.example.org\/remote.php\/dav\nFilesEnabled=true\nAcceptSslErrors=false<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Auf <code>AcceptSslErrors=false<\/code> lohnt ein Blick. Steht dort <code>true<\/code>, akzeptiert die Verbindung kaputte Zertifikate, und dann kannst du dir den ganzen Rest sparen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ab hier haben alle Programme auf dem Rechner Zugriff, die evolution-data-server nutzen. Das Softphone ist nur eines davon.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 2: GOnnect installieren<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">flatpak install flathub de.gonicus.gonnect<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das war der ganze Schritt. Rund 310 MB, und beim ersten Start liest GOnnect die Kontakte und Kalender aus evolution-data-server ein, ohne dass du irgendwo CardDAV konfigurieren m\u00fcsstest. Bei mir standen im Protokoll direkt beim ersten Start:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">gonnect.app.addressbook: Found 1 active configurations for address book plugin \"EDS\"\ngonnect.app.feeder.EDSAddressBookFeeder: Loaded 144 contact(s) of source \"Kontakte\"<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ja, und die Kalender ebenfalls, inklusive der Familienkalender aus dem Beitrag von 2014. Die stehen dann rechts im Fenster, unter den Favoriten und neben der Anrufliste.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"501\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender-1024x501.png\" alt=\"Das Hauptfenster von GOnnect mit der Anrufliste auf der linken Seite, den Favoriten rechts oben und den Terminen aus der Nextcloud rechts unten.\" class=\"wp-image-5211\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender-1024x501.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender-300x147.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender-768x375.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender-676x330.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-hauptfenster-anrufliste-kalender.png 1485w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Links die Anrufliste, rechts die Favoriten und darunter die Termine aus dem Familienkalender. Namen, Nummern und Kontaktbilder sind unkenntlich gemacht.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und die Suche oben im Fenster greift auf dasselbe Adressbuch zu. Ein paar Buchstaben gen\u00fcgen, dann steht der Treffer da, und dar\u00fcber die Quelle, aus der er kommt: <code>eds-contacts<\/code>. Genau darum ging es. Kein Zwischenschritt, kein Export, keine zweite Kontaktverwaltung im Softphone.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"502\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts-1024x502.png\" alt=\"Die Kontaktsuche in GOnnect zeigt einen gefundenen Kontakt, dar\u00fcber steht die Quelle eds-contacts.\" class=\"wp-image-5212\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts-1024x502.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts-300x147.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts-768x376.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts-676x331.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-kontaktsuche-eds-contacts.png 1482w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ein paar Buchstaben gen\u00fcgen. \u00dcber dem Treffer steht die Quelle: eds-contacts, also das Adressbuch aus der eigenen Nextcloud.<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Schritt 3: Das sipgate-Konto eintragen<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Hier kommt die Eigenheit von GOnnect, an der man sich einmal sto\u00dfen muss: <strong>es gibt keinen Einrichtungsassistenten und keinen Einstellungsdialog f\u00fcr das SIP-Konto.<\/strong> Der Client ist daf\u00fcr gebaut, in Firmen ausgerollt zu werden, und erwartet deshalb eine fertige Konfigurationsdatei. Die liegt hier:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">~\/.var\/app\/de.gonicus.gonnect\/config\/gonnect\/99-user.conf<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Unter der Haube arbeitet PJSIP, entsprechend sehen die Schl\u00fcssel aus. Das hier ist die vollst\u00e4ndige Konfiguration, die bei mir mit einem sipgate-Basis-Konto l\u00e4uft:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[account0]\nuserUri=sip:1234567e0@sipgate.de\nregistrarUri=sip:sipgate.de\nproxies=sip:sip.sipgate.de:5061;transport=tls\nauth=auth0\ntransport=tls\nsrtpUse=mandatory\nsrtpSecureSignaling=1\nverifyServer=true\ncaListFile=\/etc\/ssl\/certs\/ca-certificates.crt\ncontactRewriteMethod=always-update\n\n[auth0]\nscheme=Digest\nusername=1234567e0\nrealm=sipgate.de\ntype=digest\ndata=HIER_DER_MD5_HASH<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><code>1234567e0<\/code> ist deine SIP-ID aus dem sipgate-Konto, nicht deine Rufnummer.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wichtig ist die Zeile <code>type=digest<\/code>. Du kannst dort auch dein Passwort im Klartext hinterlegen, aber das musst du nicht. SIP authentifiziert sich per Digest, und der Hash daf\u00fcr ist schlicht <code>MD5(Benutzer:Realm:Passwort)<\/code>. Den rechnest du dir selbst aus:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">printf '%s' '1234567e0:sipgate.de:DEIN_SIP_PASSWORT' | md5sum<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das Ergebnis kommt hinter <code>data=<\/code>. Danach setzt du die Datei noch auf <code>chmod 600<\/code>.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Damit steht dein Passwort nicht mehr w\u00f6rtlich in einer Konfigurationsdatei. Ehrlich bleiben muss man trotzdem: der Hash ist f\u00fcr diesen Realm genauso viel wert wie das Passwort selbst, wer ihn hat, kann sich anmelden. Der Gewinn ist ein anderer. Falls du dieses Passwort irgendwo sonst auch verwendest, liegt es hier nicht lesbar herum.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Detail, das mich beim \u00c4ndern der Datei erwischt hat: <strong>GOnnect muss dabei beendet sein.<\/strong> Der Client schreibt die Datei beim Beenden aus dem Speicher zur\u00fcck und \u00fcberschreibt deine \u00c4nderungen sonst kommentarlos.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was der Desktop davon merkt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zwei Dinge sind mir erst im Betrieb aufgefallen, und beide geh\u00f6ren zu der Sorte, die man nicht vermisst, solange man sie nicht kennt.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wenn ein Anruf reinkommt oder du selbst einen startest, <strong>pausiert die laufende Medienwiedergabe von selbst.<\/strong> Das YouTube-Video im Firefox h\u00e4lt an, der Musikplayer ebenso, und nach dem Auflegen l\u00e4uft beides weiter. Dahinter steckt <a href=\"https:\/\/specifications.freedesktop.org\/mpris-spec\/latest\/\" target=\"_blank\" rel=\"noopener\">MPRIS<\/a>, die Schnittstelle, \u00fcber die sich Medienplayer unter Linux fernsteuern lassen. F\u00fcr mich war das der Moment, in dem sich das Ding nicht mehr nach Fremdk\u00f6rper angef\u00fchlt hat, sondern nach Teil des Desktops.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das zweite: <strong><code>sip:<\/code>-Links auf Webseiten funktionieren.<\/strong> Ein Klick darauf \u00f6ffnet die Anwendung und w\u00e4hlt sofort. Der Client tr\u00e4gt sich beim Installieren als Handler f\u00fcr dieses Schema ein, du musst daf\u00fcr nichts konfigurieren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Verschl\u00fcsselung, und warum eine Zeile davon die wichtigste ist<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">sipgate kann das seit \u00fcber zwanzig Jahren. Heise hat am 1. Februar 2006 \u00fcber \u201esipgate-Crypto&#8220; berichtet, damals schon mit TLS f\u00fcr die Signalisierung und SRTP f\u00fcr die Sprache bis zum Festnetz-Gateway. Das Problem war seinerzeit, dass kaum ein Endger\u00e4t mitspielte, weshalb sipgate passende Hardware gleich mit anbot. Zwanzig Jahre sp\u00e4ter kann es jedes Ger\u00e4t, und trotzdem steht es in kaum einer Anleitung.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Eingeschaltet ist es n\u00e4mlich nicht von allein. Die Standardkonfiguration l\u00e4uft \u00fcber UDP und unverschl\u00fcsselt, du musst zwei Dinge selbst \u00e4ndern: <code>sip.sipgate.de<\/code> als Proxy eintragen und die Signalisierung von UDP auf TLS umstellen.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Interessant ist, was danach passiert. In der sipgate-Dokumentation steht dieser Satz:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"has-small-font-size wp-block-paragraph\">Bei verschl\u00fcsselter Signalisierung erfordern wir ebenfalls, dass die Sprachdaten verschl\u00fcsselt werden. Ist diese Einstellung falsch, so werden Anrufe mit Fehler \u201e488 No Acceptable here&#8220; abgewiesen.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Sobald die Signalisierung verschl\u00fcsselt ist, erzwingt sipgate also auch die Sprachverschl\u00fcsselung.<\/strong> Das ist die angenehmste Sorte Sicherheitsentscheidung, weil sie einem die Wahl abnimmt. <code>srtpUse=mandatory<\/code> ist damit nicht meine Strenge, sondern schlicht das, was die Gegenseite ohnehin verlangt. Ein <code>optional<\/code> w\u00fcrde dir nichts retten, der Anruf k\u00e4me trotzdem nicht zustande, nur mit einer verwirrenderen Fehlermeldung.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Kleine Randbemerkung, weil es mich gefreut hat: gefunden habe ich diesen Satz nicht durch Klicken im Hilfecenter, sondern hier:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">curl https:\/\/help.sipgate.de\/llms-full.txt<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das ist das komplette Hilfecenter als eine Textdatei, rund 350 KB, gedacht als maschinenlesbare Fassung f\u00fcr Sprachmodelle. Zum Durchsuchen mit <code>grep<\/code> ist das erheblich angenehmer als jede Suchmaske, und es beantwortet Fragen, die im Men\u00fc drei Ebenen tief liegen. Ich habe so etwas seit einer Weile selbst auf diesem Blog liegen, und es ist sch\u00f6n zu sehen, dass es sich langsam herumspricht.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Die eigentlich wichtige Zeile ist eine andere, und die steht in keinem Beispiel, das ich gefunden habe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">verifyServer=true\ncaListFile=\/etc\/ssl\/certs\/ca-certificates.crt<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Die Voreinstellung von PJSIP ist <code>verifyServer=false<\/code>.<\/strong> \u00dcbersetzt hei\u00dft das: die Verbindung wird verschl\u00fcsselt, aber es wird nicht gepr\u00fcft, mit wem. Jedes beliebige Zertifikat wird angenommen. Gegen jemanden, der auf dem Weg sitzt und die Verbindung \u00fcbernimmt, ist so eine Verschl\u00fcsselung wertlos, weil er einfach sein eigenes Zertifikat vorzeigt. Erst mit diesen zwei Zeilen wird gepr\u00fcft, und zwar gegen den Zertifikatsspeicher deines Systems.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Nachmessen statt glauben<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Dass die Registrierung mit <code>code=200<\/code> klappt, sagt \u00fcber die Verschl\u00fcsselung nichts. Also nachsehen. Der einfachste Test zeigt, wohin die Verbindung \u00fcberhaupt geht:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ss -tnp | grep 5061<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ESTAB  [2001:db8::23]:49705 -> [2001:ab7::18]:5061  users:((\"gonnect\",pid=48721))\nESTAB  [2001:db8::23]:38171 -> [2001:ab7::1a]:5061  users:((\"gonnect\",pid=48721))<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Port 5061, und auf Port 5060 liegt nichts mehr, es l\u00e4uft also kein Klartext-SIP nebenher. Meine eigene Adresse habe ich ersetzt, die Gegenstellen sind echt. Ein <code>whois<\/code> auf <code>2001:ab7::\/36<\/code> nennt die netzquadrat GmbH, und <code>sip.sipgate.de<\/code> l\u00f6st genau auf diese Adressen auf. Das Zertifikat dazu:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">openssl s_client -connect sip.sipgate.de:5061 -servername sip.sipgate.de &lt;\/dev\/null | openssl x509 -noout -subject -issuer<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">subject=CN = sip.sipgate.de\nissuer=C = US, O = DigiCert Inc, CN = GeoTrust TLS RSA CA G1<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Wenn du schon dabei bist, kannst du auch gleich nachsehen, was die Gegenstelle \u00fcberhaupt anbietet. Das geht wie bei jedem anderen TLS-Dienst:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">nmap -Pn -p 5061 --script ssl-enum-ciphers sip.sipgate.de<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">TLSv1.2:\n  TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A\n  TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A\n  ...\nTLSv1.3:\n  TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A\n  TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A\n  TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A\ncipher preference: server\nleast strength: A<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Das Ergebnis ist erfreulich unaufgeregt. Es gibt <strong>nur TLS 1.2 und 1.3<\/strong>, die alten Versionen habe ich gegengepr\u00fcft und beide werden abgelehnt. Alles bewertet mit A, der Server bestimmt die Reihenfolge, und das Zertifikat ist RSA mit 4096 Bit. Ausgehandelt wird in der Praxis TLS 1.3 mit <code>TLS_AES_256_GCM_SHA384<\/code> \u00fcber X25519.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Sch\u00f6nheitsfehler bleibt: unter TLS 1.2 stehen auch noch reine <code>TLS_RSA_WITH_*<\/code>-Suiten in der Liste, also solche ohne Forward Secrecy. Weil der Server die Reihenfolge vorgibt und ECDHE oben steht, bekommt sie in der Praxis niemand, sie sind der R\u00fcckfall f\u00fcr Uraltger\u00e4te. Sch\u00f6ner w\u00e4re es trotzdem ohne.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Bleibt die Sprache. W\u00e4hrend des Gespr\u00e4chs zeigt GOnnect oben links ein gr\u00fcnes Schild, das ist die Verschl\u00fcsselungsanzeige.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"501\" src=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung-1024x501.png\" alt=\"Ein laufendes Gespr\u00e4ch in GOnnect, oben links ein gr\u00fcnes Schild als Anzeige f\u00fcr die Verschl\u00fcsselung.\" class=\"wp-image-5213\" srcset=\"https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung-1024x501.png 1024w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung-300x147.png 300w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung-768x376.png 768w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung-676x331.png 676w, https:\/\/www.kernel-error.de\/wp-content\/uploads\/2026\/08\/gonnect-gespraech-verschluesselung.png 1481w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Das gr\u00fcne Schild oben links ist die Verschl\u00fcsselungsanzeige. Was dahinter wirklich passiert, steht erst im Protokoll.<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Ein Symbol ist aber nur ein Symbol. Was dahinter passiert, protokolliert der Client erst, wenn du ihn darum bittest:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[logging]\nlevel=6<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Danach einmal telefonieren und ins Protokoll schauen. Was du sehen willst, sind zwei Dinge. Erstens die Aushandlung, in der beide Seiten <code>RTP\/SAVP<\/code> sprechen statt <code>RTP\/AVP<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">m=audio 4000 RTP\/SAVP 96 97 98 99 3 0 8 9 100 102 103 101 104 105 106\na=crypto:1 AES_256_CM_HMAC_SHA1_80 inline:S3ybiZ92TCP4JuPe48V...\na=crypto:3 AES_CM_128_HMAC_SHA1_80 inline:r2RXvwQ31ewSR0xEvuZX...<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und zweitens die Best\u00e4tigung, welche der angebotenen Suiten die Gegenstelle genommen hat:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">srtp0x...  SRTP started, keying=SDES, crypto=AES_256_CM_HMAC_SHA1_80\n           SRTP status: Active  Crypto-suite: AES_256_CM_HMAC_SHA1_80<\/pre>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">sipgate hat also die st\u00e4rkste der vier angebotenen Varianten gew\u00e4hlt. Erst damit wei\u00df ich, dass <code>srtpUse=mandatory<\/code> wirklich gegriffen hat und nicht still auf \u201eaus&#8220; stand. Nebenbei: mehr als diese vier hat der Client gar nicht im Angebot, alle mit AES im Counter-Modus und HMAC-SHA1. AES-GCM kennt diese PJSIP-Variante nicht, an der Stelle ist also nichts zu optimieren.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Und dann dreh den Loglevel wieder runter.<\/strong> In den Zeilen oben stehen die SRTP-Schl\u00fcssel im Klartext, die haben in einer Protokolldatei nichts verloren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Was dabei nicht verschl\u00fcsselt ist<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Hier muss ich die gute Laune etwas bremsen, denn \u201ealles verschl\u00fcsselt&#8220; w\u00e4re falsch.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Verschl\u00fcsselt ist die Strecke zwischen deinem Rechner und sipgate. Das ist viel wert, es sch\u00fctzt gegen dein WLAN, gegen fremde Netze und gegen deinen Provider, und zwar sowohl den Gespr\u00e4chsinhalt als auch die Information, wen du wann anrufst.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Es endet aber bei sipgate. Rufst du ein Mobiltelefon an, l\u00e4uft der Anruf ab dort durch das normale Telefonnetz, und dort gibt es keine Verschl\u00fcsselung. Dazu kommt, dass SDES als Schl\u00fcsselaustausch bedeutet, dass die Schl\u00fcssel im Signalisierungsweg stehen, den sipgate liest. Der ist zwar durch TLS gegen Dritte gesch\u00fctzt, aber sipgate kennt deine Schl\u00fcssel und k\u00f6nnte mith\u00f6ren. Etwas anderes steht auch gar nicht zur Wahl: ZRTP und DTLS-SRTP kommen in der gesamten sipgate-Dokumentation kein einziges Mal vor.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Echte Ende-zu-Ende-Verschl\u00fcsselung bekommst du nur zwischen zwei Clients, die direkt miteinander eines von beidem sprechen. Mit einem Anbieter dazwischen ist das, was hier l\u00e4uft, die Obergrenze. Das ist kein Grund, es zu lassen, man sollte es nur nicht mit etwas verwechseln, das es nicht ist.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Kleinkram, der auff\u00e4llt<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Zwei Meldungen im Protokoll haben mich kurz beunruhigt und sind harmlos. <code>Failed to connect to pipewire instance<\/code> erscheint, weil das Flatpak den PulseAudio-Socket hat und auf <code>pipewire-pulse<\/code> zur\u00fcckf\u00e4llt. Die Ger\u00e4te werden danach sauber gesetzt, der Ton funktioniert. Die Fehler des <code>JitsiConnector<\/code> betreffen die Videokonferenz-Anbindung und nicht das Telefonieren.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Was tats\u00e4chlich nicht geht: globale Tastenk\u00fcrzel. Cinnamon bringt das Portal <code>org.freedesktop.portal.GlobalShortcuts<\/code> nicht mit, Anrufe per Tastendruck annehmen f\u00e4llt damit aus. Alles andere, auch das Symbol im Systemabschnitt der Kontrollleiste, funktioniert.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-small-font-size\">Fazit<\/h3>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Der Teil, den ich f\u00fcr den schwierigen hielt, war der einfachste. Ich musste am Softphone \u00fcberhaupt kein CardDAV einrichten, weil evolution-data-server das l\u00e4ngst erledigt hatte. Ein Konto in den Online-Konten, und die Kontakte und Kalender stehen jedem Programm auf dem Rechner zur Verf\u00fcgung.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Aufwendig war stattdessen, \u00fcberhaupt einen Client zu finden, der unter Linux gepflegt wird und den man ohne AppImage-Gefummel aktuell h\u00e4lt. Dass ausgerechnet ein Client, der eigentlich f\u00fcr den Rollout in Firmen gedacht ist und deshalb keinen Einrichtungsdialog hat, f\u00fcr einen einzelnen Basis-Account die beste Wahl ist, h\u00e4tte ich vorher nicht erwartet.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Und der Familienkalender, der 2014 in die ownCloud gewandert ist, steht jetzt neben dem Telefon. Nur zw\u00f6lf Jahre sp\u00e4ter.<\/p>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Siehe auch:<\/p>\n\n\n\n<ul class=\"wp-block-list has-small-font-size\">\n<li><a href=\"https:\/\/www.kernel-error.de\/2014\/01\/30\/owncloud-und-kalender-kontakte-synchronisieren\/\">ownCloud und Kalender \/ Kontakte synchronisieren<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2015\/05\/30\/ihre-01801-rufnummer-wird-abgeschaltet-sipgate\/\">Ihre 01801-Rufnummer wird abgeschaltet, sipgate<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.kernel-error.de\/2013\/11\/04\/davdroid-owncloud-sync\/\">Davdroid ownCloud sync<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"has-small-font-size wp-block-paragraph\">Falls ihr das nachbaut und irgendwo h\u00e4ngen bleibt, oder falls ihr einen besseren Weg kennt, dann d\u00fcrft ihr mich sehr gerne <a href=\"https:\/\/www.kernel-error.de\/kontakt\/\">fragen<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ein Softphone unter Linux, das an meinem sipgate-Konto h\u00e4ngt und beim Klingeln den Namen aus meiner eigenen Nextcloud anzeigt statt einer nackten Rufnummer. Der Weg dahin f\u00fchrt nicht \u00fcber CardDAV im Telefon, sondern \u00fcber den evolution-data-server, und das Softphone merkt davon nichts. Dazu die Frage, was bei so einem Anruf eigentlich verschl\u00fcsselt ist, einmal nachgemessen statt geglaubt.<\/p>\n","protected":false},"author":1,"featured_media":5214,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[13,274,276],"tags":[377,167,36,442,372,467,65,468],"class_list":["post-5215","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kernel-error-blog","category-linux-bsd","category-self-hosting-infrastruktur","tag-encryption","tag-flatpak","tag-linux","tag-nextcloud","tag-selfhosted","tag-sipgate","tag-tls","tag-voip","post-preview"],"_links":{"self":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5215","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/comments?post=5215"}],"version-history":[{"count":1,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5215\/revisions"}],"predecessor-version":[{"id":5216,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/posts\/5215\/revisions\/5216"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media\/5214"}],"wp:attachment":[{"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/media?parent=5215"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/categories?post=5215"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kernel-error.de\/wp-json\/wp\/v2\/tags?post=5215"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}]