Network2/Experiments/tinydns: Difference between revisions
No edit summary |
mNo edit summary |
||
(29 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{Network2 header}} |
{{Network2 header}} |
||
== |
== Workable DynDNS == |
||
There seems to be no widely-adopted standard API for (remotely) modifying DNS zone files. For example, the standardized DNS UPDATE protocols defined by RFCs [http://tools.ietf.org/html/rfc2136 2136] and [http://tools.ietf.org/html/rfc3007 3007] seem to be sparsely implemented at best. Other approaches, like [http://tools.ietf.org/html/draft-jennings-app-dns-update-02 draft-jennings-app-dns-update-02] have not been standardized. Finally, there are open problems with truth maintenance as described in other unstandardized work [http://files.dns-sd.org/draft-sekar-dns-ul.txt draft-sekar-dns-ul-01]. |
There seems to be no widely-adopted standard API for (remotely) modifying DNS zone files. For example, the standardized DNS UPDATE protocols defined by RFCs [http://tools.ietf.org/html/rfc2136 2136] and [http://tools.ietf.org/html/rfc3007 3007] seem to be sparsely implemented at best. Other approaches, like [http://tools.ietf.org/html/draft-jennings-app-dns-update-02 draft-jennings-app-dns-update-02] have not been standardized. Finally, there are open problems with truth maintenance as described in other unstandardized work [http://files.dns-sd.org/draft-sekar-dns-ul.txt draft-sekar-dns-ul-01]. |
||
Line 14: | Line 14: | ||
The server-to-client communication may be handled without undue difficulty by using DNS-SD to inform clients what port to connect to. |
The server-to-client communication may be handled without undue difficulty by using DNS-SD to inform clients what port to connect to. |
||
In the simplest case, suppose that we want to set up DNS-SD for a fixed instance named "primary" at olpcdyndns host |
In the simplest case, suppose that we want to set up DNS-SD for a fixed instance named "primary" at olpcdyndns host '''<tt><foo></tt>'''. |
||
In that case, we can use a single SRV record with priority 0, weight 0, zone |
In that case, we can use a single SRV record with priority 0, weight 0, zone |
||
primary._olpcdyndns1._tcp. |
primary._olpcdyndns1._tcp.'''<foo>''' |
||
and whatever hostname and port we like to point to our real olpcdyndns server. |
and whatever hostname and port we like to point to our real olpcdyndns server. |
||
Line 24: | Line 24: | ||
On the client, we can extract the specified host and port with |
On the client, we can extract the specified host and port with |
||
SRV=$(dig +short -t srv primary._olpcdyndns1._tcp. |
SRV=$(dig +short -t srv primary._olpcdyndns1._tcp.'''<foo>''') |
||
PORT=$(echo "$SRV" | cut -d' ' -f3) |
PORT=$(echo "$SRV" | cut -d' ' -f3) |
||
HOST=$(echo "$SRV" | cut -d' ' -f4-) |
HOST=$(echo "$SRV" | cut -d' ' -f4-) |
||
Line 30: | Line 30: | ||
Auxiliary information, if we had any, could be acquired via |
Auxiliary information, if we had any, could be acquired via |
||
TXT=$(dig +short -t txt primary._olpcdyndns1._tcp. |
TXT=$(dig +short -t txt primary._olpcdyndns1._tcp.'''<foo>''') |
||
If you want to get fancy, you could also loop over _olpcdyndns services with something like: |
If you want to get fancy, you could also loop over _olpcdyndns services with something like: |
||
for PTR in $(dig +short -t ptr _olpcdyndns1._tcp. |
for PTR in $(dig +short -t ptr _olpcdyndns1._tcp.'''<foo>'''); do |
||
SRV=$(dig +short -t srv "$SRV") |
SRV=$(dig +short -t srv "$SRV") |
||
... |
... |
||
Line 40: | Line 40: | ||
Next, what should we run on this carefully communicated host+port combination? |
Next, what should we run on this carefully communicated host+port combination? |
||
Depending on our preference, we can either use |
|||
ssh -p $PORT $HOST /usr/bin/olpc-dyndns-1-ssh-update |
|||
or we can use SSL with [http://en.wikipedia.org/wiki/Server_Name_Indication SNI] like so: (with openssl >= 0.9.8j) |
|||
openssl s_client -connect $HOST:$PORT -servername '''<foo>''' -cert <my_cert> -key <my_key> |
|||
to trigger /usr/bin/olpc-dyndns-1-ssl-update through something like [http://stunnel.org stunnel] or [http://www.superscript.com/ucspi-ssl/index.html ucspi-ssl]'s sslserver. |
|||
The mythical olpc-dyndns-1-ssh-update can read SSH_CONNECTION to find out the connecting IP; the mythical olpc-dyndns-1-ssl-update can read REMOTE_HOST and SSL_CLIENT_DN (with stunnel) or the [http://www.superscript.com/ucspi-ssl/environment.html sslserver equivalents]. |
|||
Setting |
|||
LogLevel VERBOSE |
|||
in <tt>/etc/ssh/sshd_config</tt> will cause SSH to log key fingerprints as well as accounts in case you want to manage everything from a single account. There doesn't seem to be any way (at present) to find out the key fingerprint of an active SSH session except by log-munging. (grr!). |
|||
... |
... |
||
The second is to switch from SSH (which has no native support for vhosting) to SSL, which does, via [http://en.wikipedia.org/wiki/Server_Name_Indication SNI]. |
|||
Going with openssl: |
|||
The model protocol then becomes something like (with openssl >= 0.9.8j) |
|||
openssl genrsa -out ca.key 1024 |
|||
openssl req -new -x509 -nodes -sha1 -days 9999 -key ca.key -out ca.cert |
|||
cat ca.cert ca.key > ca.pem |
|||
openssl genrsa -out client.key 1024 |
|||
openssl req -new -nodes -sha1 -days 9999 -key client.key -out client.csr |
|||
openssl x509 -req -in client.csr -out client.cert -CA ca.cert -CAkey ca.key -days 9999 -CAcreateserial |
|||
openssl verify -CAfile ca.pem client.cert |
|||
cat client.cert client.key > client.pem |
|||
cat > hiya <<EOF |
|||
#!/bin/bash |
|||
echo $SSL_CLIENT_DN |
|||
echo $SSL_CLIENT_I_DN |
|||
echo $REMOTE_HOST |
|||
EOF |
|||
chmod a+x ./hiya |
|||
# with ipv4 on localhost: |
|||
stunnel -p ca.pem -v 2 -A ca.cert -d 3001 -f -P "" -l ./hiya |
|||
openssl s_client -connect localhost:3001 -cert client.cert -key client.key -CAfile ca.cert |
|||
# openssl s_client doesn't support ipv6; see, e.g. openssl #1365, #1832 |
|||
sudo dnshash attach michael.mstone.info |
|||
stunnel -p ca.pem -v 2 -A ca.cert -d michael.mstone.info:3001 -f -P "" -l ./hiya |
|||
ncat -6 -v --ssl --ssl-key client.key --ssl-verify --ssl-cert client.cert michael.mstone.info 3001 |
|||
NB: |
|||
According to [http://tools.ietf.org/html/draft-jabley-dnsop-missing-mname-00 draft-jabley-dnsop-missing-mname-00], dyndns updates are supposed to go to the MNAME field of the SOA record of '''<foo>'''. |
|||
PRIMARY_MASTER=$(dig +short -t soa '''<foo>''' | cut -d' ' -f1) |
|||
djbdns doesn't contain native support for IPv6. However, the Debian package 'dbndns' seems to have added this support. |
|||
If you lack it, it's easy to calculate the entries for your AAAA records like so: |
|||
cat > tinydns_aaaa <<EOF |
|||
#!/usr/bin/python |
|||
import sys, socket |
|||
if len(sys.argv) < 3: |
|||
print "tinydns_aaaa <name> <ip> <ttl>" |
|||
exit(1) |
|||
print ":%s:28:%s:%s" % (sys.argv[1], "".join("\%o" % ord(c) for c in socket.inet_pton(socket.AF_INET6, sys.argv[2])), sys.argv[3]) |
|||
EOF |
|||
chmod a+x tinydns_aaaa |
|||
./tinydns_aaaa xs.mstone.info fe80::1 86400 |
|||
cat > tinydns_srv <<EOF |
|||
#!/usr/bin/python |
|||
import sys |
|||
if len(sys.argv) < 7: |
|||
print "tinydns_srv <service> <priority> <weight> <port> <name> <ttl>" |
|||
exit(1) |
|||
def format_short(n): |
|||
return "\\%03o\\%03o" % (n / 256, n % 256) |
|||
def format_name(n): |
|||
return "".join("\\%03o%s" % (len(a), a) for a in n.split(".")) + r'\000' |
|||
service = sys.argv[1] |
|||
priority = format_short(int(sys.argv[2])) |
|||
weight = format_short(int(sys.argv[3])) |
|||
port = format_short(int(sys.argv[4])) |
|||
name = format_name(sys.argv[5]) |
|||
ttl = sys.argv[6] |
|||
print ":%s:33:%s%s%s%s:%s" % (service, priority, weight, port, name, ttl) |
|||
EOF |
|||
chmod a+x tinydns_srv |
|||
./tinydns_srv _olpcdydns1._tcp.xs.mstone.info 0 0 3001 xs.mstone.info 86400 |
|||
however, if you've got the version with the IPv6 patches, then go ahead with something like |
|||
.xs.mstone.info:127.0.0.1:a:86400 |
|||
3xs.mstone.info:fe800000000000000000000000000002:86400 |
|||
3a.ns.xs.mstone.info:00000000000000000000ffff7f000001:259200 |
|||
quick reference: http://cr.yp.to/djbdns/tinydns-data.html |
|||
... |
|||
Should be straightforward to use [http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html dnsmasq] to provide an IPv6 front to an old-school tinydns... |
|||
also useful background: http://www.maradns.org/tutorial/recordtypes.html |
|||
openssl s_client -connect <foo> -servername <foo> -cert <my_cert> -key <my_key> |
Latest revision as of 01:58, 11 January 2010
Workable DynDNS
There seems to be no widely-adopted standard API for (remotely) modifying DNS zone files. For example, the standardized DNS UPDATE protocols defined by RFCs 2136 and 3007 seem to be sparsely implemented at best. Other approaches, like draft-jennings-app-dns-update-02 have not been standardized. Finally, there are open problems with truth maintenance as described in other unstandardized work draft-sekar-dns-ul-01.
So what are our real options?
The simplest thing that could possibly work would be to SSH or SSL to the DNS server we want to update. A successful SSH or SSL authentication binds together a username or client CN (which identifies the subdomain to update) and an IP address which we can use to generate the new RRset for that subdomain.
This will work well so long as we can commit up-front to an address and port number for our "olpcdyndns" server listen on. Unfortunately, it seems likely that large-scale providers of this olpcdyndns service will want to be able to provide service to multiple independent domains from a single machine, e.g. via vhosting.
To support vhosting, we need a way to communicate address/port information from the server to the client (for availability) and from the client back to the server (for integrity).
The server-to-client communication may be handled without undue difficulty by using DNS-SD to inform clients what port to connect to.
In the simplest case, suppose that we want to set up DNS-SD for a fixed instance named "primary" at olpcdyndns host <foo>.
In that case, we can use a single SRV record with priority 0, weight 0, zone
primary._olpcdyndns1._tcp.<foo>
and whatever hostname and port we like to point to our real olpcdyndns server.
On the client, we can extract the specified host and port with
SRV=$(dig +short -t srv primary._olpcdyndns1._tcp.<foo>) PORT=$(echo "$SRV" | cut -d' ' -f3) HOST=$(echo "$SRV" | cut -d' ' -f4-)
Auxiliary information, if we had any, could be acquired via
TXT=$(dig +short -t txt primary._olpcdyndns1._tcp.<foo>)
If you want to get fancy, you could also loop over _olpcdyndns services with something like:
for PTR in $(dig +short -t ptr _olpcdyndns1._tcp.<foo>); do SRV=$(dig +short -t srv "$SRV") ... done
Next, what should we run on this carefully communicated host+port combination?
Depending on our preference, we can either use
ssh -p $PORT $HOST /usr/bin/olpc-dyndns-1-ssh-update
or we can use SSL with SNI like so: (with openssl >= 0.9.8j)
openssl s_client -connect $HOST:$PORT -servername <foo> -cert <my_cert> -key <my_key>
to trigger /usr/bin/olpc-dyndns-1-ssl-update through something like stunnel or ucspi-ssl's sslserver.
The mythical olpc-dyndns-1-ssh-update can read SSH_CONNECTION to find out the connecting IP; the mythical olpc-dyndns-1-ssl-update can read REMOTE_HOST and SSL_CLIENT_DN (with stunnel) or the sslserver equivalents.
Setting
LogLevel VERBOSE
in /etc/ssh/sshd_config will cause SSH to log key fingerprints as well as accounts in case you want to manage everything from a single account. There doesn't seem to be any way (at present) to find out the key fingerprint of an active SSH session except by log-munging. (grr!).
...
Going with openssl:
openssl genrsa -out ca.key 1024 openssl req -new -x509 -nodes -sha1 -days 9999 -key ca.key -out ca.cert cat ca.cert ca.key > ca.pem openssl genrsa -out client.key 1024 openssl req -new -nodes -sha1 -days 9999 -key client.key -out client.csr openssl x509 -req -in client.csr -out client.cert -CA ca.cert -CAkey ca.key -days 9999 -CAcreateserial openssl verify -CAfile ca.pem client.cert cat client.cert client.key > client.pem cat > hiya <<EOF #!/bin/bash echo $SSL_CLIENT_DN echo $SSL_CLIENT_I_DN echo $REMOTE_HOST EOF chmod a+x ./hiya # with ipv4 on localhost: stunnel -p ca.pem -v 2 -A ca.cert -d 3001 -f -P "" -l ./hiya openssl s_client -connect localhost:3001 -cert client.cert -key client.key -CAfile ca.cert
# openssl s_client doesn't support ipv6; see, e.g. openssl #1365, #1832 sudo dnshash attach michael.mstone.info stunnel -p ca.pem -v 2 -A ca.cert -d michael.mstone.info:3001 -f -P "" -l ./hiya ncat -6 -v --ssl --ssl-key client.key --ssl-verify --ssl-cert client.cert michael.mstone.info 3001
NB:
According to draft-jabley-dnsop-missing-mname-00, dyndns updates are supposed to go to the MNAME field of the SOA record of <foo>.
PRIMARY_MASTER=$(dig +short -t soa <foo> | cut -d' ' -f1)
djbdns doesn't contain native support for IPv6. However, the Debian package 'dbndns' seems to have added this support.
If you lack it, it's easy to calculate the entries for your AAAA records like so:
cat > tinydns_aaaa <<EOF #!/usr/bin/python import sys, socket if len(sys.argv) < 3: print "tinydns_aaaa <name> <ip> <ttl>" exit(1) print ":%s:28:%s:%s" % (sys.argv[1], "".join("\%o" % ord(c) for c in socket.inet_pton(socket.AF_INET6, sys.argv[2])), sys.argv[3]) EOF chmod a+x tinydns_aaaa ./tinydns_aaaa xs.mstone.info fe80::1 86400
cat > tinydns_srv <<EOF #!/usr/bin/python import sys if len(sys.argv) < 7: print "tinydns_srv <service> <priority> <weight> <port> <name> <ttl>" exit(1) def format_short(n): return "\\%03o\\%03o" % (n / 256, n % 256) def format_name(n): return "".join("\\%03o%s" % (len(a), a) for a in n.split(".")) + r'\000' service = sys.argv[1] priority = format_short(int(sys.argv[2])) weight = format_short(int(sys.argv[3])) port = format_short(int(sys.argv[4])) name = format_name(sys.argv[5]) ttl = sys.argv[6] print ":%s:33:%s%s%s%s:%s" % (service, priority, weight, port, name, ttl) EOF chmod a+x tinydns_srv ./tinydns_srv _olpcdydns1._tcp.xs.mstone.info 0 0 3001 xs.mstone.info 86400
however, if you've got the version with the IPv6 patches, then go ahead with something like
.xs.mstone.info:127.0.0.1:a:86400 3xs.mstone.info:fe800000000000000000000000000002:86400 3a.ns.xs.mstone.info:00000000000000000000ffff7f000001:259200
quick reference: http://cr.yp.to/djbdns/tinydns-data.html
...
Should be straightforward to use dnsmasq to provide an IPv6 front to an old-school tinydns...
also useful background: http://www.maradns.org/tutorial/recordtypes.html