Content tagged net

VDSL von der Telekom mit IPv6 und Debian

posted on

IPv6 is the new normal

Meine letzten IPv6-Tunnel werden langsam überflüssig. Seit letzter Woche haben wir auch im Büro natives IPv6. Die Telekom hat uns auf einen NGN-Anschluss umgestellt.

Setup

IPv6 aktivieren

Kurz zusammengefasst

Für den IPv6-Zugang muss der pppd IPv6 auf der PPP-Verbindung aktivieren. Die Telekom benutzt dann Router-Advertisements (radv) um dem ppp0-Device eine IPv6-Adresse zu geben. Soll IPv6 im LAN weiterverteilt werden, bekommen wir Präfixe hierfür über Präfix-Delegation per DHCPv6. Letzteres kann aber nicht als Ersatz für die Router-Advertisements genutzt werden, da die Telekom nur Präfixdelegation darüber macht.

pppd konfigurieren

Nachdem der IPv4-Internetzugang konfiguriert ist muss nur noch IPv6 im pppd aktiviert werden. Hierzu muss an das Ende von /etc/ppp/options eine Zeile +ipv6 eingefügt werden.

echo "+ipv6" >> /etc/ppp/options

poff
pon dsl-provider

Das ppp0-Device sollte jetzt eine link local IPv6-Adresse haben (fe80:…). Wir können das mit ifconfig ppp0 kontrollieren.

IPv6-Adresse per Router Advertisement (radv) erhalten

Wir benutzen das Linux-System als Router. Wenn wir das Routing von IPv6-Traffic einschalten bezieht Linux seine IPv6-Adressen normalerweise nicht mehr automatisch. Über den Config-Wert 2 für die Einstellung accept_ra lässt sich dies jedoch überschreiben:

echo net.ipv6.conf.all.forwarding = 1 > /etc/sysctl.d/01-ipv6-forward.conf
echo net.ipv6.conf.ppp0.accept_ra = 2 >> /etc/sysctl.d/01-ipv6-forward.conf

echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
echo 2 > /proc/sys/net/ipv6/conf/ppp0/accept_ra

Wir müssen jetzt gegebenfalls auf das nächste Router Advertisement der Telekom warten. Dies kommt nur alle fünf Minuten. Haben wir diese Gedult nicht, können wir mit poff und pon dsl-provider uns nochmals neu einwählen. Danach sollte unser Interface ppp0 auch eine öffentliche IPv6-Adresse haben (2003:…).

Präfixe für die Subnetzs von DHCPv6

Als DHCPv6-Client habe ich dibbler-client und wide-dhcpv6-client ausprobiert letzterer war in der Lage deutlich schneller zu starten und zu stoppen, so dass ich mich für die WIDE-Implementierung des DHCPv6-Clients entschieden habe:

apt update
apt install wide-dhcpv6-client

Folgende Konfiguration habe ich als /etc/wide-dhcpv6/dhcp6c.conf angelegt:

interface ppp0 {
        send ia-pd 0;
};

id-assoc pd 0 {
        prefix-interface eth1 {
                sla-len 8;
                sla-id 1;
        };
        prefix-interface eth2 {
                sla-len 8;
                sla-id 2;
        };
};

Mit dieser Konfiguration wird ein Präfix über das Interface ppp0 vom DHCPv6-Server der Telekom angefordert. Dieses Präfix wird dann benutzt um Subnetze für eth1 und eth2 abzuleiten. Hierzu wird das von der Telekom erhaltene Präfix um 0001 verlängert für eth1 (sla-id 1) und entsprechend um 0002 für eth2.

Abschließend muss der wide-dhcpv6-client noch über die Datei /etc/default/wide-dhcpv6-client für das Interface ppp0 aktiviert werden:

# Defaults for dhcpv6 client initscript
# Used by /etc/init.d/wide-dhcpv6-client

# Interfaces on which the client should send DHCPv6 requests and listen to
# answers. If empty, the client is deactivated.
INTERFACES="ppp0"
service wide-dhcpv6-client restart

Jetzt sollten auch unsere Netzwerkinterfaces eth1 und eth2 IPv6-Adressen (und zugehörige Subnetze /64) erhalten haben.

Den Rechnern im LAN eine IPv6-Adresse zuweisen

(Genauer: Den Rechnern im LAN ihr Präfix mitteilen, so dass diese sich eine IPv6-Adresse geben können.)

Wir müssen auf dem Linux-Router hierzu selbst Router Advertisements auf unseren LANs verschicken. Dies geschieht mit dem Router Advertisement Daemon radvd:

apt install radvd

Für diesen Dämon legen wir die Konfigurationsdatei /etc/radvd.conf an:

interface eth1
{
        AdvSendAdvert on;
        prefix ::/64
        {
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr on;
        };

        RDNSS 2001:4860:4860::8888  2001:4860:4860::8844
        {
        };
};

interface eth2
{
        AdvSendAdvert on;
        prefix ::/64
        {
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr on;
        };

        RDNSS 2001:4860:4860::8888  2001:4860:4860::8844
        {
        };
};

Das Präfix ::/64 in dieser Konfiguration ist ein spezieller Wert. Der radvd benutzt für die Advertisements die auf dem jeweiligen Interface konfigurierten IPv6-Adressen um den Präfix selbst zu erreichnen. Die IPv6-Adresse hat uns im vorherigen Schritt der DHCPv6-Client automatisch gesetzt.

Der radvd muss noch mit der neuen Konfiguration aktiviert werden:

service radvd restart

Rechner im LAN vor eingehenden Verbindungen schützen (optional)

Wer es wirklich will und NAT bisher als Schutz seiner Rechner angesehen hat, kann über ip6tables noch einrichten, dass auch über IPv6 nur Verbindungen vom LAN ins Internet aufgebaut werden. TCP-Verbindungen aus dem Internet ins LAN werden damit abgewiesen. Hier die Datei /etc/init.d/ipv6-firewall anlegen:

#!/bin/sh
# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then
    set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
fi
### BEGIN INIT INFO
# Provides:          ipv6-firewall
# Required-Start:    $network $remote_fs $syslog
# Required-Stop:     $network $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Starting IPv6 firewall
# Description:       This are some firewalling rules for our
#                    IPv6 connection to the internet.
### END INIT INFO

DESC="Starting IPv6 firewall"


case "$1" in
  start)
     /sbin/ip6tables -A FORWARD -m state --state NEW -i eth1 -o ppp0 -j ACCEPT
     /sbin/ip6tables -A FORWARD -m state --state NEW -i eth2 -o ppp0 -j ACCEPT
     /sbin/ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
     /sbin/ip6tables -A FORWARD -p tcp -j REJECT

     /sbin/ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
     /sbin/ip6tables -A INPUT -p tcp -j REJECT
     ;;
  stop)
     /sbin/ip6tables -F INPUT
     /sbin/ip6tables -F OUTPUT
     /sbin/ip6tables -F FORWARD
     ;;
  force-reload|restart)
     $0 stop
     $0 start
     ;;
  *)
     echo "Usage: /etc/init.d/ipv6-firewall {start|stop|restart}"
     exit 1
esac

exit 0

Dieses Skript installieren und aktivieren:

update-rc.d ipv6-firewall enable
service ipv6-firewall start

Das war's … die lokalen Netze haben jetzt IPv6-Zugang.

DNSsec-Signaturen überprüfen mit Bind

posted on

Wer einen authoritativen Nameserver betreibt sollte die eigenen Zonen mit DNSsec sichern. Hier zeige ich euch, wie ihr einen rekursiven Nameserver so einrichtet, dass der DNSsec-Signaturen prüft. Was ist der Unterschied? Authoritative Nameserver sorgen beim Besitzer einer Domain, dass diese gefunden werden kann. Rekursive Nameserver suchen für einen Internetanschluss die richtige Verbindung zu fremden Domains.

Warum soll ich DNSsec aktivieren?

Klar gibt es auch immer ein paar Nachteile:

Manche meinen ein weiterer Nachteil sei, dass die Signaturen bei DNSsec nicht im Client sondern im Nameserver des Zugangsproviders geprüft werden. Die Verbindung zwischen Provider-Nameserver und Client sei so nicht geschützt. Dieses Problem lässt sich jedoch leicht dadurch lösen, dass der Client selbst Signaturprüfungen vornimmt. Wenn er das nicht tut, kann man dies nicht dem Protokoll anlasten.

Meiner Ansicht nach überwiegen die Vorteile bei weitem.

Umkonfiguration von Bind

Wir können DNSsec bei der Namensauflösung in bind sehr einfach aktivieren. Im options-Bereich der Konfigurationsdatei müssen nur zwei Zeilen eingetragen werden:

options {
        directory "/var/cache/bind";
        
        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };

        allow-recursion { 127.0.0.1; ::1; };

        dnssec-enable yes;
        dnssec-validation yes;
};

Die Einstellung „dnssec-enable“ aktiviert den grundlegenden Support für DNSsec in Bind. Die Einstellung „dnssec-validation“ aktivert, dass beim Auflösen von Domains die DNSsec-Signaturen mit angefordert und überprüft werden.

Überprüfen, ob eine Domain DNSsec unterstützt

Mit der neuen Konfiguration gehört jede Namensauflösung in eine von zwei Kategorien:

  1. Die aufgelöste Domain ist nicht mit DNSsec geschützt. Das Ergebnis ist nicht sicherer als bisher.
  2. Die aufgelöste Domain ist durch DNSsec geschützt. Das Ergebnis, das wir bekommen, wurde von Bind überprüft. Wenn die Überprüfung fehlschlägt bekommen wir das Ergebnis gar nicht mehr.

Wie können wir feststellen in welchen dieser beiden Fälle eine Namensauflösung gehört? Entweder wir installieren uns ein Plugin in den Webbrowser. Der DNSsec-Status wird dann in der Adressleiste angezeigt. (z.B. hier für Firefox, hier für Chrome und hier für den Internet Explorer).

Auch auf der Kommandozeile können wir eine Domain überprüfen. Wie für alles hat der Befehl dig auch hierfür die passende Option: +dnssec. Der Parameter lässt dig den DNSsec-Status vom Resolver anfordern. Dieser wird dann von dig mit ausgegeben. Im Header wird hierzu das Flag „ad“ angezeigt, wenn die DNSsec-Prüfung erfolgreich war:

# dig switch.ch +dnssec

; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> switch.ch +dnssec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36976
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 10

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;switch.ch. IN A

;; ANSWER SECTION:
switch.ch. 60 IN A 130.59.108.97
[...]
;; Query time: 330 msec
;; SERVER: ::1#53(::1)
;; WHEN: Mon May  6 21:33:12 2013
;; MSG SIZE  rcvd: 1211

Achtung Falle: Wer so testen will, ob die eigene Zone korrekt mit DNSsec gesichert ist, kann falsche Ergebnisse bekommen. Wenn ein Nameserver eine Anfrage zu einer selbst gehosteten Domain beantwortet ist das ad-Flag nie gesetzt. Das liegt daran, dass er in diesem Fall keine Überprüfung der Signaturen durchgeführt hat. Das muss er auch nicht, da er bereits weiß, dass die Daten korrekt sind.

Wollt ihr eure eigene Zone überprüfen? Der öffentliche Nameserver von Google macht das für euch: dig example.com +dnssec @8.8.8.8 (8.8.8.8 ist eine der IP-Adressen unter denen Google öffentliche Nameserver betreibt.)

Mein DNSsec-Setup

posted on

Vor ein paar Tagen habe ich bereits über einen Teil meines Nameserver-Setups mit Bind geschrieben. Ich habe erklärt, wie DDNS mit bind verwendet werden kann um beispielsweise Kunden die Möglichkeit zu geben Änderungen an Ihren Zonen über ein standardisiertes Protokoll durchzuführen oder die sich häufiger ändernde IP des Internetanschlusses zuhause immer aktuell im DNS zu hinterlegen. Dabei habe ich erwähnt, dass ein interessantes Argument für den Einsatz von DDNS zum Updaten von Zonen auch ist, dass bind in diesem Fall sich automatisch darum kümmert diese erneut zu signieren. Wie dieses Setup funktioniert möchte ich in diesem Artikel beschreiben.

Ziele meines Setups

Wenn man versucht etwas kryptografisch abzusichern, dann sollte der erste Schritt immer der gleiche sein. Es ist zu bestimmen, gegen welche Arten von „Angriffen“ man sich schützen möchte. Je nachdem was man sich hier für Vorgaben macht kommt man zu teilweise komplett verschiedenen „optimalen Lösungen“.

In meinem Setup kommt es mir nicht darauf an die höchstmögliche Sicherheit herzustellen. Vielmehr ist das Ziel meines DNSsec-Einsatzes die Grundsicherheit zu erhöhen. Einfache Angriffe mit dem Ziel DNS-Einträge im Cache von DNS-Resolvern zu verfälschen sollen verhindert werden. DNSsec soll bei mir deswegen möglichst breit und damit für alle Zonen verwendet werden. Es ist deshalb wichtig, dass möglichst kein zusätzlicher Aufwand in der Verwaltung der Zonen entsteht oder dieser nur minimal ist.

Indem ich bei meinem Setup die zur Signatur der Zonen genutzten Private-Keys auf meinem Server ablege(n muss), habe ich an einem anderen Punkt jedoch keinen erhöhten Schutz meiner Zonen: wenn es einem Angreifer gelingt durch irgendeine Lücke in meinen DNS-(Master-)Server einzudringen, so kann er Modifikationen an meiner Zone vornehmen und für diese trotzdem eine gültige Signatur erzeugen.

Ein weiterer Punkt bei dem mein Setup keine Verbesserung der Sicherheit bringt ist der Schutz gegen Angriffe, die meine Zonen unerreichbar machen. Es gibt sogar Überlegungen, dass der Einsatz von DNSsec prinzipiell die Sicherheit im Internet gegen DOS-Angriffe vermindert. Bei der DNS Amplification Attack werden DNS-Server genutzt um andere Server mit einem DOS-Angriff zu überziehen. Die Aktivierung von DNSsec vergrößert dabei die Gefahr, dass der eigene DNS-Server hierfür missbraucht wird.

Konfiguration von bind

In diesem Abschnitt erkläre ich welche Änderungen an den Konfigurationsdateien von bind vorgenommen werden müssen, damit die eigenen Zonen signiert ausgeliefert werden. Wie sich zeigen wird ist dies gar nicht viel. Es reichen zwei kleine Änderungen.

Grundsätzliches aktivieren von DNSsec

Damit bind grundsätzlich den Umgang mit DNSsec aktiviert, muss dies in der Konfiguration im options-Abschnitt angegeben werden:

options {
    directory "/var/cache/bind";

    auth-nxdomain no;    # conform to RFC1035
    listen-on-v6 { any; };

    allow-recursion { 127.0.0.1; ::1; };

    dnssec-enable yes;
    dnssec-validation yes;

    allow-transfer {
        key doux.amessage.eu.;
        key eder.amessage.eu.;
    };
};

Zur Aktivierung muss hier einfach der Eintrag „dnssec-enable yes;“ angegeben werden. (Der Eintrag „dnssec-validation yes;“ ist nicht notwendig und auf einem reinen autoritativen Nameserver auch nicht sinnvoll. Der Eintrag ist bei mir nur vorhanden, da ich meinen bind für localhost als recursive DNS-Server nutze.)

Diese Konfigurationsänderung muss sowohl auf dem Master-Nameserver wie auch auf allen Slave-Nameservern erfolgen.

Den einzelnen Zonen mitteilen wo sich die zugehörigen Keys befinden

Die zweite notwendige Änderung ist es, dass die Definition der Zone noch um zwei Zeilen erweitert wird:

zone "mwimmer.com" in {
    auto-dnssec maintain;
    type master;
    notify yes;
    file "/etc/bind/zones/mwimmer.com.zone";
    update-policy {
        grant root.user.invalid.     subdomain mwimmer.com. ANY;
        grant matthias.user.invalid. subdomain mwimmer.com. ANY;
    };
    allow-transfer {
	key root.user.invalid.;
	key doux.amessage.eu.;
        key matthias.user.invalid.;
    };
    key-directory "/etc/bind/keys";
};

Hier habe ich über „auto-dnssec maintain;“ bind mitgeteilt, dass er bei Updates der Zone diese automatisch neu signiert. Außerdem habe ich mit „key-directory "/etc/bind/keys";“ angegeben wo bind nach den notwendigen Private-Keys zur Signatur der Zone sucht. Ohne letzteren Eintrag würde bind in seinem working Directory suchen. Dieses ist auf einem Debian-System (wie man auch im vorherigen Config-Ausschnitt sieht) /var/cache/bind, was für eine Ablage der DNSsec-Keys nicht geeignet ist, da dieses Verzeichnis normalerweise nicht mit gebackupt wird.

Diese Änderung ist nur auf dem Master-Nameserver notwendig.

Einrichten einer Zone

Zum Einrichten einer neuen Zone auf meinem Nameserver erstelle ich hierfür zuerst eine normale, nicht signierte Zonendatei. Außer den Einträgen auto-dnssec und key-directory in der Bind-Konfiguration, die ich sofort mit eintrage, wird die Zone also zuerst vollkommen normal und ohne Signaturen angelegt und mit einem Aufruf von rndc reconfig auch aktiviert. (Wurde die Konfiguration einer bestehenden Zone geändert um beispielsweise bei einer bereits vorhandenen Zone DNSsec nachzurüsten, so muss stattdessen rndc reload benutzt werden.)

Die Zone wird ab diesem moment von bind ausgeliefert, ist aber noch unsigniert. Im nächsten Schritt geht es jetzt also darum die notwendigen DNSsec-Schlüssel zu erzeugen. Es werden hierzu zwei Schlüsselpaare erzeugt. Das eine paar ist der Key-Signing-Key (KSK). Ein Fingerabdruck hiervon sollte soweit möglich später in die übergeordnete Zone hinterlegt werden. Mit diesem Key-Signing-Key wird später von bind das zweite Schlüsselpaar, der sogenannte Zone-Signing-Key (ZSK), unterschrieben. Erst mit dem Zone-Signing-Key werden dann tatsächlich die eigenen Records in der Zone unterschrieben.

Die Erstellung der beiden Schlüsselpaare erfolgt mit dem Tool dnssec-keygen, das Teil der bind-Distribution ist (Debian-Paket: bind9utils):

dnssec-keygen -K /etc/bind/keys -a RSASHA512 -b 2048 -f KSK mwimmer.com
dnssec-keygen -K /etc/bind/keys -a RSASHA512 -b 1024 mwimmer.com

Je nachdem wie viel Entropie auf eurem System vorhanden ist, kann die Erzeugung dieser zwei Schlüsselpaare durchaus mehrere Stunden beanspruchen. Also nicht verzweifeln, wenn lange Zeit nichts vorwärts geht. Am besten startet ihr die Schlüsselerzeugung also in einem screen.

Nach der Erstellung der Schlüsselpaare muss noch sichergestellt werden, dass die Schlüssel von bind gelesen werden können. Da dnssec-keygen den Private-Key nur für den Datei-Owner lesbar macht (Zugriffsrechte „rw-------“) müssen wir auf einem Debian-System die erzeugten Dateien beispielsweise dem User „bind“ zuweisen.

Wenn nun beide Schlüsselpaare erzeugt und für bind lesbar sind, müssen wir bind noch mitteilen, dass er diese Keys ab sofort zur Signatur der Zone verwenden soll: rndc sign mwimmer.com

Voilà: bind liefert ab diesem Moment eine signierte Zone aus und kümmert sich auch darum, wann die Zone neu signiert werden muss.

Signaturen der Zone in der übergeordneten Zone eintragen (lassen)

Damit fremde Nameserver die Signaturen in der Zone prüfen können, müssen diese wissen welche Keys überhaupt berechtigt sind die Zone zu signieren. Bei DNSsec geschieht dies, indem die Signatur(en) des/der berechtigten Keys in die übergeordnete Zone eingetragen werden. Die Keys mit denen ich die Zone „mwimmer.com“ signiere sind entsprechend also in der Zone „com“ eingetragen. Indem die Zone „com“ wiederum selbst signiert ist und deren Schlüssel in der DNS-Root-Zone eingetragen ist usw. reicht es auf anderen Nameservern aus, dass dort nur die gültigen Keys für die Root-Zone eingetragen werden müssen. Die Funktionsweise ist also sehr ähnlich zu der von X.509-Zertifikaten. Auch bei diesen müssen einem Browser nur die Stammzertifikate der Zertifizierungsstellen bekannt.

Wie konkret in die übergeordnete Zone eingetragen wird was die gültigen Key-Signing-Keys sind ist sehr unterschiedlich. Was man allgemein nur sagen kann ist, dass dieser Eintrag über den Registrar erfolgt bei dem man die Domains registriert hat. Ob dieser DNSsec überhaupt unterstützt und wenn ja wie man ihm die Keys bekannt macht, ist von Registrar zu Registrar verschieden.

Ich persönlich habe mir für InterNetX entschieden und bin wegen DNSsec extra zu diesem Provider gewechselt, da meine vorherigen Provider leider alle nicht in der Lage waren DNSsec-Signaturen anzunehmen.

Außerdem ist es auch so, dass noch nicht bei allen Top-Level-Domains überhaupt eine Delegation mit DNSsec möglich ist. Bei den meisten größeren TLDs (z.B. .DE, .com, .net, .org, .eu) ist dies allerdings kein Problem mehr.

Ich möchte euch noch kurz zeigen, wie die Konfiguration der DNSsec-Delegation vom Falle von InterNetX geschieht.

DNSsec bei InterNetX (AutoDNS 3.0)

Hinweis vorweg: DNSsec unterstützt das Eintragen der Fingerprints von DNSsec-Schlüsseln in die übergeordneten Zonen. Wenn man DNSsec benutzen möchte, so muss man die Nameserver jedoch (mit den oben beschriebenen Einstellungen) selbst betreiben. Die Nameserver, die man über den AutoDNS verwalten kann, sind selbst nicht für DNSsec aktiviert.

Damit InterNetX die Signatur des Schlüssels mit dem wir unsere Zonen signieren an die übergeordnete Registry weiter gibt, müssen wir unseren öffentlichen Teil des Key-Signing-Keys bei der jeweiligen Zone hinterlegen. Damit der entsprechende Reiter bei der Zone im AutoDNS erscheint, müssen wir „DNSSEC aktivieren“ in den Einstellungen der Benutzeroberfläche aktivieren.


Grundsätzliches aktivieren von DNSsec in der Oberfläche des AutoDNS 3.0

Nachdem diese Einstellung aktiviert ist, erhalten wir in der Domainverwaltung wenn wir eine Domain bearbeiten einen zusätzlichen Reiter „DNSSEC“:


Reiter „DNSSEC“ in der Oberfläche des AutoDNS 3.0

