Wednesday, January 02, 2013

Set up your own Dynamic DNS

The problem with external dynamic DNS services like dyndns.org, no-ip.com, etc. is that you constantly have to look after them. Either they are free, but they expire after 1 month and you have to go to their web site to re-activate your account. Or you pay for them, but then you need to take care of the payments, update the credit card info, etc. This is all much too cumbersome for something that should be entirely automated.

If you manage your own DNS anyway, it may be simpler in the long run to set-up your own dynamic DNS system.

Bind has everything needed. There is a lot of info on the Internet on how to do it, but what I found tended to be more complicated than becessary or insecure or both. So here is how I did it on a Debian 6 ("squeeze") server.

The steps described below are:

Initialize variables

To make it easier to copy/paste commands, we initialize a few variables

binddir="/var/cache/bind"
etcdir="/etc/bind"

(In Debian, you can use grep directory /etc/bind/named.conf.options to find the correct binddir value)

For dynamic hosts, we will use a subdomain of our main zone: .dyn.example.com.

host=myhost; zone=dyn.example.com

Create key

Most example use the dnssec-keygen command. That would create 2 files (with ugly names): one .private and one .key (public) file. This is useless since the secret key is the same in both files, and the nsupdate method doesn't use a public/private key mechanism anyway.

There is a less-known and more appropriate command in recent distributions : ddns-confgen. By default, it will just print sample entries with instructions to STDOUT. You can try it out with:

ddns-confgen -r /dev/urandom -s $host.$zone.

The options we use here are to use an "hmac-md5" algorithm instead of the default "hmac-sha256". It simplifies things with nsupdate later. And we also specify the key name to be the same as the host's name. That way, we can use a wildcard in the "update-policy" in named.conf.local and don't need to update it every time we add a host.

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys

chown root:bind   $etcdir/$zone.keys
chmod u=rw,g=r,o= $etcdir/$zone.keys

Depending on how you intend to use nsupdate, you may want to also have a separate key file for every host key. nsupdate cannot use the $zone.keys file if it contains multiple keys. So you might prefer to directly create these individual keyfiles by adding something like > $etcdir/key.$host.$zone :

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys > $etcdir/key.$host.$zone

chown root:bind   $etcdir/$zone.keys $etcdir/key.*
chmod u=rw,g=r,o= $etcdir/$zone.keys $etcdir/key.*

Configure bind

Create zone file

Edit $binddir/$zone :

$ORIGIN .
$TTL  3600 ; 1 hour

dyn.example.com IN SOA  dns-server.example.com. hostmaster.example.com. (
         1 ; serial (start at 1 for a dynamic zone instead of the usual date-based serial)
      3600 ; refresh by secondaries (but they get NOTIFY-ed anyway)
       600 ; retry (every 10 minutes if refresh fails)
    604800 ; expire (slaves remove the record after 1 week if they could not refresh it)
       300 ; minimum ttl for negative answers (5 minutes)
)

$ORIGIN dyn.example.com.
NS      dns-server.example.com.

Edit /etc/bind/named.conf.local

Edit /etc/bind/named.conf.local to add :

// DDNS keys
include "/etc/bind/dyn.example.com.keys";

// Dynamic zone
zone "dyn.example.com" {
    type master;
    file "/var/cache/bind/dyn.example.com";
    update-policy {
        // allow host to update themselves with a key having their own name
        grant *.dyn.example.com self dyn.example.com.;
    };
};

Reload server config

rndc reload && sleep 3 && grep named /var/log/daemon.log | tail -20

(adjust the sleep and tail values depending on the number of zones your DNS server handles, so that it has time to report any problems)

Test

If you created individual key files, or your $zone.keys file contains only a single key, you can test like this:

host=myhost; ip=10.11.12.13; zone=dyn.example.com; server=dns-server.example.com; keyfile=$etcdir/key.$host.$zone
echo -e "server $server\n zone $zone.\n update delete $host.$zone.\n update add $host.$zone. 600 A $ip\n send" | nsupdate -k "$keyfile"

