Gyepi Sam
2004-09-10
A simple and secure dynamic DNS update mechanism

This article describes how to automate remote updates of a dynamic DNS entry through SSH.

Introduction

My home network is connected to the Internet through a cable modem with IP addresses assigned through DHCP. I like to access the home network when I am away from home but can never remember whatever long-and-fairly-meaningless-hostname is currently assigned to my gateway. Of course, I don’t even try to remember the IP address. This is, of course, the very problem that DNS is designed to solve: I just need to assign a memorable hostname to the current IP address and make sure to keep it in sync when the address changes.

Solution

The home network uses a Linux gateway/firewall, and, fortunately, I rent server space, also Linux, off site to host, among others, DNS services and have a static IP address there. So whenever the cable modem IP address changes a DNS entry on the remote machine needed to be updated automatically. Easy enough, but I also wanted to automate the process and do it simply and securely. Automation is easy. Simple and secure are harder: I considered and discarded several solutions before I realized that the correct solution was right in front of me. I use ssh all the time and probably overlooked it because I was so used to using it solely in interactive shell mode. By creating a key pair without a password and limiting the key to running a single command, I can securely ssh from the gateway machine to the remote machine. Since the appropriate command is hard-coded in the public key, the client merely needs to connect and authenticate itself and does not need to specify any commands.

Steps

Create an ssh key pair without a password with the command:

$ ssh-keygen -t dsa -N "" -f dns.key

In this case, the key type is dsa and there is no password on the key. Obviously this is a security issue, but I guard all private key files equally well. By specifying an private key output filename of “dns.key”, I also get a file, “dns.key.pub”, which contains the corresponding public key.

Edit the public key file and prepend the ssh options to prevent login or unwarranted access:

command="/usr/bin/perl /etc/tinydns/dynupdate",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty

These options increase the security of the key tremendously. If the key were compromised. An attacker could only use it to connect to my machine and run a fairly harmless program with no side-effects.

Copy the private key file dns.key to the gateway machine and place it in a secure location. I put mine in /root/.ssh/dns.key

Copy the edited public key file dns.key.pub to the DNS machine and append it to /root/.ssh/authorized_keys

The file “/etc/tinydns/dynupdate”, which is a small perl script, does the update. I use djbns which has a line oriented configuration file that is particularly amenable to scripting. The script extracts the client’s IP address from the conveniently handy environment variable SSH_CONNECTION and writes the appropriate tinydns configuration line (in this case an A record specification) to a file. The script then changes into the tinydns config directory and runs make, which merges the dynamic and static data into a file that is then compiled into cdb format for tinydns.

The script, which would be even shorter without the error checking and safe file handling, is included here in it’s entirety:

#!/usr/bin/perl

use strict;

my $d='/etc/tinydns/root';
my $host = 'ext.praxis-sw.com';
my $ttl = 1800;
my $e;

unless ($e = $ENV{SSH_CONNECTION}){
  die "cannot get connection environment variable\n";
}

if (my ($client_ip) = $e =~ /^(\S+)/){
        my $f=qq[$d/dynamic.data];
        my $t=$f . 'tmp';  
        open(F, ">$t") or die "cannot open file $t. $!\n";
        print F qq[=$host:${client_ip}:$ttl\n];
        close F or die "cannot close file $t. $!\n";
        rename $t, $f or die "cannot rename file $t to $f. $!\n";
        chdir($d) or die "cannot chdir to $d. $!\n";
        exec '/usr/bin/make -s';

}
else {
        warn "cannot get client ip\n";
        exit 1;
}

The Makefile is small but useful.:

data.cdb: static.data dynamic.data
    test -e data && chmod 0600 data
    ( echo "#Do not edit. Generated data!"; cat static.data dynamic.data ) > data
    chmod 0400 data
    /usr/local/bin/tinydns-data

Now when I make an ssh connection from the gateway machine to the DNS machine using this key, the server updates the DNS entry and assigns the hostname ext.praxis-sw.com to the client’s IP address.

The final step is to add the ssh connection line:

ssh -i /root/.ssh/dns.key -n joad.praxis-sw.com

to the dhcp scripts. I use dhcpcd which executes the script /etc/dhcpcd/<interface>.exe whenever state changes so I put the line there.

Conclusion

With this simple and secure DNS updating process running, I can go anywhere and be sure that ext.praxis-sw.com points to my home network. I hope you have found this information useful. I would appreciate feedback or comments to improve it.