Wieder mal beschwerte sich einer der Resolver hier über eine sehr schlecht zu beantwortende Anfrage. Normalerweise werfe ich das nach DNSviz und bekomme eine admintaugliche Erklärung mit buntem Bild. Aber diesmal brach diese Diagnoseseite zusammen.

Anfrage

PTR IN 29.193.48.27.in-addr.arpa. 75.746150 iterator wait for 113.19.72.5

Das sieht harmlos aus, ist es aber nicht, denn die Diagnose mit DNSviz liefert nur ein einziges Ergebnis.

Gateway Timeout
The gateway did not receive a timely response from the upstream server or application.
__________________________________________________
Apache/2.4 Server at dnsviz.net Port 80

Das ist schon mächtig seltsam.

Diagnose

Der erste Schritt ist es, einen DNS Trace von der Wurzel an zu beginnen.

Das ergibt:

$ dig +trace -x 27.48.193.29
.                       2673    IN      NS      k.root-servers.net.
.                       2673    IN      NS      e.root-servers.net.
.                       2673    IN      NS      j.root-servers.net.
.                       2673    IN      NS      d.root-servers.net.
.                       2673    IN      NS      l.root-servers.net.
.                       2673    IN      NS      a.root-servers.net.
.                       2673    IN      NS      f.root-servers.net.
.                       2673    IN      NS      g.root-servers.net.
.                       2673    IN      NS      b.root-servers.net.
.                       2673    IN      NS      m.root-servers.net.
.                       2673    IN      NS      i.root-servers.net.
.                       2673    IN      NS      c.root-servers.net.
.                       2673    IN      NS      h.root-servers.net.
;; Received 492 bytes from 2001:4bd8:0:104:217:17:192:66#53(2001:4bd8:0:104:217:17:192:66) in 1 ms

in-addr.arpa.           172800  IN      NS      d.in-addr-servers.arpa.
in-addr.arpa.           172800  IN      NS      b.in-addr-servers.arpa.
in-addr.arpa.           172800  IN      NS      f.in-addr-servers.arpa.
in-addr.arpa.           172800  IN      NS      c.in-addr-servers.arpa.
in-addr.arpa.           172800  IN      NS      a.in-addr-servers.arpa.
in-addr.arpa.           172800  IN      NS      e.in-addr-servers.arpa.
;; Received 419 bytes from 2001:dc3::35#53(m.root-servers.net) in 76 ms

27.in-addr.arpa.        86400   IN      NS      ns1.apnic.net.
27.in-addr.arpa.        86400   IN      NS      ns2.lacnic.net.
27.in-addr.arpa.        86400   IN      NS      ns3.apnic.net.
27.in-addr.arpa.        86400   IN      NS      ns4.apnic.net.
27.in-addr.arpa.        86400   IN      NS      apnic.authdns.ripe.net.
27.in-addr.arpa.        86400   IN      NS      apnic1.dnsnode.net.
27.in-addr.arpa.        86400   IN      NS      tinnie.arin.net.
;; Received 225 bytes from 2001:67c:e0::1#53(f.in-addr-servers.arpa) in 75 ms

48.27.in-addr.arpa.     86400   IN      NS      ns1.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns4.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns6.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns3.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns9.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns2.ortelcom.com.
48.27.in-addr.arpa.     86400   IN      NS      ns2.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns8.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns1.skycable.net.
48.27.in-addr.arpa.     86400   IN      NS      ns7.ortel.net.
48.27.in-addr.arpa.     86400   IN      NS      ns5.ortel.net.
;; Received 271 bytes from 2001:13c7:7002:3000::11#53(ns2.lacnic.net) in 284 ms

.                       3600    IN      NS      a.root-servers.net.
.                       3600    IN      NS      m.root-servers.net.
.                       3600    IN      NS      l.root-servers.net.
.                       3600    IN      NS      k.root-servers.net.
.                       3600    IN      NS      j.root-servers.net.
.                       3600    IN      NS      i.root-servers.net.
.                       3600    IN      NS      h.root-servers.net.
.                       3600    IN      NS      g.root-servers.net.
.                       3600    IN      NS      f.root-servers.net.
.                       3600    IN      NS      e.root-servers.net.
.                       3600    IN      NS      d.root-servers.net.
.                       3600    IN      NS      c.root-servers.net.
.                       3600    IN      NS      b.root-servers.net.
;; BAD REFERRAL
;; Received 506 bytes from 2404:c00:6:1:68bb:fc71:3252:c744#53(ns1.skycable.net) in 225 ms

Wie bitte? Das Ziel der Delegation kennt den Zone nicht? Nunja, das ist doch nicht so ungewöhnlich.

48.27.in-addr.arpa.     86390   IN      NS      ns2.ortelcom.com.
48.27.in-addr.arpa.     86390   IN      NS      ns2.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns8.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns6.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns5.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns4.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns7.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns3.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns9.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns1.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns1.skycable.net.
;; BAD (HORIZONTAL) REFERRAL
;; Received 415 bytes from 202.62.224.5#53(ns1.ortel.net) in 333 ms

Dieser Nameserver ist allerdings schon heftiger drauf. Denn er antwortet mit einer rekursiven Auflösung.

Ist er denn ein rekursiver Resolver, ein offnener noch dazu?

$ dig @202.62.224.5 lutz.donnerhacke.de AAAA +dnssec
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13997
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1280
;; QUESTION SECTION:
;lutz.donnerhacke.de.           IN      AAAA

;; ANSWER SECTION:
lutz.donnerhacke.de.    57599   IN      CNAME   pro.donnerhacke.de.
pro.donnerhacke.de.     57599   IN      AAAA    2001:4bd8:1:1:209:6bff:fe49:79ea

;; Query time: 608 msec
;; SERVER: 202.62.224.5#53(202.62.224.5)
;; WHEN: Tue Feb 21 18:14:43 2017
;; MSG SIZE  rcvd: 94

Ja, ein offener Resolver. Im rekursiven Modus.

Eine ungesicherte Handgranate im Internet. Mit remote Auslöser. Ideal für Amplification Angriffe.

Wie schaut's denn bei den anderen erwähnten Servern aus?

$ dig ns  -x 27.48.193.29 +norec @apnic1.dnsnode.net |
 while read a b c d e; do
    [ "$d" = "NS" ] || continue
    dig @$e -x 27.48.193.29 +norec
 done

;; QUESTION SECTION:
;29.193.48.27.in-addr.arpa.     IN      PTR

;; AUTHORITY SECTION:
.                       3600    IN      NS      f.root-servers.net.
.                       3600    IN      NS      e.root-servers.net.
.                       3600    IN      NS      d.root-servers.net.
.                       3600    IN      NS      c.root-servers.net.
.                       3600    IN      NS      b.root-servers.net.
.                       3600    IN      NS      a.root-servers.net.
.                       3600    IN      NS      m.root-servers.net.
.                       3600    IN      NS      l.root-servers.net.
.                       3600    IN      NS      k.root-servers.net.
.                       3600    IN      NS      j.root-servers.net.
.                       3600    IN      NS      i.root-servers.net.
.                       3600    IN      NS      h.root-servers.net.
.                       3600    IN      NS      g.root-servers.net.

;; ADDITIONAL SECTION:
f.root-servers.net.     3600    IN      A       192.5.5.241
e.root-servers.net.     3600    IN      A       192.203.230.10
d.root-servers.net.     3600    IN      A       128.8.10.90

;; Query time: 206 msec
;; SERVER: 113.19.0.5#53(113.19.0.5)
;; WHEN: Tue Feb 21 18:16:47 2017
;; MSG SIZE  rcvd: 506


dig: couldn't get address for 'ns7.ortel.net.': not found


; <<>> DiG <<>> @ns6.ortel.net. -x 27.48.193.29 +norec
; (1 server found)
;; connection timed out; no servers could be reached


;; QUESTION SECTION:
;29.193.48.27.in-addr.arpa.     IN      PTR

