Datenhaufen zu IT und Elektronik.

Kategorie: Netzwerke & Protokolle (Seite 1 von 5)

SSH-Bruteforce, DigitalOcean und AbuseIPDB – warum Blocken das Problem nicht löst

Aus gegebenem Anlass möchte ich ein paar Gedanken zu DigitalOcean aufschreiben. Nicht, weil ich glaube, dass DigitalOcean ein grundsätzliches Problem hat oder etwas falsch macht. Sondern weil DigitalOcean in meinen Logs seit Jahren besonders auffällt. Am Ende steht DigitalOcean hier eher sinnbildlich für ein größeres Thema.

Wer Systeme im Internet betreibt, kennt das Spiel. Server werden dauerhaft von außen angefasst. SSH-Ports werden gescannt, Login-Versuche laufen durch, Webseiten bekommen Requests auf bekannte Pfade, WordPress-Logins, XML-RPC, das volle Programm. Das ist kein gezielter Angriff, sondern automatisiertes Dauerrauschen. Bots, Skripte, Scanner, manchmal Security-Tools, manchmal schlicht schlecht konfigurierte Kisten.

Darstellung von automatisierten SSH-Bruteforce-Angriffen und Server-Härtung in Cloud-Umgebungen

Findet so ein Bot irgendwo ein offenes Loch, einen Standard-Login, ein vergessenes Passwort oder eine ungepatchte Schwachstelle, dann geht es weiter. Meistens wird erst einmal weitere Software nachgeladen. Der Host wird Teil eines Botnetzes, scannt selbst weiter, verteilt Spam, nimmt an DDoS-Aktionen teil oder schürft Kryptowährungen. Nichts davon ist neu, nichts davon ist überraschend.

Was mir allerdings seit mindestens vier Jahren auffällt: Ein sehr großer Teil dieser Brute-Force-Versuche, insbesondere auf SSH, kommt bei mir aus Netzen von DigitalOcean. Nicht ein bisschen mehr, sondern konstant irgendwo im Bereich von achtzig bis neunzig Prozent. Über Jahre. Über verschiedene Systeme hinweg.

Der erste Reflex liegt nahe. Wenn so viel aus einem Netz kommt, warum blockt man dann nicht einfach alle Netze dieses Anbieters? Dann ist Ruhe. Und wenn das alle machen würden, müsste der Anbieter ja reagieren. Der Gedanke ist verständlich. Ich hatte ihn selbst. Er ist aber aus meiner Sicht der falsche.

Ein pauschales Blocken ist im Grunde nichts anderes als eine Decke über das eigentliche Problem zu werfen. Das Problem ist damit nicht weg, es ist nur woanders. Die Bots wechseln dann eben zum nächsten Cloud-Provider. Außerdem produziert man sich damit ganz eigene Probleme. DigitalOcean-Netze komplett zu sperren heißt auch, legitimen Traffic auszusperren. APIs, Dienste, Kunden, Monitoring, externe Abhängigkeiten. Je nach Setup schneidet man sich damit schneller ins eigene Fleisch, als einem lieb ist.

Relativ schnell landet man dann bei Reputation-Diensten wie AbuseIPDB. Dort melden Betreiber IPs, von denen Scans, Brute-Force-Versuche oder andere Auffälligkeiten ausgehen. Auch ich melde dort seit Jahren IPs, automatisiert und manuell. Formal funktioniert das gut. IPs bekommen Scores, werden gelistet, tauchen in Datenbanken auf.

Das Problem ist nur: Diese Systeme arbeiten IP-basiert. Und genau das passt schlecht zur Realität moderner Netze. In Cloud-Umgebungen sind IPs kurzlebig. Heute gehört sie einem kompromittierten Host, morgen einem völlig legitimen Kunden. Ein hoher Abuse-Score sagt wenig über den aktuellen Nutzer dieser IP aus. Reputation ist träge, Infrastruktur ist schnell.

Jan  6 22:58:08 honeypot03 sshd-session[61904]: Invalid user sonar from 64.23.228.101 port 38610
Jan  6 22:58:08 honeypot03 sshd-session[61904]: Connection closed by invalid user sonar 64.23.228.101 port 38610 [preauth]
Jan  6 23:02:13 honeypot03 sshd-session[62101]: Invalid user sonar from 64.23.228.101 port 38174
Jan  6 23:02:13 honeypot03 sshd-session[62101]: Connection closed by invalid user sonar 64.23.228.101 port 38174 [preauth]
Jan  6 23:06:12 honeypot03 sshd-session[62175]: Invalid user sonar from 64.23.228.101 port 35952
Jan  6 23:06:12 honeypot03 sshd-session[62175]: Connection closed by invalid user sonar 64.23.228.101 port 35952 [preauth]
Jan  6 23:10:10 honeypot03 sshd-session[62248]: Invalid user steam from 64.23.228.101 port 38236
Jan  6 23:10:10 honeypot03 sshd-session[62248]: Connection closed by invalid user steam 64.23.228.101 port 38236 [preauth]
Jan  6 23:14:17 honeypot03 sshd-session[62335]: Invalid user steam from 64.23.228.101 port 35952
Jan  6 23:14:18 honeypot03 sshd-session[62335]: Connection closed by invalid user steam 64.23.228.101 port 35952 [preauth]
Jan  6 23:18:22 honeypot03 sshd-session[62455]: Invalid user steam from 64.23.228.101 port 50096
Jan  6 23:18:22 honeypot03 sshd-session[62455]: Connection closed by invalid user steam 64.23.228.101 port 50096 [preauth]
Jan  6 23:22:24 honeypot03 sshd-session[62599]: Invalid user sugi from 64.23.228.101 port 53212
Jan  6 23:22:25 honeypot03 sshd-session[62599]: Connection closed by invalid user sugi 64.23.228.101 port 53212 [preauth]
Jan  6 23:26:26 honeypot03 sshd-session[62671]: Invalid user svnuser from 64.23.228.101 port 44820
Jan  6 23:26:26 honeypot03 sshd-session[62671]: Connection closed by invalid user svnuser 64.23.228.101 port 44820 [preauth]
Jan  6 23:30:26 honeypot03 sshd-session[62763]: Invalid user svnuser from 64.23.228.101 port 52156
Jan  6 23:30:27 honeypot03 sshd-session[62763]: Connection closed by invalid user svnuser 64.23.228.101 port 52156 [preauth]
Jan  6 23:34:30 honeypot03 sshd-session[62867]: Invalid user taryn from 64.23.228.101 port 54128
Jan  6 23:34:31 honeypot03 sshd-session[62867]: Connection closed by invalid user taryn 64.23.228.101 port 54128 [preauth]
Jan  6 23:38:41 honeypot03 sshd-session[62939]: Invalid user taryn from 64.23.228.101 port 39894
Jan  6 23:38:42 honeypot03 sshd-session[62939]: Connection closed by invalid user taryn 64.23.228.101 port 39894 [preauth]
Jan  6 23:42:44 honeypot03 sshd-session[63013]: Invalid user taryn from 64.23.228.101 port 57728
Jan  6 23:42:45 honeypot03 sshd-session[63013]: Connection closed by invalid user taryn 64.23.228.101 port 57728 [preauth]
Jan  6 23:46:45 honeypot03 sshd-session[63160]: Invalid user taryn from 64.23.228.101 port 38438
Jan  6 23:46:45 honeypot03 sshd-session[63160]: Connection closed by invalid user taryn 64.23.228.101 port 38438 [preauth]
Jan  6 23:50:49 honeypot03 sshd-session[63252]: Invalid user taryn from 64.23.228.101 port 54070
Jan  6 23:50:49 honeypot03 sshd-session[63252]: Connection closed by invalid user taryn 64.23.228.101 port 54070 [preauth]
Jan  6 23:54:55 honeypot03 sshd-session[63354]: Invalid user terrance from 64.23.228.101 port 57960
Jan  6 23:54:55 honeypot03 sshd-session[63354]: Connection closed by invalid user terrance 64.23.228.101 port 57960 [preauth]
Jan  6 23:59:05 honeypot03 sshd-session[63472]: Invalid user terrance from 64.23.228.101 port 47558
Jan  6 23:59:05 honeypot03 sshd-session[63472]: Connection closed by invalid user terrance 64.23.228.101 port 47558 [preauth]
Jan  7 00:03:11 honeypot03 sshd-session[64731]: Invalid user terrance from 64.23.228.101 port 42938
Jan  7 00:03:11 honeypot03 sshd-session[64731]: Connection closed by invalid user terrance 64.23.228.101 port 42938 [preauth]