Or, more readable and with an extra TXT record:

cat <<EOF | nsupdate -k $keyfile
server $server
zone $zone.
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"
send
EOF

(If you get a could not read key from $keyfile: file not found error, and the file actually exists and is owned by the bind process user, you may be using an older version of nsupdate (like the version in Debian Etch). In that case, replace nsupdate -k $keyfile with nsupdate -y "$key_name:$secret" using the key name and secret found in your key file.)

Check the result:

host -t ANY $host.$zone

It should output something like

myhost.dyn.example.com descriptive text "Update on Tue Jan  1 17:16:03 CET 2013"
myhost.dyn.example.com has address 10.11.12.13
If you try to use a file with multiple keys in the -k option to nsupdate, you will get an error like this:

... 'key' redefined near 'key'
could not read key from FILENAME.keys.{private,key}: already exists

Usage

In a /etc/network/if-up.d/ddnsupdate script.

If you have setup an update CGI page on your server, you could use something like this, letting the web server use the IP address it received anyway with your request.

#!/bin/sh

server=dns-server.example.com
host=myhost
secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

wget -O /dev/null --no-check-certificate "https://$server/ddns/update.cgi?host=$host;secret=$secret"
Otherwise, you can use nsupdate, but you need to determine your external IP first :

#!/bin/sh

server=dns-server.example.com
zone=dyn.example.com
host=myhost
secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