;; AUTHORITY SECTION:
48.27.in-addr.arpa.     86391   IN      NS      ns1.skycable.net.
48.27.in-addr.arpa.     86391   IN      NS      ns7.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns3.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns4.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns9.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns8.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns6.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns2.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns5.ortel.net.
48.27.in-addr.arpa.     86391   IN      NS      ns2.ortelcom.com.
48.27.in-addr.arpa.     86391   IN      NS      ns1.ortel.net.

;; ADDITIONAL SECTION:
ns1.skycable.net.       3600    IN      A       113.19.0.5
ns2.ortelcom.com.       3600    IN      A       202.62.224.2
ns1.ortel.net.          3600    IN      A       202.62.224.5

;; Query time: 187 msec
;; SERVER: 202.62.224.2#53(202.62.224.2)
;; WHEN: Tue Feb 21 18:17:03 2017
;; MSG SIZE  rcvd: 319


;; QUESTION SECTION:
;29.193.48.27.in-addr.arpa.     IN      PTR

;; AUTHORITY SECTION:
48.27.in-addr.arpa.     86390   IN      NS      ns9.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns1.skycable.net.
48.27.in-addr.arpa.     86390   IN      NS      ns4.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns5.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns2.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns2.ortelcom.com.
48.27.in-addr.arpa.     86390   IN      NS      ns1.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns6.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns3.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns8.ortel.net.
48.27.in-addr.arpa.     86390   IN      NS      ns7.ortel.net.

;; ADDITIONAL SECTION:
ns9.ortel.net.          3600    IN      A       113.19.72.5
ns1.skycable.net.       3600    IN      A       113.19.0.5
ns4.ortel.net.          3600    IN      A       27.49.0.5
ns2.ortel.net.          3600    IN      A       27.48.138.2
ns2.ortelcom.com.       3600    IN      A       202.62.224.2
ns1.ortel.net.          1200    IN      A       202.62.224.5
ns6.ortel.net.          3600    IN      A       27.49.96.2
ns3.ortel.net.          3600    IN      A       27.49.39.5
ns7.ortel.net.          3600    IN      A       27.49.64.2

;; Query time: 209 msec
;; SERVER: 202.62.224.5#53(202.62.224.5)
;; WHEN: Tue Feb 21 18:17:04 2017
;; MSG SIZE  rcvd: 415


dig: couldn't get address for 'ns8.ortel.net.': not found


dig: couldn't get address for 'ns4.ortel.net.': not found


;; QUESTION SECTION:
;29.193.48.27.in-addr.arpa.     IN      PTR

;; AUTHORITY SECTION:
48.27.in-addr.arpa.     86374   IN      NS      ns3.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns4.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns2.ortelcom.com.
48.27.in-addr.arpa.     86374   IN      NS      ns1.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns7.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns9.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns6.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns2.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns1.skycable.net.
48.27.in-addr.arpa.     86374   IN      NS      ns5.ortel.net.
48.27.in-addr.arpa.     86374   IN      NS      ns8.ortel.net.

;; ADDITIONAL SECTION:
ns3.ortel.net.          3600    IN      A       27.49.39.5
ns2.ortelcom.com.       2281    IN      A       202.62.224.2
ns1.skycable.net.       1069    IN      A       113.19.0.5

;; Query time: 1632 msec
;; SERVER: 27.49.39.5#53(27.49.39.5)
;; WHEN: Tue Feb 21 18:17:22 2017
;; MSG SIZE  rcvd: 319


; <<>> DiG <<>> @ns5.ortel.net. -x 27.48.193.29 +norec
; (1 server found)
;; connection timed out; no servers could be reached

Wir haben also:

  • Ein unzuständiger Server
  • Drei Server, deren Namen nicht mal mehr existiert
  • Drei Server, die nicht antworten
  • Drei offene Relays

Respekt

Wir betreiben ja DNS-Server im Rahmen der ISP-Tätigkeit. Diese Server bedienen die Anfragen der Kunden und sollten deswegen zuverlässig und schnell antworten. Dazu schau ich mir ab und zu an, welche Anfragen bei welchen Server Probleme bereiten. Diesmal traf es die regionalen Zeitungen.

Trau, schau, wem

In regelmäßigen Abständen wird eine Stichprobe der unbearbeiteten Queries mitgeloggt, um die Problemfälle zu finden.

Also schauen wir mal in die Liste der Server, die länger als 10 Sekunden keine Antwort auf eine bestimmte Anfrage lieferten.

$ egrep '[0-9][0-9]\.[0-9]* iterator' /var/log/statistic.log |
  awk '{print $9 " " $4 "/" $2 }' |
  sort |
  uniq
10.109.104.20 secureir.qa.ebaystatic.com./A
118.184.184.8 www.3322.org./A
134.91.235.100 veranstalterportal.ticketshop-thueringen.de./AAAA
134.91.235.100 www.mediengruppe-thueringen.de./AAAA
143.127.10.8 lib.norton.com./AAAA
192.166.198.86 47.250.215.85.in-addr.arpa./PTR
194.152.65.222 sme5100.d10.its.net./A
195.92.67.32 sme5100.d10.its.net./A
204.228.234.15 www.googlemail.de./A
204.236.217.229 api.prod.capptain.com./A
217.110.59.45 ads.newtention.net./A
54.221.107.196 api.prod.capptain.com./A
59.111.3.81 www.3322.org./A
62.96.140.187 ads.newtention.net./A
69.25.102.245 reports.hotbar.com./A
82.145.6.100 www.mediengruppe-thueringen.de./AAAA
82.145.6.100 www.zgt.de./AAAA
89.246.87.100 www.lesershop-thueringen.de./AAAA
89.246.87.100 www.mediengruppe-thueringen.de./AAAA
89.246.87.100 www.ticketshop-thueringen.de./AAAA

Der erste Fall ist klassisch: Ein Name eines gesicherten System hat einen nur intern erreichbaren Nameserver (10/8). Das ist kein Fehler, sondern entspricht dem empfohlenen Aufbau mit durchgehender Delegation bis nach intern.

Viel interessanter sind die vielen regionalen Medienangebote. Alle mit Anfragen nach IPv6 Adressen. Es ist immer der gleiche DNS-Server. Und die Wartezeiten bis zum finalen Timeout des DNS-Resolver können deutlich über 10min andauern.

Spurensuche

Die Häufung am Nameserver deutet auf eine Fehlkonfiguration hin.

Da es doch einen gewissen Bedarf nach den Inhalten gibt, und man im Zweifel auch mit einem Verantwortlichen das Problem klären könnte, qualifiziert sich dieses Phänomen zu einem interessanten Fall.

Zuerst einmal der Grundlagencheck:

$ host -t A www.mediengruppe-thueringen.de 82.145.6.100
www.mediengruppe-thueringen.de has address 82.145.6.159

$ host -t txt www.mediengruppe-thueringen.de 82.145.6.100
Host www.mediengruppe-thueringen.de not found: 5(REFUSED)

$ host -t AAAA www.mediengruppe-thueringen.de 82.145.6.100
;; connection timed out; no servers could be reached

Es hängt ganz offensichtlich vom angefragten Typ ab, wie der Server reagiert.

Dies deutet auf eine bewusste Konfiguration hin. Man möchte offenbar nur das zulassen, was man selbst für notwendig erachtet.

Wie schaut es dann dann mit DNSSEC aus? Schließlich habe ich auf den Resolvern DNSSEC Validierung an!

Dazu gibt es eine schöne Testseite: dnsviz.net. Diese liefert dieses Bild.

www.zgt.de-2017-02-15-13_39_45-UTC

Oh, das schaut gar nicht gut aus!