Damit erklärt sich auch, warum Provider solche externen Feeds nicht einfach hart umsetzen. Würde man IPs automatisiert abschalten, nur weil sie in einer Datenbank schlecht bewertet sind, träfe man regelmäßig Unbeteiligte. False Positives wären vorprogrammiert. Rechtlich, operativ und wirtschaftlich ist das für Provider kaum tragbar.

Warum also fällt DigitalOcean so stark auf? Das kann ich nicht belegen, nur einordnen. DigitalOcean ist günstig, schnell, einfach. In wenigen Minuten hat man dort eine VM mit öffentlicher IP. Das ist für legitime Nutzer attraktiv, aber eben auch für Leute mit schlechten Absichten. Wenn Infrastruktur billig und niedrigschwellig ist, taucht sie zwangsläufig häufiger in Logs auf. Dazu kommt, dass viele Systeme dort von Menschen betrieben werden, die vielleicht noch nicht so tief im Thema Security stecken. Offene Dienste, schwache Konfigurationen, fehlendes Hardening – all das macht solche Hosts wiederum zu guten Kandidaten für Kompromittierung und Weiterverwendung.

Wichtig dabei: DigitalOcean selbst macht aus meiner Sicht nichts grundlegend falsch. Der Abuse-Prozess funktioniert. Meldungen lassen sich automatisiert einreichen, werden angenommen, werden beantwortet, werden bearbeitet. Ich habe das über Jahre hinweg genutzt, sowohl manuell als auch automatisiert. Das ist sauber umgesetzt.

Was sich dadurch aber nicht ändert, ist die Menge der Versuche. Die wird nicht weniger. Sie bleibt konstant. Einzelne Hosts verschwinden, neue tauchen auf. Abuse-Meldungen – egal ob direkt beim Provider oder über Plattformen wie AbuseIPDB – wirken immer nur lokal und zeitverzögert. Gegen ein strukturelles Phänomen kommen sie nicht an.

Aus Sicht eines Providers ist das auch logisch. Ein paar tausend fehlgeschlagene SSH-Logins sind kein Incident. Kein DDoS, kein Ausfall, kein messbarer Schaden. Das fällt unter Hintergrundrauschen. Niemand bezahlt dafür, dieses Rauschen global zu eliminieren. Und ehrlich gesagt: Das kann auch niemand realistisch leisten.

Die eigentliche Konsequenz daraus ist unbequem, aber klar. Man darf nicht erwarten, dass Provider oder Reputation-Datenbanken einem dieses Problem abnehmen. Scan- und Brute-Force-Traffic gehört heute zum Betrieb eines öffentlich erreichbaren Systems dazu. Die einzige Stelle, an der man sinnvoll ansetzen kann, ist das eigene Setup.

Saubere Konfiguration. Keine Passwort-Logins per SSH. Kein Root-Login. Rate-Limits. Monitoring, das zwischen Rauschen und echten Zustandsänderungen unterscheidet. Fail2Ban als Dämpfer, nicht als Illusion von Sicherheit. Und vor allem: Gelassenheit gegenüber Logs, die voll sind, aber nichts bedeuten.

DigitalOcean ist hier nicht der Feind. AbuseIPDB ist kein Allheilmittel. Beides sind sichtbare Teile eines größeren Bildes. Das eigentliche Thema ist, wie man Systeme so betreibt, dass dieses Hintergrundrauschen irrelevant wird.

BIND auf FreeBSD: DoT & DoH einrichten mit Views, IP‑Trennung und Testplan für IPv4/IPv6.

Wofür braucht man noch gleich DoT oder DoH?

Nun, wenn du eine Internetadresse eingibst, muss dein Gerät zuerst herausfinden, zu welchem Server diese Adresse gehört. Diese Nachfragen heißen DNS. Lange Zeit liefen sie unverschlüsselt durchs Netz, vergleichbar mit einer Postkarte. Jeder, der den Datenverkehr sehen konnte, wusste dadurch sehr genau, welche Webseiten aufgerufen werden, und konnte die Antworten sogar manipulieren.

Beitragsgrafik zu BIND 9.20 auf FreeBSD 15: schematische Trennung von autoritativem DNS und rekursivem Resolver. Links ein Authoritative-DNS-Server mit deaktivierter Rekursion und blockiertem UDP/53, rechts ein Resolver, der ausschließlich DNS over TLS (Port 853) und DNS over HTTPS (Port 443) anbietet. In der Mitte ein Schild mit DoT/DoH-Symbolen, Pfeile zeigen verschlüsselten DNS-Verkehr. Fokus auf Sicherheits- und Rollen-Trennung.

DoT und DoH lösen genau dieses Problem. Beide sorgen dafür, dass diese DNS-Nachfragen verschlüsselt übertragen werden. Bei DNS over TLS, kurz DoT, wird die Anfrage in eine eigene sichere Verbindung gepackt. Außenstehende sehen noch, dass eine DNS-Anfrage stattfindet, aber nicht mehr, welche Webseite gemeint ist. Bei DNS over HTTPS, kurz DoH, wird dieselbe Anfrage zusätzlich im normalen Webseitenverkehr versteckt. Von außen sieht sie aus wie ein ganz gewöhnlicher Zugriff auf eine Website.

Der Zweck von beiden ist also derselbe: Schutz der Privatsphäre und Schutz vor Manipulation. Der Unterschied liegt darin, wie sichtbar diese Nachfragen noch sind. DoT ist transparent und gut kontrollierbar, DoH ist unauffälliger, kann dafür aber lokale Regeln und Schutzmechanismen umgehen.

Mal angenommen, du möchtest eine gewisse Webseite aufrufen. Dann geht der Client los und holt über einen DNS-Server die IP-Adressen vom Server. Dies kann man mitlesen und ggf. verändern. Mitlesen sagt dem Mitlesenden, wo du dich so im Internet herumtreibst. Verändern könnte man als Angriff nutzen, indem man dir einfach eine andere Webseite vorsetzt, während du versuchst, dich in deinen Mailaccount einzuloggen. Beides wird durch DoH und DoT deutlich erschwert.

Dann soll es ja Netzwerke geben, in welchen dir ein bestimmter DNS-Server aufgezwungen wird, weil dieser DNS-Server nach Werbung oder ungewollten Inhalten filtert. Damit dies nun ebenfalls nicht einfach umgangen werden kann, blockt man den Zugriff aus dem Netzwerk einfach auf die Ports, welche sonst für eine DNS-Abfrage benutzt werden (TCP/53, UDP/53, TCP/853). Da kommt nun DoH ins Spiel, denn das läuft auf dem ganz normalen HTTPS-Port TCP/443. Blockt man den, kann keiner mehr auf Webseiten zugreifen (ok, unverschlüsselt, aber hey, das macht doch keiner mehr, oder?).

Die Zeit ging weiter – BIND auch.
Meine älteren Artikel zu DoT/DoH waren für ihren Zeitpunkt korrekt, aber inzwischen hat sich an zwei Stellen richtig was getan:

  1. BIND spricht DoT/DoH nativ (kein Stunnel-/Proxy-Zirkus mehr nötig – außer du willst bewusst terminieren/filtern).
  2. „Authoritative + Public Resolver auf derselben Kiste“ ist ohne klare Trennung schnell ein Sicherheitsproblem (Open-Resolver/Reflection-Missbrauch lässt grüßen).

Darum gibt’s hier das Update:

  • ns1.kernel-error.de: nur autoritativ auf UDP/TCP 53 (Zonen, DNSSEC wie gehabt)
  • dns.kernel-error.de: Public Resolver nur auf DoT 853/TCP und DoH 443/TCP (rekursiv, DNSSEC-validierend)
  • Trennung über zusätzliche IPs + Views. Ergebnis: Authoritative bleibt „stumm rekursiv“, Resolver ist nur über TLS/HTTPS erreichbar.

Zielbild

Uff, ich muss zugeben, diesen Beitrag schon VIEL zu lange als Draft zu haben. Es ist einfach viel zu schreiben, bschreiben und mir fehlte die Zeit. Aber das kennt ihr ja. OK… das Zielbild, was soll es werden?

Was soll am Ende gelten:

  • Port 53 auf Authoritative-IP(s):
    • beantwortet nur meine autoritativen Zonen
    • keine Rekursion → REFUSED bei google.com
  • DoT/DoH auf separaten Resolver-IP(s):
    • rekursiv für „das ganze Internet“
    • DNSSEC-Validation aktiv
    • kein offenes UDP/53 → weniger Angriffsfläche für Reflection/Amplification

Warum das wichtig ist:
Ein „Public Resolver“ ist per Definition attraktiv für Missbrauch. Der Klassiker ist DNS-Amplification über UDP/53. Wenn man Rekursion auf 53 offen hat, ist man sehr schnell Teil fremder Probleme. DoT/DoH sind TCP-basiert – das ist schon mal deutlich unattraktiver für Reflection. (Nicht „unmöglich“, aber praktisch viel weniger lohnend.)

