DNSSEC: Automatische Registrar Updates

Submitted by admin on So, 11.12.2016 - 11:33

Bei DNSSEC liegt das zentrale Problem darin, das der KSK (Key Signing Key) einer Zone im Rahmen des Key Rollover über den Registrar aktualisiert werden muss. Freundlicherweise hat InterNetX einen Testzugang zur Verfügung gestellt, damit der Prozess getestet werden kann.

Leider kocht jeder Registrar sein eigenes Süppchen. Das Skript ist derzeit mit den Registrarer InterNetX und Joker.com kompatibel.

Das Perl Script wurde auf einem Ubuntu Linux 14.04 getestet, es sollte ohne weitere Anpassungen auch mit Debian und CentOS laufen. Falls es nicht unter root läuft, dann muss mit visudo folgendes ergänzt werden:

dnssec keyserver = NOPASSWD: /usr/bin/ods-ksmutil

weil ods-ksmutil nur von dem opendnssec User oder root ausgeführt werden darf. Das Perl Script kann grundsätzlich auch mit Cron ausgeführt werden.

Download (Das Skript darf unter den Bedingungen der Gnu General Public License V3 genutzt werden)

#!/usr/bin/env perl
# Copyright (c) 2014 by Christian Felsing
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see .

use strict;
use warnings;

use LWP::UserAgent;
use Data::Dumper;
use MIME::Base64;

sub doUpdateJokerCom {
  my ($domain,$flags,$protocol,$algorithm,$publickey)=@_;

  use constant USERNAME     => 'USERNAME';
  use constant PASSWORD     => 'PASSWORD';
  use constant SERVICEPROXY => 'https://dmapi.joker.com/request';

  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new( GET => SERVICEPROXY . '/login?username=' . USERNAME . '&password=' . PASSWORD );
  my $res = $ua->request($req);
  unless ( $res->is_success ) {
      die "Failed: ", $res->status_line, "\n";
  }

      my $authId=undef;
      my $auth = $res->content();
      if ( $auth =~ /Auth-Sid:\s*(\S+)/ms ) {
    $authId="$1";
  }

  my $dnssec="1";
  my $publickey_base64=encode_base64("$publickey","");

  my $ds1="${protocol}:${algorithm}:${flags}:${publickey_base64}";
  my $reqLine='/domain-modify?auth-sid=' . $authId .
    '&domain=' . $domain .
    '&dnssec=' . $dnssec .
    '&ds-1=' . $ds1;

  print "$reqLine\n";
  $req = HTTP::Request->new( GET => SERVICEPROXY . "$reqLine");
  $res = $ua->request($req);
  my $output = undef;
  if ( $res->is_success ) {
      $output = $res->content;
  } else {
      die "Failed: ", $res->status_line, "\n";
  }
  if (defined($output)) {
    print Dumper($output);
  } else {
    print "No output\n";
  }
}

sub doUpdateInternetx {
  my ($domain,$flags,$protocol,$algorithm,$publickey)=@_;

  my $username='USERNAME';
  my $password='PASSWORD';
  my $context='CONTEXT';
  my $replyto='hostmaster@example.net';
  #my $serviceProxy='https://gateway.autodns.com';
  my $serviceProxy='https://demo.autodns.com/gateway/';
  my $xml=<<EOF
    <?xml version="1.0" encoding="utf-8" ?>
    <request>
    <auth>
    <user>${username}</user>
    <password>${password}</password>
    <context>${context}</context>
    </auth>
    <task>
    <code>0102</code>
    <domain>
    <name>${domain}</name>
    <dnssec>
    <flags>257</flags>
    <protocol>${protocol}</protocol>
    <algorithm>${algorithm}</algorithm>
    <publickey>${publickey}</publickey>
    </dnssec>
    </domain>
    <reply_to>${replyto}</reply_to>
    </task>
    </request>
  EOF
  ;
  print"$xml\n";

  my $ua = new LWP::UserAgent;
  $ua->agent('AgentName/0.1 ' . $ua->agent);
  $ua->timeout(1200);
  my $req = new HTTP::Request POST => ${serviceProxy};
  $req->content_type('text');
  $req->content($xml);
  my $time = localtime();
  my $res = $ua->request($req);
  print "HTTP Request ($time):\n".$req->as_string()."\n";
  if ($res->is_success) {
    my $res_str = "HTTP Antwort:\n".$res->as_string()."\n";
    print $res->as_string() . "\n";
    $res_str=~s/^\n.*\n$//sm;
    print $res_str . "\n";
  } else {
    print($res->error_as_HTML."\n");
  }
}

use constant MODE    => 'OPENDNSSEC';
use constant PDNSSEC => '/usr/bin/pdnssec';
use constant KSMUTIL => '/usr/bin/ods-ksmutil';
use constant DNSSEC_USER => 'pdns';

my @zones=("example.com","example.net");

foreach my $domain (@zones) {
  my $flags=undef;
  my $protocol=undef;
  my $algorithm=undef;
  my $publickey=undef;

  my $cmd;
  if (MODE eq "OPENDNSSEC") {
    $cmd="sudo ".KSMUTIL." key export --zone ${domain} --keystate active";
  } elsif (MODE eq "PDNS") {
    $cmd="sudo ".PDNSSEC."show-zone ${domain} 2>&1";
  } else {
    die "unknown mode ".MODE."\n\n";
  }
  open CMD,"$cmd|" || die "cannot exec $cmd\n\n";
  while (my $line=) {
    chomp $line;
    my @dnssecRes;
    if (MODE eq "OPENDNSSEC") {
      if ($line=~/^.+IN\s+DNSKEY\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+.*$/) {
        $flags=$1;
        $protocol=$2;
        $algorithm=$3;
        $publickey=$4;
        #doUpdateInternetx($domain, $flags, $protocol, $algorithm, $publickey);
        doUpdateJokerCom($domain, $flags, $protocol, $algorithm, $publickey);
      }
    } elsif (MODE eq "PDNS") {
      # !!! DOES NOT WORK, DO NOT USE!!!
      if ($line=~/KEY/) {
        @dnssecRes=split(/\s+/,$line);
        $flags=$dnssecRes[7];
        $protocol=$dnssecRes[8];
        $algorithm=$dnssecRes[9];
        $publickey=$dnssecRes[10];
      }
    }
  }
}

Die Stellen, die im Skript mit !!! markiert sind, müssen angepasst werden, für Tests sollten die Testsysteme der jeweiligen Registrare verwendet werden.

Haftungsausschluss

Es wird nur garantiert, dass das Skript Speicher- und CPU-Last erzeugt. Eine Haftung, insbesondere für Verlust der Erreichbarkeit oder der Domain, wird ausgeschlossen.

ToDo

Dem Tool fehlen noch zahlreiche Funktionen

Update nur bei Änderung KSK

Joker.com: Bisher nur .de vom Skript unterstützt. Das Skript muss noch für .com, .org und .net Domains erweitert werden. Weitere Domains laufen IMHO bei denen nicht mit DNSSEC

Fehlerbehandlung

  • Auswertung der Antwort Mail von InterNetX
  • Andere Registrare als InterNetX und Joker.com

Registrare

Wer DNSSEC nutzen will hat nur eine sehr kleine Auswahl an Registraren, in Deutschland scheint es nur die folgenden Registrare zu geben, die zumindest rudimentär DNSSEC können und ein API anbieten:

  • InterNetX: Scheint eine wirklich saubere Implementierung zu haben
  • Joker.com: Unterschiedliche Semantik für die Datenfelder für .de und generische Domains (.com, .net und .org).