Die vielen Warnschilder habe ich mal einzeln aufgelistet:

  • www.zgt.de zone: The server(s) responded over TCP with a malformed response or with an invalid RCODE. (82.145.6.100, 89.246.87.100, 134.91.235.100)
  • www.zgt.de/AAAA: No response was received from the server over UDP (tried 8 times). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_NOEDNS)
  • www.zgt.de/DNSKEY: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_EDNS0_32768_4096, UDP_0_EDNS0_32768_512)
  • www.zgt.de/MX: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_EDNS0_32768_4096, UDP_0_EDNS0_32768_512)
  • www.zgt.de/NS: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_EDNS0_32768_4096)
  • www.zgt.de/SOA: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, TCP_0_EDNS0_32768_4096)
  • www.zgt.de/SOA: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_EDNS0_32768_4096)
  • www.zgt.de/TXT: The response had an invalid RCODE (REFUSED). (82.145.6.100, 89.246.87.100, 134.91.235.100, UDP_0_EDNS0_32768_4096)
  • zgt.de to www.zgt.de: No SOA RR was returned with the NODATA response. (74.208.254.254, 83.169.55.5, 195.34.161.195, 217.160.113.32, 2001:8d8:580:401:217:160:113:32, 2607:f1c0:849:fe00:74:208:254:254, 2a01:130:2000:118:195:34:161:195, 2a01:488:2000:c02:83:169:55:5, UDP_0_EDNS0_32768_4096)
  • zgt.de to www.zgt.de: The Authoritative Answer (AA) flag was not set in the response. (74.208.254.254, 83.169.55.5, 195.34.161.195, 217.160.113.32, 2001:8d8:580:401:217:160:113:32, 2607:f1c0:849:fe00:74:208:254:254, 2a01:130:2000:118:195:34:161:195, 2a01:488:2000:c02:83:169:55:5, UDP_0_EDNS0_32768_4096)
  • zgt.de/DNSKEY: No response was received from the server over UDP (tried 4 times). (2a01:130:2000:118:195:34:161:195, UDP_0_EDNS0_32768_512)

Kurz gesagt antwortet der Server nur auf eine Frage nach einer IPv4 Adresse korrekt. Der Rest wird entweder aktiv abgelehnt (REFUSED) oder die Anfrage komplett ignoriert (No response).

Selbst wenn eine inhaltlich korrekte Antwort zurück kommt, ist sie formal fehlerhaft.

Zum einen fehlt das (AA)-Bit, das ein zuständiger Server setzen sollte. Vermutlich ist einer der als authoritiv benannten Nameserver nur ein Recursor, der die Daten nicht in Kopie vorhält, sondern selbst auch immer an der Quelle fragen muss. Ein solcher Aufbau führt beim Ausfall der Quelle zum sofortigen Totalausfall der gesamten Zone. Sportlicher Ansatz!

Zum anderen ist die TCP-Implementierung fehlerhaft. Die Antworten entsprechen schlicht nicht der Norm. Da wäre jetzt langsam mal die eingesetzte Software von Interesse. Möglicherweise ist es sogar eine sackteure Appliance in ansprechender Gehäusefarbe mit schweren Hochglanz-Prospekten.

Auf der Suche

Der Ansprechpartner laut Whois ist telefonisch nicht erreichbar. Es tutet beliebig lange. Unglücklicherweise ist die benannte Person gleichzeitig administrativ, technisch und fürs DNS zuständig. Alle Einträge haben also die gleiche, nicht erreichbare, Telefonnummer.

Eine E-Mail werde ich nicht schreiben. Das Problem erfordert entweder ein längere schriftliche Erklärung (und da ist dieser Blogeintrag noch immer zu kurz) oder ein klärendes Gespräch, bei dem man sich auf das Gegenüber einlassen kann.

Also versuche ich über die Zentrale der FUNKE Corporate IT GmbH jemanden zu erreichen. Dort sagt man mir, dass man nicht zur IT durch stellen kann. Oh, das ist genial! Eine IT Firma, bei der die IT nur für gute Kunden erreicht werden kann.

Wesentlich hilfreicher ist dagegen die Telefonzentrale der Zeitungsgruppe Thüringen. Man reicht mich mehrfach hin und her, meist mit der Aussage Oh, da geht auch keiner dran.

Via Telefonkette gelange ich so zu Herrn H. aus der Netzwerkabteilung. Dieser hört sich das Problem an, wir kommen ins Gespräch und ich mache einen entscheidenden Fehler: Ich erkläre zu ausschweifend, welche Folgen diese Fehlkonfiguration hat.

Herr H. verbindet mich erleichtert sofort weiter zum Online Department. Schließlich hatte ich gerade gesagt, die Webseite würde deswegen mit 30 Sekunden Verzögerung …

Dort war man allerdings wesentlich schneller von Begriff und stellte mich wieder zu Herrn H. zurück. Offenbar hatten sie die besseren Worte gefunden, weil ich nun tatsächlich dazu kam, das Problem zu erklären.

Herr H. hat versprochen, sich zu informieren, wer und ob da was gemacht werden kann.

Warten auf Herrn H.

Server mit FreeBSD zu betreiben. gefällt mir. Was allerdings ein schlechtes Gefühl hinterlässt, ist die Attitüde von FreeBSD, alles oder nichts zu liefern. In diesem Sinne gibt es beim leichtesten Husten einen Kernelpanik (der standardmäßig die Kiste hängen lässt). Für Dienste gilt ähnliches: Sie werden beim Booten gestartet und dann müssen sie allein überleben.

Leben am Limit

Keine Software ist perfekt. Wenn sie mal abstürzt, gibt es zwei Möglichkeiten damit umzugehen.

Die erste Variante besteht in einem stillen Restart des Dienstes oder der Maschine. Mit etwas Redundanz im Aufbau heilt sich ein Fehler von allein.

Die andere Variante soll den Fehler schnellstmöglich beheben. Dazu muss ein Fehler weh tun, nein, es muss richtig schmerzen. Und das ist der FreeBSD Weg.

Entdeckt der Kernel einen Fehler, so wirft er gern und schnell einen panic. Die Standardbehandlung besteht darin, Debuginfos auf die Konsole zu schreiben und das System anzuhalten. Der zuständige Admin kann dann an der Maschine selbst sehen, was vorgefallen ist und manuell neu starten.

In ähnlicher Weise wird der Userspace befüllt. Während des Boot-Prozesses werden die Dienste gemäß der rc.conf gestartet. On es klappt oder nicht, steht dabei nicht zur Debatte. Und wenn ein Dienst später mal abstürzt, dann muss der Admin auf die Maschine gehen und den Dienst manuell neu starten.

Das ist für die Produktion klar indiskutabel.

Selbstheilung

Der Umgang mit den Kernelpanics ist noch am einfachsten. Man übergibt das Handling dem Kernel Debugger und scripted dessen Verhalten.

[root@server ~]# service ddb rcvar
# ddb : DDB kernel debugger
#
ddb_enable="YES"
#   (default: "")

Die zugehörigen Scripte finden sich in /etc/ddb.conf:

# kdb.enter.panic       panic(9) was called.
script kdb.enter.panic=textdump set; capture on; run lockinfo; show pcpu; bt; ps; alltrace; capture off; call doadump; reset

Im Detail tut das folgendes:

  • Umstellen auf Textdumps, statt eines kompletten Speicherabzugs.
    Das macht Dumps deutlich schneller und kleiner, so dass sie in die Swappartition passen.
  • Dann wird der Mitschnitt der Konsolenausgabe (für den Textdump) aktiviert und verschiedene Debuginfos eingesammelt.
  • Zum Schluss wird der Mitschnitt geschlossen und in die Swappartition geschrieben.
  • Abschließend rebootet das System von allein.

Mit der folgenden Einstellung in der rc.conf werden die Dumps beim Booten (vor der Aktivierung des Swapbereiches) ausgelesen und nach /var/crash gespeichert.

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"

Nach dem Reboot kann der Admin die Diagnoseinformation auslesen:

[root@server ~]# cat /var/crash/info.69
Dump header from device /dev/aacd0s1b
  Architecture: amd64
  Architecture Version: 1
  Dump Length: 105472B (0 MB)
  Blocksize: 512
  Dumptime: Wed Jan 25 15:25:04 2017
  Hostname: server...
  Magic: FreeBSD Text Dump
  Version String: FreeBSD ...
  Panic String:
  Dump Parity: 1769329166
  Bounds: 69
  Dump Status: good

[root@server ~]# tar xOf /var/crash/textdump.tar.69
db:0:kdb.enter.default>  show pcpu
cpuid        = 5
dynamic pcpu = 0xffffff807f3df680
curthread    = 0xffffff000ccc9000: pid 12 "swi1: netisr 5"
curpcb       = 0xffffff835f80cd10
fpcurthread  = none
idlethread   = 0xffffff0002b458c0: tid 100005 "idle: cpu5"
curpmap      = 0xffffffff80cb05d0
tssp         = 0xffffffff80d21188
commontssp   = 0xffffffff80d21188
...

Soweit so einfach.

Lost in userland

Schlimmer ist die Situation mit den verschiedenen Diensten. Es gibt keinen zentralen Weg, die benötigten Dienste automatisch neu zu starten, wenn diese durch irgendeinen Fehler abstürzen.

Einige Dienste bringen deswegen eigene WatchDog-Services mit. Andere hoffen einfach darauf, dass alles gut geht.

Es gibt einige Pakete, die sich mit dem Thema befassen, aber die sind entweder aus einer anderen Welt (wie monit), bauen auf doppelte Datenhaltung (wie fsc) oder sind komplett ungepflegt.

Ich möchte nun eigentlich die Bordmittel benutzen, um die benötigten Services regelmäßig zu prüfen und bei Bedarf neu zu starten.

Zentrale Komponente der Diensteverwaltung ist bei FreeBSD rc.conf. Man benutzt es über Scripte, vor allem den Wrapper service.

service -e listet alle aktivierten Dienste auf, allerdings etwas zu viel. So interessiere ich mich nur für die Dienste, die ein pidfile mitbringen, d.h. langfristig laufen. Die anderen Dienste sind oft nur einmalig auszuführende Einstellungen. Und dann gibt es noch Dienste, auch ohne pidfile durchlaufen. Also habe ich ein schnelles Shellscript geschrieben:

[root@server ~]# cat /usr/local/sbin/restart_services.sh
#! /bin/sh

# services which do not have a pidfile in the config
always="bird bird6"
# services which should not be checked
never=""

service -e | while read a; do
 doit=0
 for name in $always; do
   if expr "$a" : ".*/$name" >/dev/null; then
     doit=1
     break
   fi
 done
 for name in $never; do
   if expr "$a" : ".*/$name" >/dev/null; then
     doit=-1
     break
   fi
 done
 if [ "$doit" -eq 0 ]; then
   if egrep -q 'pidfile' $a; then
     doit=1
   fi
 fi
 if [ "$doit" -gt 0 ]; then
   if ! $a onestatus >/dev/null; then
     echo "Starting $a"
     $a start
   fi
 fi
done

Dieses Script prüft für jeden aktivierten (gemäß rc.conf) Dienst, ob dieser ein pidfile hat oder explizit benannt (always) wurde. Dienste, die ausgeschlossen wurden (never), werden ignoriert. Für die dann noch übrig bleibenden Dienste wird der Status abgefragt. Falls der Dienst nicht läuft, wird er gestartet.

Das Script kommt in die crontab von root:

2-58/5 * * * *  /usr/local/sbin/restart_services.sh

Und ich kann wieder ruhig schlafen.

Der DE-CIX ist ein Internet-Knoten in Frankfurt am Main und gemessen am Datendurchsatz der größte der Welt. Er wird von der DE-CIX Management GmbH betrieben. So sagt Wikipedia. Das riecht nach Technik, nach brummenden Routern, nach harten Kerlen, die sich schwitzend die Pakete zu werfen. Die Betreibergesellschaft sieht das aber ganz anders.

Ein Rauswurf

Im Spamordner fand ich heute folgende E-Mail.

2017-02-02-140237_581x357_scrot

Warum die Abmeldung? Weil ich den Newsletter nicht lese. Woher wollen die das wissen?

Also mal ein paar Details:

X-Spam-Report:
    *  Contains an URL listed in the URIBL greylist
    *      [URIs: list-manage2.com]
    *  Sender listed at http://www.dnswl.org/, no trust
    *      [198.2.190.6 listed in list.dnswl.org]
    *  RBL: Excellent reputation (+5)
    *      [198.2.190.6 listed in wl.mailspike.net]
    *  IADB: Sender publishes Sender ID record
    *  IADB: Sender publishes Domain Keys record
    *  IADB: Sender has reverse DNS record
    *  IADB: All mailing list mail is opt-in
    *  SPF: HELO matches SPF record
    *  From and EnvelopeFrom 2nd level mail domains are different
    *  Envelope sender domain matches handover relay domain
    *  sender matches SPF record
    *  BODY: Removal phrase right before a link
    *  BODY: Contains HTML link to CGI script with parameters
    *  HTML included in message
    *  Quoted-printable line longer than 76 chars
    *  Message has at least one valid DKIM or DK signature
    *  Message has a DKIM or DK signature, not necessarily valid
    *  Mailspike good senders
X-SMTP-Sender: 198.2.190.6
Return-Path: <bounce-mc.us4_XXX@mail6.suw11.mcdlv.net>
Received: from mail6.suw11.mcdlv.net (mail6.suw11.mcdlv.net [198.2.190.6])
    by XXX; Thu, 2 Feb 2017 13:36:02 +0100
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; s=k1;
    d=mail6.suw11.mcdlv.net; XXX
Received: from (127.0.0.1) by mail6.suw11.mcdlv.net XXX 
Subject: [SPAM] =?utf-8?Q?Sorry=20to=20see=20you=20go=20=2D=20Unsubscribe=20note?=
From: =?utf-8?Q?DE=2DCIX?= <newsletter@de-cix.net>
Reply-To: =?utf-8?Q?DE=2DCIX?= <newsletter@de-cix.net>
Date: Thu, 2 Feb 2017 12:35:55 +0000
Message-ID: <XXX@mail6.suw11.mcdlv.net>
X-Mailer: MailChimp Mailer - **CID8d09d454c9be7dc46789**
X-Campaign: mailchimpXXX
X-campaignid: mailchimpXXX
X-Report-Abuse: Please report abuse for this campaign here:
   http://www.mailchimp.com/abuse/abuse.phtml?u=XXX
X-MC-User: XXX
X-Feedback-ID: XXX:us4:mc
List-ID: XX list <XXX.list-id.mcsv.net>
X-Accounttype: pd
List-Unsubscribe: <mailto:unsubscribe-mc.us4_XXX@mailin1.us2.mcsv.net?subject=unsubscribe>,
 <http://eco.us4.list-manage2.com/unsubscribe?u=XXX>
Sender: "DE-CIX" <newsletter=de-cix.net@mail6.suw11.mcdlv.net>
x-mcda: FALSE
Content-Type: multipart/alternative; boundary="_----------=_MCPart_XXX"
MIME-Version: 1.0

Ich habe hier mal alle Dinge, die personalisierte Tracking-Nummern enthielten, mit XXX überschrieben. Man sieht sehr schön, warum das als Spam aussortiert wurde: Weil der Absender nicht der DE-CIX ist, sondern ein Dienstleister in den USA im Namen des DE-CIX zu agieren versucht.

Sorry, Leute. Das geht gar nicht.

Gründe?

Aber es bleibt noch die Frage nach dem Grund der Abmeldung.

Ganz offensichtlich haben die bisherigen Newsletter externe Tracking-Bilder/Scripte enthalten. die mein Client nicht bedient hat. Also hat der Versendedienstleister angenommen, ich würde die Mails nicht lesen.

Und dann hat man einfach alle Leute rausgeworfen, bei denen das Tracking versagte.