Warum „Views“ – und warum zusätzliche IPs?

1) Views – weil Policy pro Anfrage gelten muss

Wir wollen auf derselben named-Instanz zwei sehr unterschiedliche Rollen:

  • Authoritative: recursion no;
  • Resolver: recursion yes; + Root-Hints/Cache

Das muss pro eingehender Anfrage entschieden werden. Dafür sind Views da.

2) Also: Trennung über Ziel-IP (match-destinations)

Wenn wir DoH/DoT auf andere IPs legen, kann die View anhand der Zieladresse entscheiden:

  • Anfrage geht an 93.177.67.26 / 2a03:4000:38:20e::53auth-View
  • Anfrage geht an 37.120.183.220 / 2a03:4000:38:20e::853resolver-View

Und genau deshalb brauchen wir:

  • zusätzliche IPs (damit die Rollen sauber getrennt sind)
  • separaten FQDN dns.kernel-error.de (damit Clients überhaupt sinnvoll DoT/DoH nutzen können – und für TLS/SNI/Cert-Match)

Wenn du also grade ein ripe from ausfüllst und angeben musst, warum da eine weitere IPv4 Adresse „verbrannt“ werden soll, hast du nun eine gute Antwort 😀

BIND-Config

Ich beschreibe hier nur die Teile, die für das Rollen-Split relevant sind. Die Zonendateien/Slaves bleiben wie sie sind.

1) /usr/local/etc/namedb/named.conf – Views

Wichtig: Sobald wir view {} nutzen, müssen alle Zonen in Views liegen, sonst bricht named-checkconf ab. Das ist kein „Feature“, das ist BIND. Leicht nervig, vor allem wenn man nun viel in seinem Setup umschreiben muss. Aber ich eigentlich schon mal erwähnt, dass ich auf der Arbeit mal einen, nennen wir es mal View Ersatz, für powerdns gesehen habe? Da hat tatsächlich jemand mit einer Cisco ASA in die DNS Pakete geschaut und je nachdem welche quelle angefragt hat, wurde dann durch die ASA eine neue Adresse in die DNS Pakete geschrieben. Furchtbar! Richtig schlimm. Bis man so etwas findet, wenn man es nicht weiß. DNSsec geht kaputt und aaahhhhhhaaaaaahhhhh. Egal, mein PTBS kickt da grade. Öhm wo waren wir? Genau…

Beispiel:

include "/usr/local/etc/namedb/named.conf.options";

view "auth" {
    match-clients { any; };
    match-destinations { 93.177.67.26; 2a03:4000:38:20e::53; };

    recursion no;
    allow-recursion { none; };
    allow-query-cache { none; };
    allow-query { any; };

    include "/usr/local/etc/namedb/named.conf.default-zones";
    include "/usr/local/etc/namedb/named.conf.master";
    include "/usr/local/etc/namedb/named.conf.slave";
};

view "resolver" {
    match-clients { any; };
    match-destinations { 37.120.183.220; 2a03:4000:38:20e::853; 127.0.0.1; ::1; };

    recursion yes;
    allow-recursion { any; };
    allow-query-cache { any; };
    allow-query { any; };

    zone "." { type hint; file "/usr/local/etc/namedb/named.root"; };
};

Warum Root-Hints nur im Resolver-View?
Weil nur dieser View rekursiv arbeiten soll. Ohne Root-Hints ist Rekursion tot; dat wolln wa so!

2) /usr/local/etc/namedb/named.conf.options – Listener-Trennung + DoH/DoT

Der „Aha-Moment“ hier: Wir trennen nicht nur per View, sondern auch per listen-on.
Damit bindet named die Ports wirklich nur auf den gewünschten IPs.

Authoritative (nur 53):

listen-on { 93.177.67.26; 127.0.0.1; };
listen-on-v6 { 2a03:4000:38:20e::53; ::1; };

DoT auf Resolver-IPs (+ Loopback für lokale Tests):

listen-on port 853 tls local-tls { 37.120.183.220; 127.0.0.1; };
listen-on-v6 port 853 tls local-tls { 2a03:4000:38:20e::853; ::1; };

DoH auf Resolver-IPs (+ Loopback):
BIND 9.18+ kann DoH nativ, Endpoint typischerweise /dns-query

http doh-local {
    endpoints { "/dns-query"; };
    listener-clients 1000;
    streams-per-connection 256;
};

listen-on port 443 tls local-tls http doh-local { 37.120.183.220; 127.0.0.1; };
listen-on-v6 port 443 tls local-tls http doh-local { 2a03:4000:38:20e::853; ::1; };

TLS-Block (DoT/DoH):

tls local-tls {
    cert-file "/usr/local/etc/nginx/ssl/wild.kernel-error.de/2025/ecp/chain.crt";
    key-file "/usr/local/etc/nginx/ssl/wild.kernel-error.de/2025/ecp/http.key";
    protocols { TLSv1.2; TLSv1.3; };
    ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256";
    cipher-suites "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256";
    prefer-server-ciphers yes;
    session-tickets no;
};

„Ich schalte nginx davor – muss BIND TLS können?“
Wenn nginx wirklich TLS terminiert, kann BIND auch ohne TLS dahinter laufen – dann sprichst du intern HTTP/2 cleartext oder HTTP/1.1, je nach Setup. Das habe ich ebenfalls so umgesetzt, es hängt immer etwas davon ab, was man so will und wie groß das Setup wird. Ich lasse es in diesem Beitrag aber mal weg, so läuft alles nur mit bind. Ob BIND dafür „tls none“/HTTP-Listener sauber unterstützt, hängt an der BIND-DoH-Implementierung – hier ist die BIND/ARM-Doku die Wahrheit. bind9.readthedocs.io+1

Testplan – Linux-CLI – bewusst IPv4 und IPv6

Wir wollen natürlich einmal reproduzierbar testen. Also: jede Stufe zweimal. Einmal -4, einmal -6. Also ob es bei IPv4 und bei IPv6 jeweils korrekt ist. Ihr könnt euch nicht vorstellen, wie oft ich fest davon überzeugt bin, es für beide Adressfamilien korrekt konfiguriert zu haben, dann aber noch ein unterschied zwischen v4 und v6 ist. Daher testen wir das.

Voraussetzungen auf Linux

which dig kdig curl openssl

Schritt 1 – DoT-TLS-Handshake prüfen (IPv4/IPv6)

IPv4

openssl s_client \
  -connect 37.120.183.220:853 \
  -servername dns.kernel-error.de \
  -alpn dot

Erwartung:

  • Zertifikat passt auf dns.kernel-error.de (SAN / Wildcard ok)
  • ALPN protocol: dot
  • Verify return code: 0 (ok)

IPv6

openssl s_client \
  -connect '[2a03:4000:38:20e::853]:853' \
  -servername dns.kernel-error.de \
  -alpn dot

Wenn das passt, ist TLS-Transport ok. Also nur die TLS Terminierung für IPv4 und IPv6, da war noch keine DNS Abfrage enthalten.

Schritt 2 – DoT-Query (kdig) – IPv4/IPv6

IPv4

kdig +tls @37.120.183.220 google.com A

Erwartung:

  • status: NOERROR
  • Flags: rd ra (Recursion Desired/Available)
  • eine A-Antwort

IPv6

kdig +tls @[2a03:4000:38:20e::853] google.com A

Gleiche Erwartungshaltung wie bei IPv4.

Schritt 3 – Sicherstellen: kein Resolver auf UDP/TCP 53

Resolver-IPs dürfen auf 53 nicht antworten

dig -4 @37.120.183.220 google.com A
dig -6 @2a03:4000:38:20e::853 google.com A

Erwartung:

  • Timeout / no servers reached
    Genau das wollen wir ja: kein UDP/53 auf den Resolver-IPs.

Authoritative-IPs dürfen nicht rekursiv sein

dig -4 @93.177.67.26 google.com A
dig -6 @2a03:4000:38:20e::53 google.com A

Erwartung:

  • status: REFUSED
  • idealerweise EDE: (recursion disabled)
    Das ist genau die „nicht missbrauchbar als Open-Resolver“-Bremse.

Und unser positiver Check:

dig -4 @93.177.67.26 kernel-error.de A
dig -6 @2a03:4000:38:20e::53 kernel-error.de A

Erwartung:

  • aa gesetzt (authoritative answer)
  • Antwort aus meiner Zone

Schritt 4 – DoH GET (Base64url) – IPv4/IPv6

4.1 Query bauen (DNS-Wireformat → base64url)

Beispiel google.com A:

echo -n -e '\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01' \
| base64 -w0 | tr '+/' '-_' | tr -d '='

Das Ergebnis ist mein dns= Parameter (base64url ohne = padding). Das ist DoH-Standard nach RFC 8484.

4.2 DoH GET erzwingen – IPv4

curl -4 --http2 -s \
'https://dns.kernel-error.de/dns-query?dns=<DEIN_DNS_PARAM>' \
| hexdump -C

IPv6

curl -6 --http2 -s \
'https://dns.kernel-error.de/dns-query?dns=<DEIN_DNS_PARAM>' \
| hexdump -C

Erwartung:

  • HTTP/2 200
  • content-type: application/dns-message
  • Im Hexdump siehst du eine valide DNS-Response.

Schritt 5 – DoH POST (application/dns-message) – IPv4/IPv6

Das ist der „richtige“ DoH-Weg für Tools/Clients.

IPv4

printf '\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01' \
| curl -4 --http2 -s \
  -H 'content-type: application/dns-message' \
  --data-binary @- \
  https://dns.kernel-error.de/dns-query \
| hexdump -C

IPv6

printf '\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01' \
| curl -6 --http2 -s \
  -H 'content-type: application/dns-message' \
  --data-binary @- \
  https://dns.kernel-error.de/dns-query \
| hexdump -C

Erwartung:

  • DNS-Response im Wireformat
  • keine HTML-Antwort, kein Redirect-Quatsch

Was wir damit jetzt sicher(er) gelöst haben:

  • Kein Open-Resolver auf UDP/53 → massiver Gewinn gegen DNS-Amplification.
  • Authoritative bleibt Authoritative → Zonen-Betrieb unverändert stabil.
  • Resolver nur über DoT/DoH → TCP/TLS-Transport, weniger Missbrauchsfläche.
  • Saubere technische Trennung → Views per Ziel-IP sind simpel, robust, nachvollziehbar.

Und ja: „Public Resolver“ heißt trotzdem Monitoring/Rate-Limiting/Abuse-Handling.
Das Feintuning (RRL, QPS-Limits, minimal-responses, Response-Policy, ggf. ECS-Handling, Logging, Fail2ban-Signale) ist das nächste Kapitel. Wobei, wenn ich grade auf die TLS Parameter schaue, sollte ich da vielleicht noch mal nacharbeiten, hm?

Wenn ihr noch eine kleine liste von erreichbaren Servern sucht: GitHub-curl-wiki

Alles hilft natürlich nicht, wenn man am Ende doch komplett IP- oder Hostnamebasiert geblockt wird. In China ist da nicht viel zu holen und auch hier gibt es immer mal wieder etwas.


Japp… TLS geht besser. Im Beitrag habe ich es oben schon angepasst, es war:

tls local-tls {
    cert-file "/pfad/chain.crt";
    key-file  "/pfad/http.key";
    dhparam-file "/pfad/dhparam.pem";
    protocols { TLSv1.2; TLSv1.3; };
    ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256";
    prefer-server-ciphers yes;
    session-tickets no;
};
  • dhparam-file ist komplett raus weil, ja weil es nicht benutzt wird ich mach ja kein DHE sondern ECDHE
  • cipher-suites für TLS1.3 waren nicht gesetzt.
  • Dann konnten auch gleich die Cipher aufgeräumt werden.

Hey, da hat es sich doch gelohnt, das mal runter zu schreiben. So habe ich es direkt gefunden und nicht erst, weil mich jemand von euch darauf hinweist (macht das aber bitte immer wenn ich hier Mist schreibe) oder es beim nächsten eigenen Audit auffällt.

Quantensichere Kryptografie mit OpenSSH auf FreeBSD 15 richtig konfigurieren

Mein FreeBSD 15 kommt mit OpenSSH 10.0p2 und OpenSSL 3.5.4.
Beide bringen inzwischen das mit, was man aktuell als quantensichere Kryptografie bezeichnet. Oder genauer gesagt das, was wir Stand heute für ausreichend robust gegen zukünftige Quantenangriffe halten.

Illustration zu quantensicherer Kryptografie mit OpenSSH auf FreeBSD 15. Dargestellt sind ein Quantenchip, kryptografische Symbole, ein Server, ein SSH Schlüssel sowie der FreeBSD Daemon als Sinnbild für post-quantum Key Exchange und sichere Serverkommunikation.

Quantensicher? Nein, das hat nichts mit Füßen zu tun, sondern tatsächlich mit den Quanten aus der Physik. Quantencomputer sind eine grundlegend andere Art von Rechnern. Googles aktueller Quantenchip war in diesem Jahr bei bestimmten Physiksimulationen rund 13.000-mal schneller als der derzeit leistungsstärkste klassische Supercomputer. Der chinesische Quantencomputer Jiuzhang wurde bei speziellen Aufgaben sogar als 100 Billionen Mal schneller eingestuft.

Kurz gesagt: Quantencomputer sind bei bestimmten Berechnungen extrem viel schneller als heutige klassische Rechner. Und genau das ist für Kryptografie ein Problem.

Als Vergleich aus der klassischen Welt: Moderne Grafikkarten haben die Zeit zum Knacken von Passwörtern in den letzten Jahren drastisch verkürzt.

  • Nur Zahlen: Ein 12-stelliges Passwort wird praktisch sofort geknackt.
  • Nur Kleinbuchstaben: wenige Wochen bis Monate.
  • Groß- und Kleinschreibung plus Zahlen: etwa 100 bis 300 Jahre.
  • Zusätzlich Sonderzeichen: 2025 noch als sehr sicher einzustufen mit geschätzten 226 bis 3.000 Jahren.

Quantencomputer nutzen spezielle Algorithmen wie den Grover-Algorithmus, der die effektive Sicherheit symmetrischer Verfahren halbiert. Ein ausreichend leistungsfähiger Quantencomputer könnte damit die benötigte Zeit drastisch reduzieren. Was heute Jahrhunderte dauert, könnte theoretisch auf Tage oder Stunden schrumpfen.

Stand 2025 sind solche Systeme zwar real und in der Forschung extrem leistungsfähig, werden aber noch nicht flächendeckend zum Brechen realer Kryptosysteme eingesetzt.

Heißt das also alles entspannt bleiben? Jein.

Verschlüsselte Datenträger lassen sich kopieren und für später weglegen. Gleiches gilt für aufgezeichneten verschlüsselten Netzwerkverkehr. Heute kommt man nicht an die Daten heran, aber es ist absehbar, dass das in Zukunft möglich sein könnte. Genau hier setzt quantensichere Kryptografie an. Ziel ist es, auch aufgezeichnete Daten dauerhaft vertraulich zu halten.

Ein praktisches Beispiel ist der Schlüsselaustausch mlkem768x25519. Wenn ihr diese Seite nicht gerade über Tor lest, ist die Wahrscheinlichkeit hoch, dass euer Browser bereits eine solche hybride, post-quantum-fähige Verbindung nutzt. Im Firefox lässt sich das einfach prüfen über F12, Network, eine Verbindung anklicken, dann Security und dort die Key Exchange Group. Taucht dort mlkem768x25519 auf, ist die Verbindung entsprechend abgesichert. Richtig, auf dem Screenhot seht ihr auch HTTP/3.

Image of mlkem768+x25519 in firefox.

Für diese Webseite ist das nicht zwingend nötig. Für SSH-Verbindungen zu Servern aber unter Umständen schon eher. Deshalb zeige ich hier, wie man einen OpenSSH-Server entsprechend konfiguriert.

Ich beziehe mich dabei bewusst nur auf die Kryptografie. Ein echtes SSH-Hardening umfasst deutlich mehr, darum geht es hier aber nicht.

Die zentrale Konfigurationsdatei ist wie üblich: /etc/ssh/sshd_config

Stand Ende 2025 kann ich folgende Konfiguration empfehlen:

KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com

Die Zeilen werden entweder an die bestehende Konfiguration angehängt oder ersetzen vorhandene Einträge. Da wir nicht einfach blind kopieren wollen, hier kurz die Erklärung.

Schlüsselaustausch:
Bevorzugt werden hybride Verfahren wie mlkem768 kombiniert mit x25519 sowie sntrup761 kombiniert mit x25519. Diese verbinden klassische elliptische Kryptografie mit post-quantum-resistenten Algorithmen. Damit ist die Verbindung sowohl gegen heutige Angreifer als auch gegen zukünftige Store-now-decrypt-later-Szenarien abgesichert. Curve25519 dient als bewährter Fallback. Klassische Diffie-Hellman-Gruppen sind nur aus Kompatibilitätsgründen enthalten.