ip=$(wget -q -O - http://example.com/myip.cgi)

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"
send
EOF

I used a very simple myip.cgi script on the web server, to avoid having to parse the output of the various existing services which show your IP in the browser:

#!/bin/sh
echo "Content-type: text/plain"
echo ""
echo $REMOTE_ADDR

This alternative script example uses SNMP to get the WAN IP from the cable router. It only does the update if the address has changed, and logs to syslog.

#!/bin/sh

zone=dyn.example.com
host=myname
secret="nBlw18hxipEyMEVUmwluQx=="
router=192.168.81.3

server=$(dig +short -t SOA $zone | awk '{print $1}')

ip=$( snmpwalk -v1 -m RFC1213-MIB -c public $router ipAdEntAddr | awk '!'"/$router/ {print \$4}" )

if [ -z "$ip" ]; then
 echo "Error getting wan ip from $router" 1>&2
 exit 1
fi

oldip=$(dig +short $host.$zone)

if [ "$ip" == "$oldip" ]; then
 logger -t `basename $0` "No IP change for $host.$zone ($ip)"
 exit
fi

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"
send
EOF

logger -t `basename $0` "IP for $host.$zone changed from $oldip to $ip"

Web server update.cgi

An example update.cgi :

#!/usr/bin/perl

## Use nsupdate to update a DDNS zone.

## (This could be done with the Net::DNS module. It
##  would be more portable (Windows, etc.), but also
##  more complicated. So I chose the nsupdate utility
##  that comes with Bind instead.)

# "mi\x40alma.ch", 2013

use strict;

my $VERSION = 0.2;
my $debug = 1;

my $title = "DDNS update";

my $zone     = "dyn.example.com";
my $server   = "localhost";
my $nsupdate = "/usr/bin/nsupdate";


use CGI qw(:standard);

my $q = new CGI;

my $CR = "\r\n";

print $q->header(),
      $q->start_html(-title => $title),
      $q->h1($title);


if (param("debug")) {
    $debug = 1;
};

my $host   = param("host");
my $secret = param("secret");
my $ip     = param("ip") || $ENV{"REMOTE_ADDR"};
my $time   = localtime(time);

foreach ($host, $secret, $ip) {
    s/[^A-Za-z0-9\.\/\+=]//g; # sanitize, just in case...
    unless (length($_)) {
        die "Missing or bad parameters. host='$host', secret='$secret', ip='$ip'\n";
    }
}

my $commands = qq{
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated by $0 v. $VERSION, $time"
send
};

print $q->p("sending update commands to $nsupdate:"), $CR,
      $q->pre($commands), $CR;

open( NSUPDATE, "| $nsupdate" ) or die "Cannot open pipe to $nsupdate : $!\n";
print NSUPDATE $commands        or die "Error writing to $nsupdate : $!\n";
close NSUPDATE                  or die "Error closing $nsupdate : $!\n";

print $q->p("Done:"), $CR;

my @result = `host -t ANY $host.$zone`;

foreach (@result) {
    print $q->pre($_), $CR;
}


if ($debug) {
# also log received parameters
    my @lines;
    for my $key (param) {
        my @values = param($key);
        push @lines, "$key=" . join(", ", @values);
    }
    warn join("; ", @lines), "\n";
}

print $q->end_html, $CR;

__END__

Labels: , , , , , , , ,

Monday, June 22, 2009

Pirater la musique, c'est mal

...

Il faut l'acheter pour que les multinationales du disque ne finissent pas comme les constructeurs de voitures américains artistes gagnent quelque chose. Alors j'essaye...

J'étais allé voir iTunes il y a des années, mais c'était réservé aux américains, et ne proposait que des fichiers cryptés que je ne pouvais pas écouter sur l'appareil de mon choix.

Puis sont venus les .mp3 de Amazon.com. Je m'y suis précipité, mais ils sont réservés aux américains.

Depuis, iTunes est disponible en Suisse, et il paraît qu'ils ont enfin renoncé au DRM dans leurs fichiers. Alors j'essaye itunes.ch. Pour découvrir que je ne peux télécharger de la musique que si j'installe le programme iTunes sur un ordinateur Mac ou Windows. Non merci.

Après de longues recherches par Google qui me propose des dizaines de sites et de manières de télécharger immédiatement la musique que je souhaite par divers moyens illégaux, je découvre Qobuz.com qui semble très intéressant. Hélas, c'était durant un week-end où leurs serveurs souffraient de pannes, et je n'arrive à rien.

Quelques semaines plus tard je retourne sur Qobuz.com, et j'y fais mon choix. Au moment de "passer à la caisse", je suis envoyé chez ClickandBuy qui semble être leur seul moyen de payement. ClickandBuy prend note de toutes mes données de carte de crédit, adresse, téléphone, etc. puis doit absolument m'envoyer un SMS avec un code de confirmation. Hélas, le code n'arrive jamais, malgré 2 tentatives, et mes vérifications minutieuses du numéro de téléphone portable que je leur donne.

Mais j'ai entendu dire que Amazon France offrait enfin aussi des .mp3. J'y trouve ce que je veux, mais au moment de mettre la musique à télécharger dans mon "panier", je reçois ce message: "Message important Nous sommes désolés. Nous n'avons pas pu traiter votre commande en raison de restrictions géographiques appliquées au produit que vous avez essayé d'acheter. Veuillez consulter les conditions d'utilisation de ce produit pour déterminer les restrictions géographiques applicables. Nous nous excusons pour la gêne occasionnée."

Heureusement, je peux écouter toutes ces musiques chez mon voisin, qui semble connaître de meilleurs moyens pour se procurer de la musique rapidement et sans complications...

Labels: , , , , ,

Friday, April 24, 2009

The Is Your New Bicycle Meme Revisited

Even though the '... Is Your New Bicycle' meme is already over a year old, I couldn't resist adding to it. So here is a new meta-meme-site for your amusement:

allyournewbicylces.com fetches new 'Is Your New Bicycle' quotes for you

It has a list of the best sites which are still alive, selects one, gets you a fresh quote from it, and displays it using the obligatory layout, with a helpful link to the original source site in the corner.

And in case you find it easier to remember, it even has an alternative name: thisisyournewbicycle.com.

If you had not heard about this American election year meme, this is the original site (which even became a book), and here is an article about it.

Labels: ,