Kurz nochmal rekapitulieren: An wen wendet sich der Newsletter? An Techniker, vermutlich sogar Hard-Core(sic)-Techniker.

Und solche Menschen neigen dazu, Ihre eigene Technik sorgfältig zu gebrauchen. Der Mailreader vieler der Techniker wird ein mutt oder pine sein.

Selbst wer Outlook oder ähnliches benutzt, wird die Einstellungen entsprechend angepasst haben.

Diese Mailreader führen also keine Scripte aus, sie laden keine Bilder nach – damit sind diese Leser für die Marketingabteilung unsichtbar.

Ein Leser weniger

Der Versuchung, mich wieder anzumelden, konnte ich widerstehen.

Die verlinkte AnmeldeURL lautet: http://eco.us4.list-manage2.com/track/click?u=XXX&id=YYY&e=ZZZ

Das BDSG §4b verbietet die Weitergabe von personenbezogen Daten ins (datenschutzrechtliche) Ausland. Aber das sind die USA derzeit.

Bei der Suche nach einem Problem mit dem CDN von Google war es nötig, zwischen den IPs, die Google selbst benutzt und denen der externen Cache Server zu unterscheiden. Aber welche IPs hat Google denn?

Der offensichtliche Ansatz ist, bei https://stat.ripe.net/ das Suchwort "Google" einzugeben und sich die AS Nummer auflisten zu lassen.

Klickt man dann eins der ASNs an, z.B. https://stat.ripe.net/AS15169#tabId=routing erhält man die Liste der IP Bereiche. Allerdings ist das evtl. nur auf die RIPE Region beschränkt oder wie in dem Fall keine gute Idee:

Data Warning

Rendering this data completely requires a large number of visual items.
This may cause your browser to freeze. How would you like to proceed?

Natürlich kann man auch direkt eine whois Abfrage über den Origin der Route-Objekte ausführen.

$ whois -h whois.ripe.net -- -ar -i origin AS15169 | egrep '^route'
route:          1.0.0.0/24
route:          1.1.1.0/24
route:          1.2.3.0/24
route:          103.62.64.0/24
[6977 Zeilen ausgelassen]
route6:         2a03:ace0::/32
route6:         2c0f:fb50:4002::/48
route6:         2c0f:fb50:4003::/48
route6:         2c0f:fb50::/32

Das ist ehrlich gesagt unpraktisch, und mit hoher Wahrscheinlichkeit sogar falsch für meinen Kontext. Und natürlich fehlen noch all die anderen ASNs, die Google benutzt.