Verschlüsselung:
Es werden ausschließlich moderne Algorithmen eingesetzt. Primär kommen AEAD-Ciphers wie ChaCha20-Poly1305 und AES-GCM zum Einsatz, die Vertraulichkeit und Integrität gleichzeitig liefern und bekannte Schwächen älterer Modi vermeiden. Ältere Verfahren wie CBC sind bewusst ausgeschlossen.

Integrität:
Zum Einsatz kommen ausschließlich SHA-2-basierte MACs im Encrypt-then-MAC-Modus. Dadurch werden klassische Angriffe auf SSH wie Padding-Oracles und bestimmte Timing-Leaks wirksam verhindert.

Serveridentität:
Als Hostkey-Algorithmus wird Ed25519 verwendet. Optional auch mit Zertifikaten oder hardwaregestützten Security Keys. Das bietet hohe kryptografische Sicherheit bei überschaubarem Verwaltungsaufwand.

Wichtig: Das funktioniert nur, wenn Server und Client diese Algorithmen auch unterstützen. Wer bereits mit SSH-Keys arbeitet, sollte prüfen, dass es sich um Ed25519-Keys handelt. Andernfalls sperrt man sich im Zweifel selbst aus.

Auf dem Server lässt sich die aktive Konfiguration prüfen mit:

sshd -T | grep -Ei 'kexalgorithms|ciphers|macs|hostkeyalgorithms'

Auf dem Client geht es am einfachsten mit:

ssh -Q kex
ssh -Q cipher
ssh -Q mac
ssh -Q key

So sieht man schnell, welche Algorithmen tatsächlich verfügbar sind.

Zur externen Überprüfung der SSH-Konfiguration kann ich außerdem das Tool ssh-audit empfehlen. Aufruf einfach per:

ssh-audit hostname oder IP -p PORT

Das liefert eine brauchbare Einschätzung der aktiven Kryptografie und möglicher Schwachstellen. Oh, wenn ihr schon dabei seit, vergesst nicht:

Hinweis zur Einordnung der Quantensicherheit:
Die hier gezeigte Konfiguration verbessert ausschließlich den Schlüsselaustausch (Key Exchange) durch hybride post-quantum-fähige Verfahren. Hostkeys und Signaturen in OpenSSH basieren weiterhin auf klassischen Algorithmen (z. B. Ed25519 oder ECDSA); standardisierte post-quantum-Signaturalgorithmen sind in OpenSSH aktuell noch nicht implementiert. Es existieren zwar experimentelle Forks (z. B. aus dem Open-Quantum-Safe-Projekt), diese gelten jedoch ausdrücklich nicht als produktionsreif und sind nicht Bestandteil des OpenSSH-Mainlines. Die hier gezeigte Konfiguration ist daher als pragmatischer Übergangsschritt zu verstehen, um „store-now-decrypt-later“-Risiken beim Schlüsselaustausch bereits heute zu reduzieren, ohne auf instabile oder nicht standardisierte Komponenten zu setzen.
Weiterführende Informationen zum aktuellen Stand der post-quantum-Unterstützung in OpenSSH finden sich in der offiziellen Dokumentation: https://www.openssh.com/pq.html

Viel Spaß beim Nachbauen. Und wie immer: bei Fragen, fragen.

IP-Kameras: Risiken, Portfreigaben (RTSP/HTTP) & Checks

Moin, ich mag noch einmal etwas zu IP-Überwachungskameras schreiben. Ihr erinnert euch vielleicht an meinen letzten Beitrag zu diesem Thema KLICK.

article image of ip cameras

Dort habe ich mich speziell auf den RTSP-Port bezogen und auch https://www.shodan.io/ als Beispiel genannt. Shodan scannt unaufhörlich IPv4-Adressen (bei IPv6 wäre ein flächendeckender Scan kaum praktikabel) und stellt seine Ergebnisse öffentlich zur Verfügung. Sicherheitsforscher — aber leider auch Menschen mit schlechten Absichten — bedienen sich solcher Dienste, um Informationen über Systeme hinter einer IPv4-Adresse zu bekommen, ganz ohne selbst groß zu scannen oder zu testen. Grob gesagt: Google für „Hacker“.

Dass IP-Kameras — vor allem günstige oder ältere Modelle — schnell ein Sicherheitsrisiko darstellen, habe ich schon erwähnt; wer das hier liest, weiß es in der Regel auch. Automatische Portfreigaben in Kombination mit solchen Kameras sind oft problematisch. Wenn ich über so etwas stolpere und ohne großen Aufwand den Betreiber ausfindig machen kann, versuche ich jeweils per E-Mail oder einem kurzen Anruf zu warnen. Das stößt mal auf offene Ohren, manchmal wird es komplett ignoriert (manchmal mit Abschalten des öffentlichen Zugriffs, manchmal ohne); selten kommt die Antwort „Anzeige ist raus“.

Kameras filmen oft sensible Bereiche, sowohl innen als auch außen. Das kann viele Probleme mit sich bringen, wenn diese Informationen einfach öffentlich zugänglich sind. Anders als bei Datei-Freigaben scheint bei Kamerastreams noch nicht die nötige Awareness vorhanden zu sein — genau deshalb informiere ich hier und weise darauf hin.

Es ist nicht nur der RTSP-Stream, der sich häufig per UPnP seinen Weg nach draußen „tunnelt“. Oft werden auch per DNAT / Portfreigabe / Portweiterleitung die Webinterfaces der Kameras direkt aus dem Internet erreichbar gemacht. Im schlimmsten Fall kann man also mit dem Browser direkt auf Webinterface und Stream zugreifen. Viele sichern den Zugriff mit der kameraeigenen Anmeldung — das ist schon mal ein Anfang. Leider reicht das nicht immer: Bei manchen Modellen sind Funktionen wie Snapshots oder einzelne JPEG-Endpoints weiterhin ohne Anmeldung erreichbar. Das ist auf den ersten Blick nicht sichtbar — kennt man aber die entsprechende URL, genügt ein Browseraufruf und man sieht wieder alles.

Deshalb gebe ich immer den Rat: Zugriff lieber hinter ein VPN legen und niemals direkt offen ins Internet. Gebt jedem Gerät und jedem Dienst, den ihr aus dem Internet erreichbar macht, mindestens so viel Vertrauen wie eurer Haustür. Und prüft regelmäßig, ob dieses Vertrauen noch gerechtfertigt ist.

Wer selbst prüfen möchte, ob die EIGENE Kamera trotz eingerichteter Anmeldung noch irgendwie ohne Login zugänglich ist, kann mein kurzes Python-Tool nutzen: https://github.com/Kernel-Error/cam_probe

Denkt also bitte einmal darüber nach, ob ihr allem, was ihr direkt mit dem Internet verbunden habt, mindestens das gleiche Vertrauen entgegenbringt wie eurer Haustür oder Wohnungstür. Denkt an die security.txt und daran, dass, wenn sich jemand die Mühe macht, euch über ein solches Problem zu informieren, diese Person damit wahrscheinlich den größten Aufwand und auch das größte Risiko für sich selbst aufnimmt – nur, um euch auf ein Problem hinzuweisen.
Einen solchen Fund zu ignorieren, zu verkaufen oder sonst wie auszunutzen, ist deutlich einfacher, als den Betreiber zu informieren.

Natürlich gibt es auch hier schwarze Schafe, aber die Vertrauenswürdigkeit einer solchen Nachricht lässt sich meist schnell per Google oder auch ChatGPT prüfen.
Frage? Dann fragen. 🙂

GPT in Rspamd aktivieren: so nutze ich das LLM-Signal im Score

Setup: FreeBSD 14.3, Rspamd 3.12.1, Postfix + Dovecot. Ich lasse bei kniffligen Mails zusätzlich ein LLM draufschauen. Wichtig: GPT ist bei mir nur ein weiterer Sensor im ganz normalen Rspamd-Scoring — keine Allzweckwaffe und kein „hartes Urteil“.

Voraussetzungen

  • Rspamd inkl. GPT-Plugin (ab ~3.12.x im Paket; konfiguriert wird in local.d/gpt.conf).
  • API-Zugang (OpenAI-kompatibel oder eigener Endpunkt).
  • Grundverständnis zu Rspamd-Metrics/Actions (Reject/Add-Header/Greylist).

OpenAI API Key erstellen: Melde dich auf der Developer-Plattform an, öffne die Seite API Keys und klicke auf Create new secret key. Lege bei Bedarf Berechtigungen fest oder arbeite mit projektbasierten Keys. Kopiere den Key einmalig und bewahre ihn sicher (root-only) auf – bitte nicht teilen. Nutzung/Kosten siehst du im Usage-Dashboard.

Mein gpt.conf

Ich halte die Konfiguration bewusst nüchtern — genug, um robuste Labels zu bekommen, aber ohne Schnickschnack:

# local.d/gpt.conf (Auszug)
enabled = true;
type = "openai";
model = "gpt-4o-mini";
api_key = "GEHEIMER-KEY";

model_parameters {
  gpt-4o-mini {
    max_tokens = 160;
    temperature = 0.0;
  }
}

timeout = 10s;
allow_ham = true;
allow_passthrough = false;
json = false;
reason_header = "X-GPT-Reason";

input = "text";
min_words = 1;
max_size = 256k;

symbols_to_except {
  RCVD_IN_DNSWL_MED = -0.1;
  RCVD_IN_DNSWL_HI  = -0.1;
  DWL_DNSWL_MED     = -0.1;
  WHITELIST_RECP_ADDR = -0.1;
  GREYLIST = 0; GREYLIST_CHECK = 0; GREYLIST_SAVE = 0;
  RCPT_IN_SPAMTRAP = 0; SPAMTRAP = 0; SPAMTRAP_ADDR = 0;
  RCVD_VIA_SMTP_AUTH = 0; LOCAL_CLIENT = 0; FROM_LOCAL = 0;
}

Was bedeutet das?!

  • model = gpt-4o-mini: flott & günstig, deterministisch per temperature = 0.0.
  • allow_ham = true: GPT darf „HAM“ melden (kleines, positives Signal).
  • allow_passthrough = false: Bei Fehlern (Timeout/API down) keine stillen Freifahrten.
  • reason_header = "X-GPT-Reason": Kurzbegründung landet im Header (s.u. Datenschutz).
  • symbols_to_except: Offensichtliche interne Fälle werden neutralisiert, damit GPT nicht in klaren Situationen wirkt.
  • Limits: min_words = 1, max_size = 256k, timeout = 10s.

Metric/Scoring: drei GPT-Symbole

symbols {
  GPT_SPAM       { weight = 9.0;  group = "gpt"; description = "GPT: classified as SPAM"; }
  GPT_SUSPICIOUS { weight = 4.5;  group = "gpt"; description = "GPT: classified as SUSPICIOUS"; }
  GPT_HAM        { weight = -0.5; group = "gpt"; one_shot = true; description = "GPT: classified as HAM"; }
}

GPT wirkt wie ein starker, aber nicht absoluter Faktor.
SPAM (9.0): kräftiger Zuschlag.
SUSPICIOUS (4.5): sanfter Schubs Richtung Greylist/Review.
HAM (-0.5): kleine Entlastung, einmalig pro Mail.

Warum diese Gewichte?
Die Zahlen habe ich bewusst so gewählt, dass das GPT-Signal stark, aber nie absolut ist. Rspamd summiert Scores, GPT ist also nur ein Faktor:

  • GPT_SPAM = 9.0: genug, um bei Kombination mit klassischen Checks (Bayes, RBL, DMARC) die Add-Header-Schwelle sicher zu reißen, aber unterhalb von reject allein.
  • GPT_SUSPICIOUS = 4.5: halber Wert, schiebt Grauzonen in Richtung Greylist/Review, ohne sofortige Eskalation.
  • GPT_HAM = -0.5: nur eine kleine Entlastung (one_shot). So verhindert man, dass GPT-HAM mehrere Punkte abzieht und Spams „rettet“.

Wie wird die GPT-Gewichtung berechnet?
In den Logs/WebUI taucht das oft so auf: GPT_SPAM(2.10)[0.85]. Das bedeutet:

  • [0.85] = Rohwert von GPT, z. B. 85 % Wahrscheinlichkeit für Spam.
  • weight aus der Metric (z. B. 9.0 für GPT_SPAM).
  • Grundformel: Rohwert × weight → ergibt den Beitrag zum Gesamtscore.
  • Hinweis: Je nach Rspamd-Version kann der im Header gezeigte Wert zusätzlich skaliert sein (z. B. falls das Modell nur ein „softes“ Signal liefert). Deshalb sieht man in der Praxis häufig 2–8 Punkte statt des Maximalgewichts.

Actions/Schwellen

actions {
  greylist = 4;
  add_header = 6;
  reject = 15;
}

SUSPICIOUS (4.5) kippt oft in Greylist. SPAM (9.0) bringt fast immer Add-Header, Reject nur zusammen mit weiteren harten Befunden. Klassische Checks (SPF/DKIM/DMARC, RBL, Bayes) bleiben führend, GPT ergänzt nur.

Tuning
Zu bissig? Gewicht etwas senken.
Zu lasch? Gewicht erhöhen.
Zu optimistisch bei HAM? Gewicht kleiner machen oder 0 setzen.
Header mit X-GPT-Reason liefert Nachvollziehbarkeit, kann bei Bedarf wieder entfernt werden.

Praxis
– Symbole erscheinen im WebUI und Logfiles.
X-GPT-Reason erklärt im Header die Bewertung.
– Latenz/Kosten: gpt-4o-mini mit 160 Tokens und 10 s Timeout ist performant und günstig.

Jetzt schauen wir uns mal die Mailheader eines echten Beispiels an und wie GPT dort gegriffen hat:

X-Spamd-Result: default: False [8.59 / 15.00];
        VIOLATED_DIRECT_SPF(3.50)[];
        GPT_SPAM(2.10)[0.85];
        MISSING_MIMEOLE(2.00)[];
        CTYPE_MIXED_BOGUS(1.00)[];
        MID_RHS_NOT_FQDN(0.50)[];
        DMARC_POLICY_ALLOW_WITH_FAILURES(-0.50)[];
        MIME_HTML_ONLY(0.20)[];
        R_DKIM_ALLOW(-0.20)[thejewelbox.dd:s=1759374209.thejewelbox];
        ...

Erklärung:

  • X-Spamd-Result: [8.59 / 15.00] – Gesamtscore 8.59, Reject-Schwelle bei 15. Hier also kein Reject, sondern nur Add-Header.
  • GPT_SPAM(2.10)[0.85] – GPT meldet Spam mit 85 % Sicherheit ([0.85]). Daraus errechnet Rspamd den Beitrag ((…)), der in den Gesamtscore einfließt.
  • Die klassischen Checks wie VIOLATED_DIRECT_SPF(3.50) oder MISSING_MIMEOLE(2.00) haben ebenfalls beigetragen – GPT ist also nur ein Faktor im Gesamtbild.

Zusätzlich schreibt das GPT-Modul auf Wunsch auch eine kurze Begründung in den Mailheader:

X-GPT-Reason: This email is likely spam due to the urgency created around an unpaid invoice and the mismatch between the sender's domain and the company name.

Erklärung:

  • X-GPT-Reason – eigener Header, den du in gpt.conf mit reason_header = "X-GPT-Reason" aktivierst.
  • Der Text stammt direkt aus dem Modell und begründet die Einstufung (hier: Dringlichkeit „unpaid invoice“ + Domain/Company-Mismatch).
  • Nützlich für Analyse/Transparenz; kann auf MTA/MDA-Ebene wieder entfernt werden, wenn du ihn nicht bis zum Postfach durchreichen willst.

Ein Hinweis zum Datenschutz (gesamt)
Mit GPT-Integration gehen Mailinhalte an einen externen Dienst (z. B. OpenAI). Das kann datenschutzrechtlich relevant sein. Wer sensible oder personenbezogene Daten verarbeitet, sollte vorher prüfen, ob die Nutzung zulässig ist – oder alternativ ein selbst gehostetes, OpenAI-kompatibles Modell nutzen (z. B. Ollama). Den Reason-Header kannst du, falls nötig, serverseitig wieder entfernen.

DNS over TLS mit BIND, Stunnel und Android 9: Eigener DoT-Server​

Die Zeit ging weiter, die Entwicklung bei BIND und DNS ebenfalls. Daher gibt es nun einen neuen Beitrag, der das aktuelle Setup mit BIND 9.20 auf FreeBSD 15 beschreibt – inklusive sauberer Trennung von authoritative DNS (Port 53) und öffentlichem Resolver (DoT/DoH) sowie reproduzierbaren CLI-Tests für IPv4 und IPv6. Bitte dort weiterlesen.

Über die Techniken DoT (DNS over TLS) habe ich bereits im Zusammenhang mit Bind 9.16 geschrieben. Ebenfalls DoH (DNS over HTTPS) gibt es einen kleinen Beitrag.

Bilder der Bind 9 TLS Konfiguration

Zu diesem Zeitpunkt bracht BIND 9 die Unterstützung für DoH und DoT noch nicht selbst mit. Daher waren zu diesem Zeitpunkt noch Umwege über stunnel oder nginx zusammen mit doh-proxy nötig.

Zum Glück kommt die letzte stable Version 9.18.0 (26. Januar 2022) mit dem nötigen Support.