In diesem Reiter müssen wir den verwendeten Algorithmus unseres Key-Signing-Keys eintragen (in obigem Beispiel haben wir einen RSASHA512-Schlüssel erzeugt, wir wählen also „10 (RSA/SHA-512)“. Außerdem tragen wir den BASE64-enkodierten öffentlichen Schlüssel (public Key) in das darunter stehende Eingabefeld ein. Zu diesem öffentlichen Schlüssel kommen wir am einfachsten, wenn wir unseren Nameserver fragen:

dig mwimmer.com DNSKEY | grep 257

Hinweis: Ich habe die Erfahrung gemacht, dass es mit AutoDNS 3.0 bei DE-Domains nicht funktioniert, wenn ich den DNSsec-Schlüssel bereits bei der Registrierung mit angebe. Die Domain wird dann zwar registriert, aber der Fingerprint des DNSsec-Keys nicht mit eingetragen. Ich habe mir deswegen auch bei der Registrierung angewöhnt diese zuerst ohne DNSsec durchzuführen und wenn sie fertig ist, bearbeite ich die Zone nochmals und trage die DNSsec-Einstellungen im AutoDNS 3.0 nach.

DDNS mit Bind einrichten

posted on

Der Nameserver Bind bringt von Hause aus die Möglichkeit mit, dass Änderungen in Zonen dynamisch durchgeführt werden können. Dies kann zum Beispiel genutzt werden um selbst etwas wie DynDNS zu realisieren. In der Tat nennt sich das was Bind da implementiert auch DDNS. Spezifiziert ist das Protokoll in RFC 2136: Dynamic Updates in the Domain Name System (DNS UPDATE).

Da ich heute eh den größten Teil dieses Textes als Mini-HOWTO für einen Kollegen geschrieben habe, möchte ich ihn auch hier auf meinem Weblog für andere zur Verfügung stellen. Alle hier verwendeten Befehle sind Befehle von Bind. Unter Debian sind diese Utility-Befehle im Paket dnsutils verpackt. Aktuell benutze ich die Version 9.8.4 von Bind.

Ich zeige in diesem Artikel:

  1. wie man einen kryptografischen Schlüssel zur Sicherung des Zugriffs erstellt,
  2. wie die Konfiguration von Bind angepasst wird,
  3. wie dynamische Updates an Bind geschickt werden und
  4. was zu beachten ist, wenn weiterhin auch klassische Updates an der Zone durchgeführt werden sollen.

Bei max.example.com handelt es sich im folgenden um den Namen des erzeugten Keys. Dieser hat die Form einer Domain, muss aber nicht zwangsläufig einer existierenden Domain entsprechen. Ich empfehle die E-Mail-Adresse des Schlüsselinhabers in dem das @-Zeichen durch einen Punkt ersetzt ist zu verwenden (im Beispiel wäre als die E-Mail-Adresse des Inhabers max@example.com). Bei example.net um die Zone, die für DDNS konfiguriert werden soll. Ein Key kann dabei für mehrere Zonen gemeinsam genutzt werden. Genauso ist es möglich mehreren Keys die Änderung einer Zone zu erlauben.

Key erzeugen

# dnssec-keygen -a HMAC-SHA512 -b 512 -n USER max.example.com

Das kann einen Moment dauern. Es entstehen zwei Dateien mit den Namen Kmax.example.com.*.key und Kmax.example.com.*.private. Beide Dateien enthalten den gleichen Key in unterschiedlichen Formaten, da wir für DDNS einen symmetrischen Key benutzen (d.h. kein Public-Key-Verfahren). Entsprechend muss der Inhalt beider Dateien geheim gehalten werden.

Konfigurieren von Bind

Zuerst muss der erzeugte Schlüssel in die Konfiguration von bind eingefügt werden:

key max.example.com. {
    algorithm hmac-sha512;
    secret "nc/Q3sL8LDpBE6XpPFdXji2VlhRirN3zxIErqA8xmS8JSMbqQ1PGZrAEdTHl0ekduiQGwDt9zicKtJCSBvaLJw==";
};

Der Inhalt von „secret“ ist dabei aus der Datei Kmax.example.com.*.key übernommen.

Außerdem wird die Konfiguration der Zone angepasst indem eingetragen wird mit welchem Key welche Änderungen gemacht werden dürfen:

zone "example.net" in {
    type master;
    notify yes;
    file "/etc/bind/zones/example.net.zone";
    update-policy {
        grant max.example.com.         subdomain example.net. ANY;
    };
};

Neu eingefügt ist hier der „update-policy“-Abschnitt. (Es gibt auch das Keyword allow-update, da kann man allerdings nicht konfigurieren was (nicht) geändert werden darf mit einem Key.)

Bind muss damit alles funktioniert Schreibzugriff auf die Zonenfiles und das Verzeichnis in dem diese liegen haben.

Editieren der Zone mit DDNS-Befehlen

# nsupdate -k Kmax.example.com.*.private
update delete example.net A
update add example.net 500 A 192.0.2.128
send
quit

Manuelles Editieren des Zonefiles

Wenn für eine Zone DDNS aktiv ist, dann darf nicht mehr einfach das Zonenfile geändert werden. Grund hierzu ist, dass Bind nicht für jede Änderung eine neue Version des Zonenfiles erzeugt sondern ein Journal anlegt. (Datei wie die Zonendatei plus Endung *.jnl.) Um die Zonendatei zu editieren muss bind gesagt werden, dass er keine weiteren Updates für die Zone annimmt und alle vorhandenen Updates in die Zonendatei schreibt. Das geht mit:

# rndc freeze example.net

Jetzt kann die Zonendatei editiert werden.

# rndc thaw example.net

Mit dem "thaw"-Kommando von rndc wurde die Annahme von Updates wieder aktiviert und gleichzeitig Bind instruiert die Zonendatei neu einzulesen, also gemachte Updates zu übernehmen.

Ich habe inzwischen aber komplett aufgehört Zonendateien auf diesem Weg zu editieren. Ich mache alles über nsupdate. Das hat den großen Vorteil, dass nichts schiefgehen kann. nsupdate übernimmt einerseits nur korrekte Änderungen und kümmert sich andererseits auch automatisch darum, dass der Serial im SOA-Record inkrementiert wird. Wenn DNSsec auf dem Nameserver aktiviert ist, so sorgt sich nsupdate sogar darum, dass die Zone automatisch neu signiert wird.

Kompatibilität von 8192-Bit-Zertifikaten

posted on

Aufgrund der Tatsache, dass die Hardware mit denen Public-Key-Schlüssel angegriffen werden kann immer leistungsfähiger wird, ist es notwendig, dass auch die Größe der eingesetzten Schlüssel immer weiter vergrößert wird.

Für den Einsatz mit qualifizierten digitalen Signaturen in Deutschland veröffentlicht beispielsweise die Bundesnetzagentur jährlich den sogenannten Algorithmenkatalog. In ihm steht jeweils welche Mindestschlüssellänge bis zu welchem Zeitpunkt als ausreichend angesehen werden kann.

Aktuell wird für den Einsatz von RSA-Schlüsseln bis Ende 2018 beispielsweise empfohlen, dass diese mindestens 2048 Bit lang sein sollten.

Dies betrifft aber natürlich nicht nur qualifizierte Signaturen sondern beispielsweise auch die verschlüsselte Übertragung von Webseiten mit dem HTTPS-Protokoll. Auch hierfür werden meistens RSA-Schlüssel eingesetzt. Üblicherweise trifft man auf Webservern dabei meistens Schlüssel der empfohlenen Länge an. (Ergebnis einer nicht repräsentativen Überprüfung einiger Websites durch mich.)

Natürlich kann man aber auch längere Schlüssel erzeugen und genau das habe ich gemacht bevor ich mir mein letztes Zertifikat habe ausstellen lassen. Der zugehörige RSA-Schlüssel ist 8192 Bit lang. Sicher eine an sich zur Zeit noch übertriebene Schlüssellänge, aber ein interessanter Key um damit zu testen ob alle Browser mit Keys dieser länge umgehen können.

ChromeFirefoxInternet Explorer 9Opera 12Safari 6
Linux (Debian wheezy)OKOK-OK-
Mac OS X (10.8)Meldung „Ungültiges Serverzertifikat“. Fehler kann nicht ignoriert werden.OK-OKLaden der Seite hängt, keine Fehlermeldung
Windows 7OKOKOKOK- (Safari 5: OK)

Wer sicherstellen möchte, dass seine Seite problemlos von allen Nutzern angeschaut werden kann, sollte zur Zeit also noch keine 8192-Bit-Schlüssel einsetzen, da er ansonsten einen Großteil von Mac-OS-X-Nutzern ausschließt.


Fehlermeldung von Chrome unter Mac OS X 10.8.1

TODO: Es interessiert mich noch, wie es mit älteren Versionen von Windows aussieht. Ich habe es noch nicht getestet, aber es könnte sein, dass es unter Windows XP anders aussieht als unter Windows 7.

Site schnellstens aus Google entfernen: alles außer robots.txt sperren

posted on

Diese Woche bekomme ich einen Anruf von einem Bekannten: er entwickelt gerade eine Website für einen Kunden. Und obwohl die Seite noch nicht fertig ist, taucht sie schon im Suchindex von Google auf. Er hatte vergessen die Seite während der Entwicklung mit einem Passwortschutz zu versehen. Er fragte mich, ob ich eine Idee habe, wie er dieses Versäumnis so schnell als möglich korrigieren könne.

Wir sind nun gemeinsam ein paar Möglichkeiten durchgegangen das Problem zu beheben:

  • Site jetzt mit Passwortschutz versehen: es kommt zwar niemand mehr auf die Seite, allerdings ist sie noch in Google (und vermutlich anderen Suchmaschinen) gelistet,
  • robots.txt auf die Seite laden, die allen Suchmaschinen sagt, dass die Site nicht gelistet werden möchte: wer die Adresse kennt kommt trotzdem noch darauf oder
  • manuell über die Google Webmaster-Tools die Löschung beantragen: allerdings hatte Google schon über 10.000 Treffer auf der Site.

Alle drei Einzelmaßnahmen alleine reichen nicht aus. Wir haben uns entschlossen, dass alles drei in Kombination sinnvoll ist. Eine kleine Herausforderung war nun den Webserver so zu konfigurieren, dass alle Seiten der Site grundsätzlich mit einem Passwortschutz versehen waren, als einziges der Zugriff auf die Datei robots.txt jedoch ohne Passwort möglich war. Meistens wird ein Passwortschutz ja über einen Eintrag wie "require valid-user" in einer .htaccess-Datei eingerichtet. In diesem Fall hätte ein solcher Schutz allerdings ins Root-Verzeichnis der Domain gelegt werden müssen und die robots.txt wäre dann auch nicht mehr abrufbar gewesen.

Lösen lässt sich dieses Problem mit der <LocationMatch>-Einstellung von Apache und einem kleinen regulären Ausdruck:

<LocationMatch "^/(?!robots\.txt).|robots\.txt.+$">
    require valid-user
    AuthType Basic
    AuthUserFile /etc/apache2/users/.htuser
    AuthName Devel-Area
</LocationMatch>

Eingetragen kann dies allerdings nicht in eine .htaccess-Datei werden. <LocationMatch> ist in einer solchen Datei nicht zulässig. Der Eintrag muss in den vhost-Eintrag der Apache-Konfiguration gemacht werden (unter Debian: /etc/apache2/sites-available/*).

Auf diesem Weg konnten wir den Zugriff auf die Seite sofort sperren und trotzdem den Suchmaschinen ermöglichen die Datei robots.txt zu lesen. Da diese Datei von Suchmaschinen häufig angefragt wird und wir darin die komplette Indizierung der Site gesperrt haben, waren so auch die Einträge in Google auch innerhalb von ein paar Stunden wieder Vergangenheit.

Probleme mit TLS-Verbindungen bei StartSSL-Zertifikaten

posted on

Ich greife mit zwei E-Mail-Programmen auf meinen Mailaccount zu. Das eine ist mutt, das andere ist Thunderbird. Während ich mit mutt keine Probleme habe, kann ich seit zwei Tagen mit Thunderbird nicht mehr auf mein E-Mail-Konto zugreifen.

Wenn ich es probiere, dann sehe ich Thunderbird für etwa 20 Sekunden versuchen eine Verbindung mit dem Mailserver aufzubauen. Nach diesen 20 Sekunden stellt sich Thunderbird wieder so als hätte es nicht versucht E-Mails abzufragen. Es kommt also auch keine Fehlermeldung oder dergleichen.

Da ich meinen Mailserver selbst betreibe, habe ich auch die Gelegenheit in dessen Logfile zu schreiben. Besonders aussagekräftig ist der Eintrag dort allerdings auch nicht:

Jul 11 22:30:24 eder imapd: couriertls: read: error:14094412:SSL routines:SSL3_READ_BYTES:sslv3 alert bad certificate

Das heißt so viel wie „der Client hat uns gesagt, dass er unser Zertifikat nicht akzeptiert, mehr weiß ich auch nicht“.

Ich habe eine ganze Zeit gesucht an was es liegen könnte, dass mein Thunderbird das Zertifikat des Mailservers nicht mehr akzeptiert. Des Rätsels Lösung ist, dass ich Thunderbird so eingestellt habe, dass er jedes Zertifikat bei der jeweiligen Zertifizierungsstelle (in meinem Fall StartSSL) überprüft, ob es inzwischen zurückgezogen wurde. Hierzu gibt es das Protokoll OCSP (Online Certificate Status Protocol).

Screenshot von den OCSP-Einstellungen meines Thunderbirds.
OCSP-Einstellungen von Thunderbird. Zu erreichen über Einstellungen > Erweitert > Zertifikate > Validierung

Hier liegt auch das aktuelle Problem: StartSSL scheint aktuell nicht in der Lage zu sein OCSP-Anfragen zu beantworten. Und eine weitere Einstellung in meinem Thunderbird sagt diesem: „Wenn eine OCSP-Server-Verbindung fehlschlägt, das Zertifikat als ungültig betrachten“. Soweit ich weiß, ist diese Einstellung per Default in den Mozilla-Produkten ausgeschaltet. Ich halte dies jedoch für keine gute Idee, da auf diesem Weg von einem Angreifer sehr einfach eine Überprüfung eines ggf. gestohlenen Zertifikates über OCSP verhindert werden kann. Er müsste nur dafür sorgen, dass kein Connect zum Zertifikatsherausgeber mehr gelingt. Diese Einstellung abzusichern ist bei der Konfiguration von Thunderbird also immer eines der ersten Dinge die ich mache.

Auch jetzt habe ich mich dazu entschlossen, dass ich diese Einstellung in meinem Mailprogramm nicht lockere sondern stattdessen besser vorübergehend auf den Einsatz von Thunderbird verzichte und all meine E-Mails mit mutt lese. Man könnte zwar nun sagen, dass dieser aktuell ja auch nur funktioniert, da er keiene OCSP-Überprüfung durchführt. Allerdings läuft mein mutt auf dem gleichen Server wie auch mein Mailserver. Da aller Traffic nur über das loopback-Device läuft habe ich hier geringere Sorgen auf diesem Weg angegriffen zu werden. Wenn jemand in der Lage ist diese IP-Pakete zu modifizieren, dann hat er solchen Zugriff auf den Server, dass er sich diese Mühen nicht mehr machen muss. Er verfügt dann ohnehin über mehr Möglichkeiten.

Was man an der ganzen Erfahrung eigentlich am Meisten enttäuscht ist, dass mir Thunderbird keine Fehlermeldung ausgibt. Ein Hinweis, dass die OCSP-Abfrage beim Herausgeber des Zertifikates nicht möglich war und er das Zertifikat des Servers deswegen ablehnt, hätte mir viel Problemsuche erspart. Oder zumindest nur eine Ausgabe, dass das Zertifikat ungültig sei, hätte mir zumindest die Suche nach dem Grund dafür erleichtert. Ich habe einen Bug hierzu bei Thunderbird aufgemacht. Vielleicht baut ja jemand eine entsprechende Fehlermeldung in zukünftige Versionen ein.

Mein Dank geht übrigens an Milan Berger, er hat mich mit seinem Blogpost OCSP, StartSSL und Performanceeinbußen auf den richtigen Weg gebracht die Ursache zu finden.

Nachtrag 12. Juli 2012, 10:00

Aktuell scheint der OCSP-Responder von StartSSL wieder zu funktionieren. Thunderbird akzeptiert das Zertifikat meines Mailservers wieder.

Virtuelles Hosting für HTTPS mit Apache und mod_gnutls

posted on

Die IPv4-Adressen gehen aus – so ließt man. Im Februar 2011 wurde die letzte freie Adresse in Europa vergeben. Was seitdem getan wird: nichts. Trotzdem leiste ich meinen Beitrag: ich spare IPv4-Adressen mit virtuellem Hosting auch für HTTPS.

HTTPS und die IP-Adressen

Der wichtigste Grund warum ein Webserver mehrere IP-Adressen hat ist HTTPS. Für jede HTTPS-Domain brauchte man bisher eine eigene IP-Adresse. Die Verschlüsselung von HTTPS wird aktiviert bevor der Webbrowser dem Server sagt welche Webadresse er abrufen möchte. Das Problem dabei: das Zertifikat welcher Domain soll der Server nun benutzen? Verwendet er das falsche meckert der Browser. Eine rote Seite erscheint und warnt den Internetnutzer, dass da etwas nicht stimmt und die Seite vielleicht unsicher ist. Das wollen wir nicht!

Die Lösung bisher: jede Domain hat eine eigene IP-Adresse. Muss ein Webserver eine HTTPS-Anfrage beantworten, schaut er welche IP-Adresse angesprochen wurde. Kein Problem, das kann er. Für jede IP-Adresse weiß er die zugehörige Domain. Er kann das richtige Zertifikat nutzen.

Schon 2003 aber wurde Server Name Indication (engl. für „Andeutung des Servernamens“) erfunden [RFC 6066: Transport Layer Security (TLS) Extensions: Extension Definitions]. Moderne Browser übermitteln hiermit schon bevor verschlüsselt wird welche Domain sie erwarten. Wenn sich der Browser an diesen Standard hält, dann kann der Webserver auch ohne verschiedene IP-Adressen erkennen welches Zertifikat er nutzen muss.

Funktioniert das auch immer?

Nein! Sowohl der Browser als auch der Server muss das Verfahren unterstützen. Probleme gibt es wenn der Nutzer mit einem Internet Explorer Version 6 oder mit Windows XP unterwegs sind. Beides ist aber inzwischen selten. Mit anderen Browsern und Betriebssystemen gibt es keine Probleme mehr.

Und der Server? Das haben wir Administratoren selbst in der Hand. Ich nutze Apache mit mod_gnutls. Server Name Indication versteht dieses seit irgendwann zwischen April 2005 (Version 0.2.0) und November 2007 (Version 0.3.4).

Also passt alles?

Leider nein. Ich hatte alles nach Anleitung eingerichtet. Die Fehlermeldungen sind nicht verschwunden. Immer wieder beschwerte sich der Browser: das Zertifikat ist falsch.

Suchen wir mit Google. Es gibt fast kein Problem, das ein anderer schon hatte. Tatsächlich: Jan Krüger beschreibt das gleiche Problem in mod_gnutls and StartSSL level 1 certificates: the problem (and solution).

mod_gnutls berücksichtigt nur die im Feld CN (Common Name) des Zertifikates eingetragene Domain. Dort ist aber nur Platz für eine einzelne Domain. Steht ein Zertifikat für mehrere Domains (z.B. mit ohne ohne „www“), so werden diese in einem anderen Feld eingetragen: Subject Alternative Name / dnsName. Solche Zertifikate hatte ich von StartSSL bekommen.

Jan hat hierfür einen Patch geschrieben und veröffentlicht. Allerdings unterstützt seine Lösung nur bis zu vier Domains pro Zertifikat. In den meisten Fällen reicht das sicherlich. Trotzdem wollte ich das Problem noch allgemeiner lösen.

Meine Version des gnu_tls-Patches kann mit einer beliebigen Anzahl von Domains in einem Zertifikat umgehen.

Ich wollte meine Verbesserung auch anderen Nutzern von Debian und mod_gnutls einfach zur Verfügung stellen. Eine E-Mail habe ich an den Debian-Paket-Betreuer und den Entwickler von mod_gnutls geschickt. Beide kamen unzustellbar zurück.

History of Matthias Wimmer's PGP and GPG keys

posted on

PGP-signed version of this history. - Text version of this history with a qualified signature.

KeyID Fingerprint Date Expires (sign/encrypt) Status
70D6C898 CAEC A12D CE23 37A6 6DFD 17B0 7AC7 631D 70D6 C898 2006-08-10 2010-01-01/2010-01-01 This is my main GPG key since 2006-08-10.
C9CD24F7 85A0 801A 2392 424F 69AB 5A63 C6D6 197D C9CD 24F7 2003-03-06 2008-03-04/2008-03-04 Key in use for special purposes. Please don't use it for e-mail.
4E59C7E6 B0F5 9D8C 28A8 34B6 FDE1 410A 3D27 3DDA 4E59 C7E6 2003-01-21 2007-01-01/2006-01-01 Key still exists, but only used for migration-signing to new key anymore.
8D8B4A2E 6F81 B414 B8A1 1806 A333 A18E 0142 F366 8D8B 4A2E 2001-01-11 *** revoked *** Public and private key still exist, never used for e-mail/not used anymore
AA839AF9 85E8 0EE5 C852 363C FC19 BD5F 27FE 6356 AA83 9AF9 2000-03-06 *** revoked *** Public and private key still exist, but not used anymore.
034FDE2A EA16 DEA8 4146 12EE 3B56 68D8 0A70 794E 034F DE2A 1999-09-15 *** revoked *** Public and private key still exist.
55DB8129 AF 14 32 B5 69 38 30 6E 3A B8 13 8D 66 A8 66 AE 1999-04-27 never :-( Lost private key!
B3D1AF25 8C 79 81 AA ED 95 4D 0B 8F 53 09 52 9A 39 32 49 1998-05-25 *** revoked *** Public and private key still exist, revoked because e-mail address is not used anymore.
730BD791 ??? 1997-07-05 never Lost public key, private key still exists.
D9954A11 ??? 1996-10-23 never Lost public key, private key still exists.
3336E4E9 ??? 1996-07-24 never Shared key (for the sysops of my former mailbox), I am not the only one that has the private key. Public key lost.
8B674F75 DE 51 C1 7E E4 99 4B 9D 5B 76 06 B2 DF 00 64 F1 1996-07-18 *** revoked *** Public and private key still exist, revoked because e-mail address is not used anymore.

There exist some even older keys that I have never used in the internet and that only contain FidoNet addresses. These keys are completely lost. I neither have the public nor the private keys anymore.

Change history

2006-08-10: Marked key AA839AF9 as revoked, updated signing-expire-date for key 4E59C7E6, added new key 70D6C898
2003-12-04: Updated expiry information for 4e59c7e6

IMUnified - Protokoll

posted on

IMUnified hat vor etwa zwei Jahren angekündigt, ein Protokoll zu schaffen, das es ermöglichen soll, verschiedene Instant Messaging Systeme mit einem Client zu benutzen. Gründungsmitglieder waren unter anderem MSN, Odigo und Yahoo!.

Inzwischen ist es wieder ruhig geworden, ob IMU jemals ernsthaft eingesetzt wird, wird wage ich fast zu bezweifeln. Aber immerhin kann beispielsweise der Yahoo!-Messenger das Protokoll, man muss es nur mit ein paar Registry-Änderungen unter Windows einschalten. Danach hat man die Möglichkeit, sich zusätzlich zu Yahoo! auch in andere IM Dienste anzumelden.

Dies habe ich zum Anlass genommen, mir IM Unified doch einmal anzuschauen und habe ein paar Sessions mit einem Network-Sniffer protokolliert. Das Protokoll ist an sich sehr einfach aufgebaut und erinnert etwas an Protokolle wie SMTP oder HTTP.

Alle Daten werden über eine TCP/IP-Verbindung ausgetauscht. Der Client konnektiert hierzu auf den Port 11319 des Servers. Über diese Verbindung macht er zunächst ein Login-Handshake und tauscht danach Daten aus. Der Austausch erfolgt in Blöcken.

Diese Blöcke haben dabei eine Zeilenstruktur, jede Zeile wird durch \r\n (carriage return, line feed) abgeschlosssen. Die erste Zeile enthält den Typ des Blockes, er gibt die Art des Befehles an. In der zweiten Zeile steht eine ASCII-Dezimalzahl, die angibt, wie viele Bytes der Block (gerechnet ab der dritten Zeile) enthält. Ab der dritten Zeile stehen Parameter-Zeilen, optional gefolgt von einer leeren Zeile zum Abschluss der Headerzeilen, optional gefolgt von einem Body des Blockes (z.B. in Nachrichten-Blöcken). Der Yahoo!-Messenger scheint die leere Zeile grundsätzlich zu übertragen, auch wenn kein Body benötigt wird, der Odigo-Server scheint die Leerzeile nur zu übermitteln, wenn der Body nicht leer ist.

Blocktypen (also Inhalt der ersten Zeile) habe ich bei der Nutzung von Odigo mit dem Yahoo!-Client dabei bisher folgende erhalten: "HELO" (vor Anmeldung, Versionsabsprache, Absprache über Authorisierungstyp), "LOGN Odigo-Nummer" (Austausch der eigentlichen Logininfo), "STAT status" (Setzen und empfangen eines Anwesenheitsstatus, z.B. STAT ONLINE), "ACK 600" (Bestätigung, keine Ahnung was die 600 bedeutet), "LIST ADD Odigo-Nummer" (Hinzufügen eines Nutzers), "PING" (Keep-Alive-Packets, Intervall bestimmt der Server in seinem HELO-Block), "MESG" (Nachricht), "LIST ACCEPT Odigo-Nummer" (Hinzufügen von sich selbst für einen Nutzer erlauben), "DISC" (Abmelden).

Die Kopfzeilen in einem Block haben immer das Format "Bezeichnung: Inhalt". Eine dabei immer enthaltene Kopfzeile ist die ID-Zeile. Sie identifiziert jeden Block eindeutig, der Server spiegelt die enthaltene ID in einer Reference-Kopfzeile, wenn sich ein Block direkt auf einen Block des Clients bezieht (z.B. bei "ACK 600"-Blöcken). Im Falle es Yahoo!-Messengers werden die IDs anscheinend immer als erste Kopfzeile gesendet und haben als Inhalt eine fortlaufende ASCII-Dezimalzahl ab 1001. Auch übermittelt der Yahoo!-Messenger die ID-Zeile immer als erste Kopfzeile. Dies ist vermutlich nicht notwendig, jedenfalls fügt der Odigo-Server seine ID-Zeile nicht immer als erste Kopfzeile ein.

Der erste vom Client zum Server übertragene Block ist ein "HELO"-Block, neben der ID-Zeile enthält dieser Block noch eine Protocol-Zeile ("Protocol: IMIP/1.0") sowie eine leere Zeile. Der Odigo-Server antwortet mit "Auth-Type: imip-md5", "Capabilities: server-lists", ID-Zeile, "Keep-Alive: 60", "Protocol: IMIP/1.0", "Registration: http://www.odigo.com/user", "Service: odigo.com", "ServiceDisplayName: Odigo", Leerzeile, ASCII-String (bei Odigo eine dezimale 32-Bit-ASCII-Zahl, als Salt für das Passwort). Die Zeilen scheinen recht eindeutig zu sein, die Keep-Alive-Zeile scheint zu steuern wie oft der Client einen PING-Block schickt. Der HELO-Block des Servers besitzt interessanterweise keine Reference-Zeile.

Danach sendet der Client einen "LOGN Odigo-Nummer"-Block. Die enthaltenen Zeilen sind: ID-Zeile, "Client: Yahoo! IMIP Client", "Auth-Type: imip-md5" und eine Leerzeile, danach kommt als Body eine 128-Bit-Zahl hexadezimal mit Kleinbuchstaben in ASCII-Darstellung. Diese Zahl ist der md5-Hash des Strings, der durch direktes aneinanderhängen des Salt-Strings aus dem HELO-Block des Servers und dem Passwort gebildet wird. An der UNIX-Kommandozeile kann dies mit dem m5sum-Befehl nachvollzogen werden. Der Wert für das Salt "1919833824" und das Passwort "Passwort" wird wie folgt berechnet: printf "1919833824Passwort" | md5sum. Der Server antwortet darauf mit einem "LOGN Odigo-Nummer"-Block. Kopfzeilen sind nur ID und Reference enthalten.

Anschließend setzt der Client sich online mit einem "STAT ONLINE"-Block, enthaltene Kopfzeilen: ID-Zeile und Leerzeile. Der Server antwortet mit einem "ACK 600"-Block, enthaltene Zeilen: ID und Reference. Andere Online-Stati sind "STAT BUSY", "STAT AWAY" und "STAT OFFLINE" (invisible). Dem Online-Status kann ein erklärender Text als Body des Blockes hinzugefügt werden.

Der Server informiert den Client über die Anwesenheit der Kontakte ebenfalls mit "STAT Status"-Blöcken. Enthaltene Kopfzeilen sind ID und "From: Odigo-Nummer". Es ist keine Leerzeile und kein Body enthalten. Auch bestätigt der Client den Empfang nicht.

Der Server setzt in seinem HELO-Block ein Intervall mit dem der Client PING-Blöcke sendet. Ein solcher Block enthält zwei Kopfzeilen: ID und eine leere. Der Server antwortet mit dem oben beschriebenen "ACK 600"-Block.

Eine abgehende Nachricht verschickt der Yahoo!-Messenger mit einem "MESG"-Block, der die folgenden Zeilen enthält: ID, "To: Odigo-Nummer", "ACK-Type: errors-only", "From: eigene Odigo-Nummer", Leerzeile gefolgt vom Nachrichtenbody. Vom Server kam keine Bestätigung, dies ist vermutlich über die ACK-Type-Zeile gesteuert.

Eine eingehende Nachricht wird ebenfalls in einem "MESG"-Block übertragen. Der Odigo-Server übermittelt dabei jedoch ein paar mehr Kopfzeilen als oben im "MESG"-Block des Clients. Enthalten sind: "Content-Type: text/plain;charset=utf-8", "From: Odigo-Nummer "Nickname"", ID-Zeile, "Time: yyyy-mm-ddThh:mm:ssZ" (Kleinbuchstaben im Datumsstring stehen für entsprechende Zahlen, Angabe in UTC), "To: eigene Odigo-Nummer", Leerzeile und der Nachricht im Body. Der Client bestätigt diesen Block nicht.

Die Abmeldung leitet der Client mit einem "DISC"-Block ein. Enthalten ist eine ID-Zeile und eine leere Zeile. Der Server bestätigt dies mit einem üblichen "ACK 600"-Block. Danach wird die Verbindung getrennt.

Auf die Kontaktliste wird ein Kontakt mit einem "LIST ADD Odigo-Nummer"-Block hinzugefügt. Enthaltene Kopfzeilen sind dabei: ID, "List: Buddy" (vermutlich Auswahl einer Gruppe), "From: Odigo-Nummer" und eine Leerzeile. Der Server antwortet sofort mit einem "ACK 600"-Block. Dass das Gegenüber die Sichtbarkeit erlaubt hat, erfährt man, indem man die Anwesenheit mit einem "STAT Online-Status"-Block übermittelt bekommt.

Wenn jemand einen selbst auf seine Kontaktliste hinzufügen möchte, so bekommt man einen "LIST ADD"-Block ohne Odigo-Nummer in der ersten Zeile. Enthaltene Kopfzeilen sind: "From: Odigo-Nummer "Nick-Name"", ID, "List: Buddy" (ich hatte den entsprechenden Kontakt in dieser Gruppe stehen), Leerzeile und der mit der Authorisierungsanforderung angegebene Text. Die Authorisierung wird mit einem "LIST ACCEPT Odigo-Nummer "Nick-Name""-Block angenommen. Enthaltene Kopfzeilen darin sind ID, From und eine Leerzeile. Der Server bestätigt dies mit einem "ACK 600"-Block.

Update: Ich vermute inzwischen, dass "List: Buddy" keine Gruppe ist, sondern es sich dabei um die Contact-List an sich handelt und andere Listen evtl. Invisible-List und dergleichen sein könnten. Ich habe hierzu jedoch noch keine weiteren Nachforschungen angestellt. Neu habe ich jedoch inzwischen herausgefunden, wie ein Kontakt von der Contact-List entfernt wird (Block des Typs "LIST REMOVE Odigo-Nummer" mit den Headern ID, "List: Buddy" und "From: Odigo-Nummer"; Bestätigung durch den Server mit einem "ACK 600"-Paket) und wie das Hinzufügen zur Contact-List abgelehnt wird (Block des Typs "LIST REJECT Odigo-Nummer" mit den Headern ID und "From: Odigo-Nummer").

Im LOGN-Block des Servers wird die Contact-List des Users gemeldet. Dies geschieht in der Header-Zeile Buddy, die eine Liste von Nutzern (und deren Nick-Name in Anführungszeichen) durch Kommata getrennt enthält.

Folgende weitere ACK-Statusnummern habe ich neben "ACK 600" bisher erhalten: 800 als ich einen STAT-Block ohne Status gesendet habe, 801 als ich einen STAT-Block mit ungültigem Status gesendet habe, 810 wenn ich beim Login ein falsches Passwort verwende, 811 wenn ich beim Login eine ungültige User-ID nutze oder an eine solche User-ID eine Nachricht schicke (die UserID war nicht numerisch, wie bei Odigo immer de Fall).

Einfache IMIP-Session als Beispiel, Beispiel mit Nachrichtentausch und Subscription, Testimplementierung eines einfachen Java-Odigo-Client auf IMIP-Basis.

IMUnified - protocol

posted on

In 2000, IM Unified announced to create a protocol that enables an instant messaging client to join different IM services. Founding members were MSN, Odigo, Yahoo! and others.

Since then I have not heard much of IMU. I don't even think that their IMIP protocol will ever be used really. But it does exist and e.g. the Yahoo! messenger client supports it. You only have to change some values in the Windows registry. Afterwards, you're client is able to join other IM services like Odigo with the Y! messenger.

For no other reason than to see how IMIP works I've dumped some IMIP sessions with a network sniffer. The IMIP protocol is very simple structured and looks a bit like SMTP or http.

All data is exchanged over a TCP/IP connection. The clients connects to port 11319 at the server. Firstly, it performs the login handshake and afterswards, it is exchanging messages, presences and other data. All data is exchanged in blocks.

All blocks have a structure of lines. Every line is finished by \r\n (carriage return, line feed). The first line contains the type of a block, the second line contains the size of the block (first two lines not counted). It is a decimal ASCII number containing the size in bytes. Starting at the third line, there are head lines, optionally followed by an empty line, optionally followed by a block body. If the body is empty, the empty line can be absent. (The Y! messenger is allways sending the empty line, the Odigo server isn't.)

As block types (content of the first line) I have noticed the following so far: "HELO" (before login, version check, negotiation of auth type), "LOGN odigo-number" (exchange of login data), "STAT presence" (exchange of presence information, presence is one of ONLINE, AWAY, BUSY, OFFLINE (other side going offline or this side going invisible), "ACK status" acknowledge (status 600 is OK, I got 811 after sending wrong login data), "LIST ADD odigo-number" (adding user to list), "PING" (keep-alive packet, interval set by server in HELO block), "MESG" (message), "LIST ACCEPT odigo-number" (accept being added by other side), "DISC" (logout).

The header lines always have the form "identifier: content\r\n". Every block has an ID line ("ID: idvalue"). This ID is mirrored in a reply by the "Reference: idvalue" header line, sent by the server. The Y! messenger starts to generate the IDs at "1001" and increments this value for every block. The Y! messenger is always sending the ID lines as the first header line. I don't think this is neccessary because the Odigo server is not always sending this line as first header line.

The first block that is transmitted from the client to the server is a "HELO" block. Besides the ID line it contains a "Protocol: IMIP/1.0" line. The Odigo server replies with another "HELO" block containing the following lines: "Auth-Type: imip-md5", "Capabilities: server-lists", the ID line, "Keep-Alive: 60", "Protocol: IMIP/1.0", "Registration http://www.odigo.com/user", "Service: odigo.com", "ServiceDisplayName: Odigo", an empty line and an ASCII string (Odigo uses the decimal ASCII representation of a 32 bit value), that is used as salt for the MD5 password exchange. The Keep-Alive line seems to control the interval of the client sending "PING" blocks. The HELO block does not contain a Reference line.

Afterwards, the client sends a "LOGIN odigo-number" block containing the following header lines: ID line, "Client: Yahoo! IMIP Client", "Auth-Type: imip-md5", an empty line and the MD5 hash of a string containing the salt concatenated with the user's password. The hash is represented as a hexadecimal ASCII value with small letters. At the UNIX command line you can reproduce the hash value with md5sum. e.g. if the salt in the HELO block was "1919833824" and your password is "password" you get the hash with: printf "1919833824password" | md5sum (the result should be c35900c24a82592dd2d0db27c647ff17). The server replies with a "LOGN odigo-number" block containing only ID and Reference header lines.

After login, the client sets its presence with a "STAT status" block (status being ONLINE, BUSY, AWAY or OFFLINE). This block only contains an ID header line. The server replies with an "ACK 600" block containing ID and reference header lines. The presence can be explained additionally by a message in the body of the block.

The server informs the client about the presence of the user's contacts with "STAT status" blocks as well. These blocks contain an ID header line and a "From: odigo-number" header line. The client doesn't reply these blocks.

Every time the interval set by the HELO block expires the client sends a "PING" block to the server. This block only contains the ID header line. The server replies with "ACK 600".

A message is sent by a "MESG" block that contains the following header lines: ID, "To: odigo-number", "ACK-Type: errors-only", "From: own-odigo-number". After an empty line, there is the body of the message. The server doesn't react on this, probably controlled by the ACK-Type line.

A message is also received by a "MESG" block. The server sends some more header lines: "Content-Type: text/plain;charset=utf-8", "From: odigo-number \"nickname\"", ID line, "Timo: yyyy-mm-ddThh:mm:ssZ" (time in UTC), "To: own-odigo-number", an empty line and afterwards the body with the message in it. The client doesn't react on this block.

The clients starts a disconnect with a "DISC" block containing only an ID header line. The server replies with an "ACK 600" block. Afterwards the connection is closed.

To add a contact to the list, the client sends a "LIST ADD odigo-number" block. The contained header lines are: ID, "List: Buddy", "From: odigo-number". The body can contain a message. The server replies immediately with an "ACK 600" block containing ID and Reference header lines. After the contact has accepted the subscription, the server sends a "STAT status" block.

If somebody else wants to add you to his list, the server sends a "LIST ADD" block without an Odigo number in the first line. This block contains the following header lines: "From: odigo-number \"nick\"", ID, "List: Buddy" (I had this user in the Buddy group). This block can contain a message in the body. The subscription is accepted by a "LIST ACCEPT odigo-number \"nick\"" block, that contains an ID and a From header line. It does not contain a Reference header line. The server replies with an "ACK 600" block.

Update: In the meantime I beleave that "List: Buddy" is no contact list group but just the contact list and that there could be other lists like a invisible list. I havn't done more research on this topic yet. New is what I found out how a contact is removed (block of type "LIST REMOVE odigo-number" with the headers ID, "List: Buddy" and "From: odigo-number"; acknowledged with A "ACK 600" block by the server) and how to reject a subscription (block of type "LIST REJECT odigo-number" with the headers ID and "From: odigo-number").

In the LOGN block of the server there is also the list of buddies on the contact-list. This in the Buddy header. The content of this header is a list of comma separated contacts (with their nick names appended in quotes).

I got the following ACK status numbers besides "ACK 600": 800 after I sent a STAT block without a status, 801 after I sent a STAT block with a non existant status, 810 if I try to log in with a wrong password, 811 if I try to log in with an illegal user id or send a message to an illegal user id (the id was non-numerical, Odigo uses numerical IDs).

Simple IMIP session as an example, IMIP session with message exchange and subscription, simple Java-Odigo-Client implementing the IMIP protocol.


Unless otherwise credited all material Creative Commons License by Matthias Wimmer