Aber dann kam die Rettung aus dem DNS (ich hab's im tcpdump vorbei fliegen sehen):

google.com.             TXT     "v=spf1 include:_spf.google.com ~all"

Natürlich! Google listet die relevanten Bereiche selbst auf, weil sie die beim Mailversand prüfen lassen möchte.

Und so gehen dann die Details ins Detail:

_spf.google.com.        TXT     "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"
_netblocks.google.com.  TXT     "v=spf1 ip4:64.18.0.0/20 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:207.126.144.0/20 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all"
_netblocks2.google.com. TXT     "v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all"
_netblocks3.google.com. TXT     "v=spf1 ip4:172.217.0.0/19 ip4:108.177.96.0/19 ~all"

Und das deckt sich mit den Netzen, die ich aussortieren muss, ziemlich gut.

Der Große hat sich ein Handy gewünscht, allerdings ein Windows Phone. Die Gründe liegen auf der Hand: Zum einen haben die Eltern sowas (Nokia) und zum anderen ist – Zitat – der AppStore viel kleiner, da kann man sich nicht so verzetteln. Allerdings funktionierte nach dem Einrichten nicht viel mehr als Telefonie, SMS und Internet. Danach begann eine Odyssee durch die Tiefen der Microsoft-Accounts.

Ausgangslage

Das Kind weiß bereits, wann man korrekte Angaben zu machen hat und wann man sich auf das Schweigerecht beruft. Am Ende der Einrichtung hat er ein Handy das mit einem Minderjährigen-Konto verknüpft ist. Das Konto läuft auf seine private E-Mail-Adresse, nicht auf die von Microsoft.

Microsoft lässt auf einem Konto für Kinder standardmäßig folgendes zu:

  • Unbegrenzte Nutzungsdauer zu allen Zeiten an allen Tagen.
  • Keine Einkäufe im AppStore, bei Xbox etc. pp.

Und natürlich will das Kind mit dem Handy auch spielen. Oder WhatsApp machen. Oder ...

Zuerst fiel auf, dass die Store-App keine Updates von installierter Software machen konnte.

Diese Aktion erfordert Ihre Zustimmung. Sie haben kein Berechtigung für diese Aktion.
Mehr Informationen unter  https://account.microsoft.com/family

Eine ähnliche Meldung gab's bei jedem Versuch eine App aus dem Shop zu installieren.

Familienspiele

Eine kurze Recherche im Internet zeigt, dass der Elternteil per Abbuchung der Kreditkarte als Elt authentisiert werden muss. Das will ich nicht.

Aber das Kind drängelt und das zu Recht. Also muss man in den sauren Apfel beißen. Die offizielle Beschreibung sagt, man solle als Elternteil seinem Kind eine Einladung schicken, die dieses dann annehmen muss. Soweit so einfach.

Das große Problem bei der Aktion ist, dass man sich auf einer Webseite anmelden soll. Und zwar mit den Zugangsdaten, die man verwendet hat, als man das Handy eingerichtet hatte. Natürlich hat man das Passwort vergessen. Ist bei ihm so. Ist bei mir so.

Denn wenn man einen selten (bis gar nicht) benutzten Account anlegen muss, dann kommt da ein längeres Zufallspasswort rein. Da der Account aber gegen eine reale E-Mail verknüpft ist, kann man im Notfall ein Passwort-Recovery nutzen.

Also machen wir mal Passwort-Recovery für beide Konten. Das geht ganz einfach und schnell. Anschließend kann das Kind vom Papa eingeladen werden. Die Einladung klickt man auf der Webseite durch. Fertig.

Nach einer Kreditkarte hat keiner gefragt. Wie schön. Beim genauen Lesen stellt sich heraus, dass eine Kreditkarte nur in den USA Pflicht ist. Da sind wir aber nicht. Also ein Problem weniger.

Panik

Das einzige Problem ist, dass sich nichts an der Sachlage ändert. Die Apps werden weiterhin nicht aktualisiert. Man kann nichts laden. Und die Fehlermeldung redet immer noch davon, der Familie beizutreten.

Kann es daran liegen, dass das Handy die neuen Zugangsdaten nicht kennt?

Und viel schlimmer: Geht mein eigenes Handy eigentlich noch? Ja, noch!

Wie ändert man beim Handy eigentlich die Zugangsdaten des verknüpften Profils?

Die Antwort auf diese Frage kann die Nutzer offenbar beunruhigen und wird deswegen offiziell nicht gegeben. Es gibt schlicht keine offizielle Webseite, die beschreibt, wie man die Zugangsdaten eines mit dem Handy verknüpften Profils ändert.

Nunja, KB2432933 enthält einen Passus, aber der gefällt mir nicht.

Haben Sie vor kurzem Ihr primäres Microsoft-Konto geändert oder umbenannt?

Wenn Sie kürzlich Ihr primäres Microsoft-Konto geändert oder umbenannt
haben, müssen Sie Ihr Telefon zurücksetzen, um das Microsoft-Konto auf
Ihrem Windows Phone zu ändern. Weitere Informationen finden Sie im
folgenden KB-Artikel von Microsoft:
   2430020 Ändern des Microsoft-Kontos auf Ihrem Windows Phone

Der KB2430020 Artikel liefert aber keinen Infos mehr, sondern leitet direkt auf die Windows Phone Hilfe weiter.

Von dort geht's zur FAQ und da findet sich tatsächlich ein Punkt:

Kann ich das Microsoft-Konto auf dem Handy ändern?

Sie können Ihr Microsoft-Konto nach dem Einrichten nicht mehr ändern. Wenn
Sie das Microsoft-Konto auf Ihrem Handy ändern möchten, müssen Sie es
zunächst auf die Werkseinstellungen zurücksetzen. Seien Sie dabei
vorsichtig: Wenn Sie Ihr Handy zurücksetzen, werden alle darauf
gespeicherten Inhalte und Einstellungen gelöscht.

Nun ist es Zeit für eine gepflegte Panik!

Drüber geschlafen

Am nächsten Morgen stellte sich die Situation noch anders dar.

  • Das Handy des Kindes funktioniert einfach so.
  • Der Store aktualisiert die bestehenden Apps.
  • Man kann neue Apps installieren (allerdings nur kostenlose, wie von Papa eingestellt)
  • Und Papas Handy funktioniert noch immer.

Und das alles ohne Passwortwechsel am Handy.

Was ist passiert?

Ganz offensichtlich hat es etwas gedauert, bis die Änderungen am Microsoft-Profil sich bis zum Handy herumgesprochen haben. Vermutlich setzt Microsoft ein Active Directory für die ganzen Windows Phones ein. Und AD-Synchronisierung kann schon mal mehrere Stunden brauchen. Aber Geduld ist im Familienumfeld eine seltene Tugend.

Und warum geht das Handy ohne Passwortwechsel noch immer?

Nun, das ist einfach erklärt: Beim Verknüpfen eines Handys mit einem Microsoft-Profil wird nicht das Profil im Handy hinterlegt. Das wäre also so, wie man das bei Postfächern und Co. machen würde.

Stattdessen werden die Anmeldedaten benutzt, um einmalig das Handy im Profil einzutragen. Anschließend erkennt das Microsoft-Profil das Handy direkt. Die Frage nach einer Passwortänderung im Handy ist damit völlig sinnlos.

Darauf einen Kaffee.

Unser Google Traffic zeigte ein sehr seltsames Muster. In den frühen Abendstunden stiegt der Google-Traffic an, während der übrige Internet-Traffic abnahm. Das hielt etwa eine Stunde an, nach einer kurzen Pause dauerte es wieder eine Stunde und dann war der Spuk vorbei. Manchmal täglich. Manchmal nur alle zwei Tage. Rückfrage bei Google, was denn das gewesen sein könnte, ging raus.

Das Schreckensbild

Die Auslastungskurve zeigte heute früh folgendes Bild.

ggc-peak-yesterday

Ganz offensichtlich dupliziert das System massiv Inhalte für verschiedene Nutzer. Allerdings ohne diese neu nachzufragen. Die Inhalte sind also bereits lokal da.

Es geht 18:00 los, hat eine Pause bei 19:00 und endet kurz vor 20:00. Was mag das sein?

Rückblick

Der Effekt trat immer mal wieder auf, wie ein Blick auf die letzten zwei Wochen zeigt:

ggc-peak-weeks

Ach schau an: Der Effekt wurde immer stärker, mal kommt er täglich, mal nur alle zwei Tage. Und es fing an einem Freitag, den 13. an. Vorher gab es solche Effekte nicht.

Ursachenforschung

Der erste Anlaufpunkt in solchen Fällen ist der Betreiber der Plattform. Auch wenn das Unternehmen so groß ist, wie Google, kann und sollte man immer von der Quelle beginnend den Sachverhalt auseinander dröseln.

Und die Antwort ist verblüffend:

I think the mystery is solved - I've checked what sort of content type
(I cannot drill down into more specific details) was being served ...
Turns out, that the spike in traffic is caused by your users tuning in
for a Youtube Live broadcast and this fits your description.

Ein Live Broadcast? Von Youtube? Das erklärt zumindest den Verstärkungseffekt.

Aber mit so einer Resonanz? Was könnte in #neuland eine größere Menge von Nutzern dazu bewegen, sich gemeinsam Dinge im Internet anzuschauen? Und das auch noch so regelmäßig.

Das ist mir so unverständlich, dass ich die IRC-Kollegen in #denog fragte und die meinten, es könne die Handball WM sein.

Ein Vergleich mit dem Spielplan zeigt, dass es genau die Deutschland-Spiele sind:

Datum Uhrzeit Ort Partie Ergebnis
13.01. 17:45 Rouen Deutschland – Ungarn 27:23 (16:11)
15.01. 14:45 Rouen Chile – Deutschland 14:35 (6:17)
17.01. 17:45 Rouen Deutschland – Saudi-Arabien 38:24 (21:13)
18.01. 17:45 Rouen Weißrussland – Deutschland 25:31 (16:16)
20.01. 17:45 Rouen Deutschland – Kroatien 28:21 (13:9)
22.01. 18:00 Paris Deutschland - Katar 20:21 (10:9)

Schade, dass der Spuk vorbei ist.

Es gibt Sicherheitsanbieter, die aus einer Mücke einen Elefanten machen, um damit Aufmerksamkeit zu erregen. Das Geschäftsmodell kann man verbessern, indem man als Betreiber einer Blockliste die Aufmerksamkeit der Betroffenen einfordert. Obendrauf kann man ein Zusatzgeschäft satteln, das das Streichen von der Liste entgeltpflichtig macht. Natürlich wird eine solche Liste perfekt und seriös gepflegt sein.

Die Warnung

Dear Provider,

I’m George Egri, the Co-Founder and CEO of BitNinja Server Security.
I’m writing to inform you that we have detected malicious requests
from the IP a.b.c.d directed at our clients’ servers.

As a result of these attacks, we have added your IP to our greylist
to prevent it from attacking our clients’ servers.

Servers are increasingly the target of botnet attacks and you might not be aware
that your server is being used as a “bot” to send malicious attacks over the Internet.

I've collected the 3 earliest logs below, and you can find the freshest 100,
that may help you disinfect your server, under the link.

http://bitninja.io/incidentReport.php?details=ABC...XYZ

Url: [www.rechtslexikon.net/d/rechtsmittelverzicht/rechtsmittelverzicht.htm]
Agent: [Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0]

Url: [www.rechtslexikon.net/d/rechtsmittelverzicht/img/logo.png]
Agent: [Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0]

Url: [www.rechtslexikon.net/d/rechtsmittelverzicht/css/style.css]
Agent: [Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0]

For more information on analyzing and understanding outbound traffic,
check out the guide that we’ve created. We’ve dedicated an entire site to helping people
prevent their server from sending malicious attacks: https://doc.bitninja.io/investigations.html

Our incident experts are also happy to help you and can provide detailed logs if needed.
Please feel free to connect me with the administrator or technical team responsible
for managing your server.

This is not the correct contact email?

Please provide us the e-mail address of the server administrator or the abuse department
of the server management company, so we can send the securityreport to them and
discuss the further steps needed to resolve this problem.
You can provide the e-mail address using a web form. 

Thank you for helping us make the Internet a safer place!

Regards,

George Egri
CEO at BitNinja.io

Selbstverständlich ist die E-Mail an den falschen Abuse-Kontakt  gegangen. Deren Whois-Abfrage beherrscht Abuse-C ala RIPE nicht. Das habe ich schon zig-mal mit denen durch dekliniert. Jedes Mal ohne Erfolg. Sie verstehen es nicht.

Und es fehlen die wichtigen Angaben im Report: Da es sich um die öffentliche-IP eines NAT-Pools ist, benötige ich die Portnummer, um überhaupt die Chance zu haben, den Verursacher zu identifizieren.

Der Vorfall

Aber nun, schauen wir mal wieviel Aufwand man rein stecken könnte, also welche Art von Schaden angerichtet wird.

2017-01-10 18:10:06

Url: [ho###er.com/default.php?os=wp&calling_app=9nblggh4p0vk]
Agent: [Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/8.0; Touch; rv:11.0; IEMobile/11.0; Microsoft; Lumia 950 XL) like Gecko]
Get data: [Array(
 [os] => wp
 [calling_app] => 9nblggh4p0vk)]
 

2017-01-08 12:49:16

Url: [www.softair.at/faq.html]
Agent: [Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0]

2017-01-02 20:14:58

Url: [fa###ng.###.com/Overload/?steamid=76561198141804145&map=ttt_rooftops_2016_v1]
Agent: [Mozilla/5.0 (Windows; Valve Source Client) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1003.1 Safari/535.19 Awesomium/1.#.#.1 GMod/13]
Get data: [Array(
 [steamid] => 76561198141804145
 [map] => ttt_rooftops_2016_v1)]

2017-01-02 20:08:44

Url: [fa###ng.###.com/Overload/?steamid=76561198141804145&map=ttt_rooftops_2016_v1]
Agent: [Mozilla/5.0 (Windows; Valve Source Client) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1003.1 Safari/535.19 Awesomium/1.#.#.1 GMod/13]
Get data: [Array(
 [steamid] => 76561198141804145
 [map] => ttt_rooftops_2016_v1)]

2016-12-28 22:31:07

Url: [tattoomodels.hol.es/gotham-suicide/]
Agent: [Mozilla/5.0 (Linux; Android 4.4.2; X7 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.#.#.0 Safari/537.36]

2016-12-17 22:15:34

Url: [www.st-sebastian-bickenriede.hol.es/impressum.htm]
Agent: [Mozilla/5.0 (Linux; U; Android 4.3; de-de; HUAWEI G6-U10 Build/HuaweiG6-U10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30]

2016-12-17 22:15:34

Url: [www.st-sebastian-bickenriede.hol.es/kontakt.htm]
Agent: [Mozilla/5.0 (Linux; U; Android 4.3; de-de; HUAWEI G6-U10 Build/HuaweiG6-U10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30]

2016-12-17 22:15:02

Url: [www.st-sebastian-bickenriede.hol.es/Gottesdienste_Vermeldungen/aktuelle_gottesdienste_und_vermeldungen.pdf]
Agent: [Mozilla/5.0 (Linux; Android 4.3; HUAWEI G6-U10 Build/HuaweiG6-U10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36]

2016-12-17 22:14:29

Url: [www.st-sebastian-bickenriede.hol.es/Gottesdienste_Vermeldungen/aktuelle_gottesdienste_und_vermeldungen.pdf]
Agent: [Mozilla/5.0 (Linux; Android 4.3; HUAWEI G6-U10 Build/HuaweiG6-U10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36]

Das schaut aber nicht nach einem Angriff aus, sondern nach normaler Benutzung. Also stelle ich mal die explizite Rückfrage.

Can you please explain, what type of attack you are referring to?
I do only see valid web usage in your reports.

Der Abschluss

Die Antwort war ernsthaft überraschend:

Hello, 

Thank you for reaching out to us.
I checked the provided IP address and found nothing malicious in the recent,
therefore I delisted the mentioned IP address from our greylist.
If you have any more questions, do not hesitate to contact us.

Best Regards, 
Anita Batári
Incident Expert
https://bitninja.io

Mich begrüßte heute früh eine Mitteilung der Mailinglistensoftware von gnu.org, dass es Zustellprobleme bei E-Mail gäbe und man mich aus den Listen werfen würde. Die Fehlermeldung besagt, dass die Diffie-Hellmann-Schlüssel zu klein seien. Stattdessen ist es umgekehrt. Oder doch nicht?

Der Schreck

Messages to you from the xxx mailing list seem to
have been bouncing. I've attached a copy of the first bounce
message I received.

If this message bounces too, I will send you a probe. If the probe bounces,
I will remove your address from the xxx mailing list,
without further notice.
... --- ...
Hi. This is the qmail-send program at sourceware.org.
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.

<lutz@...>:
TLS connect failed:
  error:14082174:SSL routines:SSL3_CHECK_CERT_AND_ALGORITHM:
  dh key too small; connected to 217.17.192.66.
I'm not going to try again; this message has been in the queue too long.

Sollte ich die Maschine vergessen habe? Sollten da noch kurze DH-Schlüssel rumliegen?

Ungläubiges Staunen

Also schaue ich mal nach (und zwar auf beiden MXen) nach. Aber natürlich würde ich kaum Schlüssellängen verwenden, die auch andere benutzen.

[217.17.192.66]# dhparam -in /etc/sendmail/dhparams.pem -text | head -1
Diffie-Hellman-Parameters: (1139 bit)

[217.17.192.67]# dhparam -in /etc/sendmail/dhparams.pem -text | head -1
Diffie-Hellman-Parameters: (1123 bit)

Auf der 66, dem sekundären MX, ist der Schlüssel größer als auf dem primären MX (der 67). Und qmail sagt, dieser Schlüssel sei zu klein? Eine Prüfung ergibt, dass die Einlieferung auf dem primären MX problemlos klappt.

Dec  1 11:51:33 excalibur sm-mta[31817]: uB1ApSQ1031817:
 from=<xxx@yyy.gnu.org>, size=5506, class=-60, nrcpts=1,
 msgid=<zzz>, proto=ESMTP, daemon=MTA,
 relay=server1.sourceware.org [209.132.180.131]

Analoge Zeilen auf dem sekundären MX fehlen, stattdessen findet sich dort (passend zu der Beschwerde der Mailinglistensoftware)

Nov 30 13:02:07 avalon sm-mta[2695]: uAUC243i002695:
 server1.sourceware.org [209.132.180.131] did not issue MAIL/EXPN/VRFY/ETRN during connection to MTA

Ganz offensichtlich kommt das absendende Programm qmail nicht mit diesem Server klar.

Die eigne Nase

Intensives Suchen überzeugt mich, dass ich mit dem Problem allein bin. Es ist also sehr wahrscheinlich, dass der Fehler bei mir liegt.

Ein Test gegen den Server mit openssl s_client zeigt, dass tatsächlich ein zu kurzer DH-Key offeriert wird. WTF?

Die Konfiguration des Sendmail hat ganz klar die Konfigurationszeile auskommentiert, in der die Parameter zugewiesen werden. Der Sendmail hat sich selbst einen 512bit Key gebaut. Und der ist tatsächlich zu klein!

Also Konfig korrekt vorgenommen und ...

... siehe da, es geht!

Nebenan beschäftigt man sich mit der Laufzeit von Programmen anhand des Spieles Subtract a Square. Da ich aus einem anderen Projekt heraus in eine ähnliche Situation geriet, möchte ich diese Vorlage hier nutzen. Es geht darum, eine effiziente Methode zu finden, eine größere Menge an Werten systematisch nach verschiedenen Kriterien zu durchsuchen.

Aufgabestellung

Beim Spiel geht es darum, abwechselnd von einer vorgegebenen, positiven Zahl eine Quadratzahl abzuziehen, ohne ins Minus zu rutschen. Wer die letzte positive Zahl abziehen kann, hat gewonnen. Das Spiel endet offensichtlich mit dem Wert 0.

Für derartige Nimm-Spiele gibt es eine allgemeine Gewinnstrategie: Man zieht so, dass der Gegner nicht gewinnen kann.

Dazu tabellarisiert man alle Spiele mit kleineren Startzahlen und schaut, ob man dieses kleinere Spiel gewinnen könnte. Kann man das nicht, hat man eine Verliererposition. Mit der Kenntnis aller kleineren Spielausgänge muss man nur den ersten Spielzug betrachten.

Prozedurale Lösung

Zum Verständnis hier eine prozedurale Lösung des Problems:

with Ada.Text_IO;
with Ada.Command_Line;

procedure SubtractSquare is

   function Winners(i : Natural) return Boolean is
      type bitfield is array(0 .. i) of Boolean;
      pragma Pack(bitfield);
      win : bitfield := (0 => False, others => True);
   begin
      for pos in win'Range loop
         for offset in 1 .. pos loop
            declare
               square : Natural := offset*offset;
            begin
               exit when square > pos;
               if win(pos - square) then
                  win(pos) := False;
                  exit;
               end if;
            end;
         end loop;
      end loop;
      return win(i);
   end;

   package Cmd renames Ada.Command_Line;
   package BIO is new Ada.Text_IO.Enumeration_IO(Boolean);

   pos : Positive := Positive'Value(Cmd.Argument(1));
begin
   BIO.Put(Winners(pos));
end SubtractSquare;

Dieses Programm erzeugt ein (kompaktes) Bitfeld und setzt die Werte von kleinen zu großen Positionen. Der letzte Wert ist dann das gewünschte Ergebnis. Die gewünschte Position wird dem ersten Argument der Kommandozeile entnommen.

$ gnatmake -O2 subtractsquare.adb 
ada -c -O2 subtractsquare.adb
gnatbind -x subtractsquare.ali
gnatlink subtractsquare.ali -O2
$ ./subtractsquare 9
FALSE

Soweit so einfach.

Die Laufzeit ist maximal quadratisch (wegen der Doppelschleife), es besteht aber Hoffnung nur knapp über linear zu landen, weil die zweite Schleife vorzeitig abgebrochen wird.

Funktionale Lösung

Die direkte Übernahme dieses Ansatzes in eine funktionale Programmiersprache wie Haskell ist trivial:

import System.Environment

squares = [ i*i | i <- [ 1 .. ] ]

winners :: [Bool]
winners = False : [ and [ not $ winners !! (i-x)
                        | x <- takeWhile (<= i) squares
                        ]
                  | i <- [1 .. ]
                  ]

main = do [c] <- getArgs
          count <- readIO c
          print (winners !! count)

Man baut sich also eine unendliche Liste der möglichen Offsets (Quadrate). Mit der bastelt man die unendliche Liste der Gewinnerwerte zusammen, indem man beim Aufbau auf die Liste selbst zurückgreift. Das funktioniert, weil man nur auf Werte zugreift, die schon berechnet werden konnten.

Von der Liste gibt man das entsprechende Element zurück.

Unglücklicherweise ist das ziemlich langsam, denn die Doppelschleife ist eigentlich eine Dreifachschleife. Die Datenstruktur "[]" ist eine einfach verkettete Liste, die der "!!"-Operator immer von Anfang an durchlaufen muss.

Und genau das Problem habe ich in einem anderen Kontext auch. Also muss da eine Lösung her.

Effizientere Implementierung

Um die ständigen Rücksprünge zu vermeiden, kann man die Tatsache ausnutzen, dass man eigentlich mit mehreren Indizes (um Quadrate versetzt) linear durch die Liste durchläuft.

winners :: [Bool]
winners = False : worker 1 squares []
 where
  worker i so@(s:sn) rs
    | s == i    = worker' i sn (winners:rs)
    | otherwise = worker' i so          rs
  worker' i ss rs = and (map (not . head) rs)
                  : worker (i+1) ss (map tail rs)

Die Implementierung besteht also darin, mitzuzählen und jedes mal, wenn man eine Quadratzahl erreicht hat, einen weiteren Runner über das bisherige Feld aufzunehmen. Das macht die Teilfunktion worker.

Die eigentliche Wertermittlung erfolgt in der Teilfunktion worker', die von allen Runnern das erste Element auswertet und mit den Resten in die nächste Iteration läuft.

Ein direkter Vergleich der Laufzeiten zeigt einen durchschlagenden Erfolg:

subsquare-value-runtime

Seltsame Spitzen

Ungewöhnlich sind die Spitzen der violetten Kurve in diesem Graphen. Wo kommen die her? Fehlmessungen?

Also lass ich weitere Werte berechnen und sehe, dass die Spitzen bleiben. (Den Teil mit dem count ignoriere man mal vorerst.)

subsquare-stream-valuesum

Nun gut, es mag an der Schrittweite liegen, denn ich nehme nur alle 1000 Werte einen Messpunkt. Also mal in Einzelschritten in ein solches Intervall hinein hüpfen:

subsquare-stream-detail

Auch hier zeigt die violette Kurve ein extrem zackiges Bild. Warum?

Weil Haskell faul ist. Lazy evaluation ist eine Eigenschaft von Haskell, nur dann einen Wert auszurechnen, wenn der auch gebraucht wird.

In diesem Fall lautet die Aufgabe, einen bestimmten Wert in einer Liste auszugeben. Dazu geht Haskell die Listenkonstruktion durch und stellt fest, dass in jeder Iteration ein neues Element erzeugt wird. (In worker')

Um den konkreten Wert dann doch zu ermitteln, geht Haskell die Berechnung an und beginnt mit dem letzten hinzugefügten Runner. Der steht aber am Anfang der Liste und mit etwas Glück erwischt er gleich einen Abbruchwert. Damit kann Haskell das Ergebnis ausgeben und ist fertig.

Nur, wenn das Spiel wirklich eine komplexere Situation zu bewerten hat, muss Haskell deutlich mehr berechnen.

Um ihn zu zwingen, alle Werte zu ermitteln lasse ich die Anzahl der Gewinnzahlen bis zu der angefragten Position zu ermitteln.

--          print $ winners !! count
          print . length . filter id . take count $ winners

Eine alternative Formulierung wäre:

          print $ length [ x | x <- take count winners , x ]

Das Ergebnis ist verblüffend. Es ist genau die grüne count-Kurve in den oben stehenden Diagrammen.

Immer, wenn ein neuer Gewinnwert dazu kommt (oder in kleinen Schritten erreichbar wäre), steigt die Rechenzeit deutlich an. Das ist an der überlagerten blauen Stufenkurve zu sehen.

Als Gegentest kann man die Konstruktion "(winners:xs)" in worker durch "(xs ++ [winners])" ersetzen und zuerst den Wert des direkten Vorgängers benutzen. Das stellt ebenfalls sicher, dass alle Werte ermittelt werden. Die Laufzeiten liegen dann in vergleichbaren Größenordnungen wie beim count-Fall.

Warum dauert die Abarbeitung in Einzelfällen aber deutlich länger als im count-Fall? Er hat doch eigentlich weniger zu berechnen?

Die von Haskell zurückgelegten Teilberechnungen sind eine teuere Angelegenheit, sie anzulegen und später auszurechnen kostet deutlich mehr Zeit (und Speicher) als direkt zur Rechnung zu schreiten.

Nochmal!

Mit diesen Erkenntnissen vergleiche ich jetzt nochmal die beiden Algorithmen: Backreferenz per "!!"-Operator und mehrere Stream-Runner. Allerdings berechne ich diesmal die Anzahl der Gewinnzahlen.

subsquare-sum-runtime

Auch hier ist der Vergleich ähnlich: Die Runner gewinnen deutlich.

Evtl. hat ja die count-Berechnung auch das andere Verfahren massiv verlangsamt? Also beide Laufzeiten im direkten Vergleich:

subsquare-backref-valuesum

Die Backreferenz-Methode benötigt auch dann viel Zeit, wenn sie gar nicht alle Werte berechnen muss. Die Berechnung aller Werte dauert aber konstant länger. Beide Laufzeiten sind aber vergleichbar.

Zum Abschluss noch der direkte Vergleich der Methoden in der logarithmischen Darstellung:

subsquare-sum-runtime-log

Die Backreferenz-Methode ist klar polynomial (vermutlich O(n3)) während die Stream-Methode noch exponentiell aussieht (was sie aber nicht ist).

Fazit

Man achte bei der Programmierung stets darauf, ob der Algorithmus zum Programmumgebung passt.

Ist das nicht der Fall, verschlechtern sich die Ergebnisse erheblich. Dann muss entweder der Algorithmus (Streams statt Backreferenzen) oder die Umgebung (Data.Array statt Listen) angepasst werden.

Will man die Vorteile der Programmumgebung (Lazy Evaluation) erhalten, muss man die gewohnten algorithmischen Bahnen verlassen.

Als Belohnung winkt eine überdurchschnittliche Performance.