named now supports securing DNS traffic using Transport Layer Security (TLS). TLS is used by both DNS over TLS (DoT) and DNS over HTTPS (DoH).

Warum möchte man noch gleich DoH oder DoT benutzen? Ganz einfach… Über diese Techniken werden DNS Abfragen verschlüsselt übertragen. Dieses ist ein weiterer Schutz davor manipulierte Antworten zu bekommen und selbstverständlich, damit die eigenen DNS Abfragen erst überhaupt nicht mitgelesen werden. Denn wenn von einem Gerät im Netzwerk die DNS Abfrage zu z.B.: www.tagesschau.de kommt, könnte man davon bereits Dinge ableiten.

Wie die meisten Bind Konfigurationen ist dieses ebenfalls straightforward. Ab Version 9.18 bringt Bind alles Nötige mit. Da wir nun TLS mit dem Bind sprechen möchten, benötigen wir natürlich ein gültiges Zertifikat, wie z.B. beim nginx für seine Webseite.

Ebenfalls sollte man ein paar frische Diffie-Hellmann Parameter generieren:

openssl dhparam -out dhparam.pem 4096

Die eigentliche bind Konfiguration kann in der named.conf.options geschehen:

options {
        [...]
        listen-on port 853 tls local-tls { 37.120.183.220; };
        listen-on-v6 port 853 tls local-tls { 2a03:4000:38:20e::853; };
        listen-on port 443 tls local-tls http default { 37.120.183.220;  };
        listen-on-v6 port 443 tls local-tls http default { 2a03:4000:38:20e::853; };
        [...]
        allow-recursion-on { 127.0.0.0/8; ::1/128; 2a03:4000:38:20e::853; 37.120.183.220; };
        [...]
};

Da der bind auf weiteren Ports lauschen soll erweitert man diese für IPv4 und IPv6. Der Default Port für DoH ist dabei 443 und der default Port für DoT ist 853, beides TCP.

listen-on sowie listen-on-v6 sind wohl selbsterklärend.
port ist der TCP Port und erklärt sich ebenfalls.
tls sagt dem Bind das wir tls sprechen möchten.
local-tls verweißt auf den gleichnamigen tls Block über welchen man seine TLS Konfiguration vornimmt.
http ist für DoH.
default gibt den eigentlichen endpoint für die DoH Abfragen an, im default ist es /dns-query

Da der Server unsere DNS Abfragen erledigen soll, müssen wir ihm dieses noch per allow-recursion-on auf den jeweiligen Adressen erlauben.

Als nächstes wird die eigentliche TLS Terminierung konfiguriert (das lässt sich ebenfalls auslagern, wenn gewünscht). Dafür wird der folgende Block, außerhalb der Options Blocks, ergänzt:

tls local-tls {
    cert-file "/usr/local/etc/ssl/wild.kernel-error.de/2022/ecp/chain.crt";
    key-file "/usr/local/etc/ssl/wild.kernel-error.de/2022/ecp/http.key";
    dhparam-file "/usr/local/etc/ssl/dhparam.pem";
    protocols { TLSv1.2; TLSv1.3; };
    ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256";
    prefer-server-ciphers yes;
    session-tickets no;
};

local-tls ist dabei der name des Blocks. Auf diesen verweisen wir oben.
cert-file ist der Pfad zum Zertifikat. Ich habe dort nicht nur das Zertifikat, sondern die gesamte Chain, also mit Intermediate und Root.
key-file ist der Pfad zum Key des Zertifikates.
dhparam-file ist der Pfad zu den Diffie-Hellman Parametern.
protocols definiert die zu verwendenden TLS Protokolle. In diesem Beispiel TLS1.2 sowie TLS1.3.
ciphers definiert die zu verwendenden cipher. Es soll ja „sicher“ bleiben.
prefer-server-ciphers übermittelt dem Client die Information, in welcher Reihenfolge protokoll/cipher Kombinationen probiert werden sollen um einen Match zu finden. Erst das vermeintlich sicherste und dann immer „schlechter“.
session-tickets regelt ob eine Wiederaufnahme von TLS Sessions erlaubt ist oder nicht. Da ich forward secrecy nutzen möchte, ist es deaktiviert.

Damit ist die Konfiguration schon abgeschlossen (Firewall ggf. nicht vergessen!). Also testen….

Ein einfaches Tool dafür ist dog, oder natürlich dig aus den bind-tools aber Version 9.18. Für bind gibt es dann die Optionen +https oder auch +tls

dig +https @dns.kernel-error.de www.kernel-error.de A
dig +tls @dns.kernel-error.de www.kernel-error.de A

Der gleiche Test mit dog, sieht wie folgt aus:

dog www.kernel-error.de --tls "@dns.kernel-error.de"
A www.kernel-error.de. 6h00m00s   148.251.40.23
dog www.kernel-error.de --https "@https://dns.kernel-error.de/dns-query"
A www.kernel-error.de. 6h00m00s   148.251.40.23

Das war es auch schon! Viele Spaß mit einem „besseren“ DNS und wenn es noch Fragen gibt, einfach fragen 🙂

FreeBSD und Netcup: Probleme mit IPv6 lösen

Als Kunden der bei netcup FreeBSD „rootserver“ auf KVM qemu Basis einsetzt, habe ich schnell gemerkt, dass meine IPv6 Verbindung nicht stabil ist.

Bild wie ein Dinosaurier ein Patchkabel durchbeißt, im Hintergrund ist das Buch IPv6 Workshop zu erkennen.

Dieses zeigt sich wie folgt:

– Direkt nach dem Boot scheinen IPv6 Verbindungen zu funktionieren, wie gewünscht.

– Nach einiger Zeit brechen die Verbindungen zusammen, sowohl Verbindungen vom System in die Welt als auch Verbindungen von der Welt ins System.

– Verbindungen scheinen „Startprobleme“ zu haben. Ein ping läuft erst ein paar Mal ins Leere und dann funktionieren alle Verbindungen plötzlich wieder für einen Moment.

Ein möglicher Workaround ist es, vom System aus einen ping auf die IPv6 Adresse des Gateways von netcup zu starten. Dieses sorgt in der Regel kurzzeitig für funktionsfähige IPv6 Verbindungen. Aber was ist das genaue Problem und wie könnte eine brauchbare Lösung aussehen?

Als Leser mit IPv6 Wissen, denkt man natürlich sofort a NDP das Neighbor Discovery Protocol und ja ich denke genau hier liegt das Problem. Dieses unterscheidet sich zur Implementation bei Linux etwas, daher funktioniert IPv6 im netcup Testsystem problemfrei unter FreeBSD, NetBSD usw. aber nicht. Die Probleme im NDP lassen sich auf dem System schnell wie folgt erkennen:

root@netcup-vps:~ # netstat -s -picmp6
icmp6:
    0 calls to icmp6_error
    0 errors not generated in response to an icmp6 message
    0 errors not generated because of rate limitation
    Output histogram:
        neighbor solicitation: 29
        neighbor advertisement: 10
        MLDv2 listener report: 8
    0 messages with bad code fields
    0 messages < minimum length
    0 bad checksums
    0 messages with bad length
    Input histogram:
        router advertisement: 17
        neighbor solicitation: 633
        neighbor advertisement: 25
    Histogram of error messages to be generated:
        0 no route
        0 administratively prohibited
        0 beyond scope
        0 address unreachable
        0 port unreachable
        0 packet too big
        0 time exceed transit
        0 time exceed reassembly
        0 erroneous header field
        0 unrecognized next header
        0 unrecognized option
        0 redirect
        0 unknown
    0 message responses generated
    0 messages with too many ND options
    0 messages with bad ND options
    423 bad neighbor solicitation messages
    0 bad neighbor advertisement messages
    0 bad router solicitation messages
    0 bad router advertisement messages
    0 bad redirect messages
    0 path MTU changes

Ich habe einige Zeit damit verbracht, mich mit dem Support darüber auszutauschen. Wie immer landet man zuerst in der human firewall, im Anschluss erreichte ich oft Supportmitarbeiter in Ausbildung eines IT Berufes. Zwischenzeitlich wurde mir das Problem von netcup dann auch bestätigt und mit dem Hinweis versehen, dass es an ihren Core-Routern liege, welche irgendwann ein Update bekommen sollen. Wann genau dieses Updates gemacht wird, konnte/wollte mir niemand bestätigen. Ebenfalls wurde versucht mein System auf verschiedene Hosts zu verschieben, von welchen andere Kunden wohl positives berichtet hatten. Dann sollte das Problem irgendwann behoben sein und netcup war erstaunt, dass es dieses noch nicht ist. Was auch immer… Aus meiner Sicht liegt das Problem an netcup.

Gelöst werden kann dadurch, dass man seinem BSD mitteilt, es soll bitte ND nach RFC4861 machen. Dieses ähnelt zumindest im Punkt der Bindung des jeweiligen ND ans Interface. Dafür kann man dieses als kurzen Test wie folgt aktivieren:

sysctl net.inet6.icmp6.nd6_onlink_ns_rfc4861=1

Stellt sich der gewünschte Erfolg ein, wird es mit dem passenden Eintrag in der /etc/sysctl.conf permanent aktiviert:

net.inet6.icmp6.nd6_onlink_ns_rfc4861=1

Dabei aber bitte beachten, dass FreeBSD dieses vor ca. 10 Jahren aus Sicherheitsgründen deaktiviert hat. https://www.freebsd.org/security/advisories/FreeBSD-SA-08:10.nd6.asc

Ich bin mit meinen Bemühungen aber leider nicht zu einer, für mich funktionierenden, Lösung von Seiten netcups gekommen. Daher blieb mir nur RFC4861.

Ich denke das Problem ist sicher schon lange beim Support bekannt, einfache google Suchen oder im netcup Forum zeigen dieses schnell auf. Dennoch vermittelte der Support mir erst das Gefühl, selbst etwas falsch konfiguriert zu haben, nur um mir dann den Eindruck zu vermitteln, dass sie noch nie von einem solchen Problem gehört haben. Ich vermute daher, dass ihr Setup aktuell einfach keine „einfache“ Lösung dieses Problems zulässt und sie daher so agieren. Was aber natürlich reine Spekulation ist, ich kann auch nur „vor“ das Setup schauen.

Fragen? Dann fragen…

MikroTik 4-Port 10-Gbit-SFP-Switch: Leistung für knapp 100 Euro

Ich habe einen neuen Switch. MikroTik CRS305-1G-4S+IN… Das Gerät verbindet bei mir zuhause im Arbeitszimmer meine Workstation, das Storage, meinen eigentlichen Switch und meinen Windows PC.


Das Teil ist der Knaller! Die angepriesene Leistung ist tatsächlich da, das Gerät kommt zwar nur mit einem Netzteil, man könnte aber ein weiteres anschließen. Ebenfalls kann man den Switch per PoE betrieben, somit hätte man im Grunde schon drei Netzteile als Ausfallsicherheit, wenn man es denn braucht. Das Gehäuse ist komplett aus Metall und dient zusätzlich zur passiven Kühlung. Es gibt also keinen Lüfter oder ähnliches, dass Geräusche machen könnte.

Unter dem Gehäuse sind noch Löcher, um es einfach an einer Wand oder ähnlichem befestigen zu können.

Wer MikroTik kennt, der fragt sich natürlich sofort: „Läuft da ein MikroTik OS drauf?“ JA tut es… Voll nutzbar und mit allem was man so gewohnt ist. Man könnte also noch über verschiedenste Dinge wir z.B.: VRRP die verrücktesten Failover Szenarien abbilden. Aber selbst, wenn nicht… 4 SFP+ Ports mit 10 GB/s für knapp über 100€, muss man da noch nachdenken?!?

Ich mache ja eher selten Werbung für ein Produkt aber den Switch wollt ihr haben. Als „Desktopswitch“ für zuhause (wie bei mir) ist der schon zu schade. Ich kann mir den gut als Pärchen vorstellen um klassische 1GB/s Switche zu verbinden oder ähnliche Dinge. Klickt euch den und testet ihn. Das Ding ist super: https://amzn.to/3r3kQiW

Windows Server 2012 R2 mit Exchange 2016: SSL Labs A-Rating erreichen

Qualis A+ Icon.

Ich habe hier einen Windows Server 2012 R2 mit einem Exchange 2016. Out of the Box spricht das Ding TLS 1.0, SSLv3 und RC4. Da gehen einem schnell die Haare hoch, oder?

Kann man so ein System auf ein A+ im Qualys Rating bekommen und es arbeitet dann noch? Kleiner Spoiler, ja geht!

Ich muss zugeben, Microsoft Produkte strengen mich in dieser Hinsicht immer sehr an. Auch wenn sie ganz sicher ihre Daseinsberechtigung haben.

Nun gut… Für ein A+ benötigen wir neben brauchbaren ciphern, sauberen Protokollen usw. noch HTTP Strict Transport Security (https://de.wikipedia.org/wiki/HTTP_Strict_Transport_Security). Dieses ist im Grunde nur ein http response header und dieses konfigurieren wir im IIS Manager. Das besagte Testsystem kümmert sich nur um den Exchangeserver, daher konfiguriere ich es ganz oben (so wird es überall hin vererbt).

IIS-Manager ==> HTTP-Antwortheader ==> Hinzufügen

Screenshot der Internetinformationsdienste (IIS)-Manager.

Name: strict-transport-security
Wert: max-age=31536000; includeSubdomains

Wie auch im Bild zu sehen.

Qualis A+ Wertung.

Jetzt der spannende Teil. Man muss einige Änderungen in der Registrierung vornehmen um MD5, RC4 usw. zu deaktivieren. Ich habe da etwas vorbereitet um dieses zu tun. Ebenfalls wird SSLv3, TLS 1.0, TLS 1.1 deaktiviert. TLS 1.2 und TLS 1.3  wird aktiviert (TLS 1.3 dabei nur für die Zukunft). Ebenfalls werden super schwache Cipher deaktiviert. Die bestmöglichen dafür aktiviert und es wird auch eine Cipherorder vorgegeben. Das File einfach herunterladen, ausführen und im Anschluss den Server durchstarten (Microsoft halt…).

Aber dann sollte man sein A+ haben. Auch wenn ich gerne noch schönere Cipher hätte :-/ aber mehr ging nicht, bzw. habe ich nicht hin bekommen!

Qualis Wertung mit Blick auf Cipher Suites und Protocols.

Oh natürlich… Das Registry Snippet zum Download:

Fragen? Dann fragen…

We have now run out of IPv4 addresses.

Wenn ich dann mal „zitieren“ darf..

Dear colleagues,

Today, at 15:35 UTC+1 on 25 November 2019, we made our final /22 IPv4 allocation from the last remaining addresses in our available pool. We have now run out of IPv4 addresses.

Our announcement will not come as a surprise for network operators - IPv4 run-out has long been anticipated and planned for by the RIPE community. In fact, it is due to the community's responsible stewardship of these resources that we have been able to provide many thousands of new networks in our service region with /22 allocations after we reached our last /8 in 2012.

----------------------------------------------------------------
Recovered IPv4 Addresses and the Waiting List
----------------------------------------------------------------

Even though we have run out, we will continue to recover IPv4 addresses in the future. These will come from organisations that have gone out of business or whose LIR accounts are closed, or from networks that return addresses they no longer need. They will be allocated to our members according to their position on a new waiting list that is now active.

While we therefore expect to be allocating IPv4 for some time, these small amounts will not come close to the many millions of addresses that networks in our region need today. Only LIRs that have never received an IPv4 allocation from the RIPE NCC (of any size) may request addresses from the waiting list, and they are only eligible to receive a single /24 allocation.

LIRs that have submitted an IPv4 request can see their position on the waiting list in the LIR Portal. A new graph has also been published that shows the number of requests on the waiting list and the number of days that the LIR at the front of the queue has been waiting:
https://www.ripe.net/manage-ips-and-asns/ipv4/ipv4-waiting-list

----------------------------------------------------------------
Call for Greater Progress on IPv6
----------------------------------------------------------------

This event is another step on the path towards global exhaustion of the remaining IPv4 addressing space. In recent years, we have seen the emergence of an IPv4 transfer market and greater use of Carrier Grade Network Address Translation (CGNAT) in our region. There are costs and trade-offs with both approaches and neither one solves the underlying problem, which is that there are not enough IPv4 addresses for everyone.

Without wide-scale IPv6 deployment, we risk heading into a future where the growth of our Internet is unnecessarily limited - not by a lack of skilled network engineers, technical equipment or investment, but by a shortage of unique network identifiers. There is still a long way to go, and we call on all stakeholders to play their role in supporting the IPv6 roll-out.

At the RIPE NCC, we are here to support our membership and the wider RIPE community in this work. Aside from allocating the IPv6 resources that will be required, we will continue to provide advice, training, measurements and tools to help network operators as they put their deployment plans into action.

We are optimistic and excited to see what the next chapter will bring. So let's get to work - and together, let's shape the future of the Internet.


Best regards,

Everyone at the RIPE NCC

Japp… RIPE NCC ist damit leer!

« Ältere Beiträge

© 2026 -=Kernel-Error=-RSS

Theme von Anders NorénHoch ↑