Wednesday, June 11. 2014Get home, even being miles away
Under way again, but forgot some files from my home. I know, there's a network drive there, the data is on it. May I be so lucky to access it being physically separated from my home network? YES!™, follow this guide to be able to ...
DSL is a really great technology bringing the broadband access to me as an end user. Comparing to the times back then when I had a 56k modem and paid the connection hourly or whatever (well, still have it, a voice/fax modem ![]() So - they change my IP, but I still can access my home network from the outside. The solution is called dynamic DNS and is unbelievable simple - the current router IP is bound to some domain name, the domain stays constant while a new IP is rebound to it after router reconnection. That's actually common to know for every DSL user.
Now, there are many providers offering a dynamic DNS service, so why is it necessary to do it yourself? Well, some of them are free of charge, some not. Some are free of charge just now ![]() Things needed for this solution are - a server with root access - own domain and ability to administrate its DNS - a DSL router able to handle a custom dynamic DNS provider First lets start with the tinydns installation. On my server I use Debian wheezy, that one has no tinydns package. That's why I compile it from source which works quite well. The other dependencies - daemontools and ucspi-tcp are available from the standard repo. So first, installing those BASH: $ aptitude install daemontools ucspi-tcp Then making tinydns available BASH: $ cd /usr/local/src $ wget http://cr.yp.to/djbdns/djbdns-1.05.tar.gz ....... And following the instructions on the djbdns installation page http://cr.yp.to/djbdns/install.html. The default install path for the djbdns tools will be /usr/local/bin, so don't forget to add it to the PATH. After the installation some knowledge of how daemontools work is necessary to add the service. In short, in my case it was first init the tinydns service and then tell daemontools it's available BASH: $ groupadd Gtinydns $ groupadd Gdnslog $ tinydns-conf Gtinydns Gdnslog /usr/local/etc/tinydns 1.2.3.4 $ ln -s /usr/local/etc/tinydns /etc/service/ Where 1.2.3.4 is the IP of the server where tinydns is installed. The official document about tinydns setup is here http://cr.yp.to/djbdns/run-server.html. After installing the service you can check whether it's running with a command like this BASH: $ svstat /etc/service/tinydns or you can restart it with BASH: svc -t /etc/service/tinydns After this is done we have the tinydns service running, time to go forward. We need to delegate a subdomain to our DNS server, and we need to prepare our tinydns to handle it. The latter first. BASH: $ cd /usr/local/etc/tinydns/root $ ./add-ns dyn.mydomain.com 1.2.3.4 $ ./add-host myhome.dyn.mydomain.com 5.6.7.8 Note that 5.6.7.8 is subject of the change later, so that doesn't matter much what IP you assign at this stage. It's just practical to set it to your current dynamic IP to experiment with it later, yet where you have no update mechanism. Thus, after invoking the commands above you should see in your data file something similar to TEXT: .dyn.mydomain.com:1.2.3.4:a:259200 =myhome.dyn.mydomain.com:5.6.7.8:86400 Note the last number at the end of each line, like 86400 - that's TTL in seconds of the corresponding record. For the dynamic IPs it makes sense to set it some shorter, like for instance one minute. You can edit the data file manually and would get a line like TEXT: =myhome.dyn.mydomain.com:5.6.7.8:60 After the edit is done, run make which will compile the data file and produce data.cdb usable by tinydns. The domain dyn.mydomain.com is a subject of the delegation which has to be done by your domain hoster, or often just in some web interface there. This topic will probably cost you some time, but not because it's huge. Every DNS record has some TTL which will prevent any changes to be propagated immediately. Also, the concrete steps to delegate a zone to your server will vary hardly depending on the hosting provider. That's where you probably should be more active to read the provider docs and maybe contact the support to achieve the goals. for me, it has just worked with an online DNS editor in my providers account. Given that the rights was delegated to dyn.mydomain.com, you should see something similar to the following BASH: $ dig dyn.mydomain.com ; <<>> DiG 9.7.0-P1 <<>> dyn.mydomain.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58085 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 ;; QUESTION SECTION: ;dyn.mydomain.com. IN A ;; AUTHORITY SECTION: dyn.mydomain.com. 2560 IN SOA a.ns.dyn.mydomain.com. hostmaster.dyn.mydomain.com. 1402383961 16384 2048 1048576 2560 ;; Query time: 52 msec ;; SERVER: 213.73.91.35#53(213.73.91.35) ;; WHEN: Tue Jun 10 14:34:57 2014 ;; MSG SIZE rcvd: 83 as you see, dyn.mydomain.com is listed as SOA or "start of authority", which means our tinydns server is authorized to manage all the domains within *.dyn.mydomain.com . tinydns also exposes a.ns.dyn.mydomain.com as NS server. You can add more with ./add-ns if your server has more IPs. You can also setup another tinydns instance on the other server and replicate the changes there. Note also, that you don't necessarily need to use a subdomain here, something like a second level domain were also appropriate and for sure some shorter )) Note that it may take some time to the NS of your domain provider after you've configured the delegation to propagate the changes. Some NS have TTL of 2-3 hours, or even longer. But after the configuration became operative, the changes are visible with the dig tool. If everything is working correctly, you should be able to access myhome.dyn.mydomain.com which is bound to your real dynamic IP, yet manually. The next part is not less tricky, and that is to synchronize your dynamic IP to the running tinydns instance. For that, we'll need to do two things - an update script available through HTTP/HTTPS - a cron job to update the data in tinydns As DSL router, I have a FRITZ!Box, which supports some commercial and free dynamic DNS providers out of the box. Besides that, it supports a custom DNS provider and that's what I'm going to exploit ![]() TEXT: http://somewhere.com/update.php?username=<username>&pass=<pass>&domain=<domain>&ipaddr=<ipaddr>&ip6addr=<ip6addr> The router will replace all the placeholders with the actual data when sending an update request. On the other hand, you see which parameters the update.php has to handle. For how to do it with some other DSL router please consult the concrete documentation, but I'm pretty sure it will go a similar way defining an URL with some placeholders. While this is out of scope of this article, but ... Even though your router might not support a dynamic DNS provider, you can use a script inside your home network and still call the update URL. There are several ways to get your public IP then, for example you could get it with like `curl ident.me` or just write a similar service on your own server besides the update.php, that's easy done with PHP. Of course for this to work you'll need a machine running in your private network, but nowadays it could be (and indeed is) some light toy like Raspberry Pi. But do not setup your router with the update URL yet, first lets prepare the update infrastructure. The update.php is actually simple - accept the parameters, authenticate, write the update to the temporary location. I'm not going to post my complete script as that's specific to what I have and is a bit too long, bit here's the general approach PHP: <?php $users = array( array( 'username' => 'me', 'pass' => 'mypass', 'domain' => array( 'myhome.dyn.mydomain.com', ), ), ); class TinyDNS { function scheduleUpdate($domain, $ip4) { /* XXX IP sanity check*/ $target = "/tmp/ddns_updates/$domain"; if (file_put_contents($target, $ip4, LOCK_EX) !== strlen($ip4)) { throw new Exception("Could not schedule update for '$domain', invalid write"); } } } class User { protected $users = NULL; protected $user_data = NULL; function __construct($users) { $this->users = $users; } function auth($username, $pass) { foreach ($this->users as $user) { if ($user["username"] == $username && $user["pass"] == $pass) { $this->user_data = $user; return true; } } return false; } function hasDomain($domain) { /* XXX domain sanity check */ if (isset($this->user_data["domain"])) { foreach ($this->user_data["domain"] as $item) { if ($domain == $item) { return true; } } } return false; } } /* Handle the request */ $username = isset($_GET["username"]) ? $_GET["username"] : NULL; $pass = isset($_GET["pass"]) ? $_GET["pass"] : NULL; $domain = isset($_GET["domain"]) ? $_GET["domain"] : NULL; $ipaddr = isset($_GET["ipaddr"]) ? $_GET["ipaddr"] : NULL; $ip6addr = isset($_GET["ip6addr"]) ? $_GET["ip6addr"] : NULL; $user = new User; try { if (!$user->auth($username, $pass)) { http_response_code(403); exit; } if (!$user->hasDomain($domain)) { http_response_code(412); exit; } $tdns = new TinyDNS; $tdns->scheduleUpdate($domain, $ip4); } catch (Exception $e) { http_response_code(500); exit; } http_response_code(200); Be aware that I totally miss sanity check here for domain and IP correctness as well as some other security stuff. This script is really just a possible approach for you on how to apply for your own circumstances. Generally this is enough to get the updater working and to record the update info. Right now as you can see, I'm storing the new IP4 into "/tmp/ddns_updates/$domain", that's safe for the case a router will send an update twice as only the latest update info will stay. That obviously will need to change if we'll need to update some more, for example the IPv6 too. Or an interesting idea I had - besides the standard parameters using placeholders, we actually could use some additional parameters to translate TTL for example. While not common, they still could be usable and handled. And after this, we're a half step on the way to the goal. Now what is needed is to update the actual DNS records using data sent by a router. For that I'm using a cron job, as we know good writing to the sensitive locations directly from the web space is a big security hole, while doable. My cronjob is written in bash and looks as follows BASH: #!/bin/bash TINYDNS_UP_LOCK=/tmp/tinydns.up.lock TINYDNS_SV_ROOT=/etc/service/tinydns/root UPDATES_DIR=/tmp/ddns_updates DATA_TMP="$TINYDNS_SV_ROOT/data.tmp" DATA_NEW="$TINYDNS_SV_ROOT/data" if [ -f "$TINYDNS_UP_LOCK" ] then echo Another update is running, exitinng exit 0 fi ls -1 $UPDATES_DIR/* &> /dev/null if [ "$?" -ne "0" ] then # no updates, nothing to do exit 0 fi touch "$TINYDNS_UP_LOCK" # first loop to read the lines from data # that means - adding new hosts only can be added manually to tinydns data file cd "$TINYDNS_SV_ROOT" cat "$DATA_NEW" | while read LINE do ls -1 $UPDATES_DIR/* &> /dev/null if [ "$?" -ne "0" ] then # probably all the updates was applied, so take as is echo $LINE >> "$DATA_TMP" continue fi for FL_PATH in $UPDATES_DIR/* do DOMAIN=`basename "$FL_PATH"` REC=`echo $LINE | cut -d: -f1` if [ "=$DOMAIN" == "$REC" ] then # 60 seconds TTL for all, that's it for now IP=`cat "$FL_PATH"` echo "=$DOMAIN:$IP:60" >> "$DATA_TMP" rm "$FL_PATH" else # not this domain, leave untouched echo $LINE >> "$DATA_TMP" fi done done mv "$DATA_TMP" "$DATA_NEW" make 1> /dev/null rm "$TINYDNS_UP_LOCK" exit 0 Running this script as a cronjob every minute is actually fine with the TTL of one minute in the DNS. That means we possibly can have a gap of 1 minute where the DNS record will point to a wrong IP, at the moment I can live with that. Of course, there are some more complicated solutions which will drive that gap to zero. Whereby, the DSL reconnection will bring some gap, too. So no general way to avaid it. At the end here, I just have to mention again - don't forget to run make after any changes to the data file - if you wonder what's wrong with DNS, first see which TTL the records have, then check fresh after it has expired So now, be under way and prepare yourself to be able to - VPN - FTP/FTPS - network to network coupling - beyond services your net can expose to the outta world Links Basic FRITZ!Box dynamic DNS docs http://cr.yp.to/djbdns/run-server.html http://cr.yp.to/daemontools/start.html http://cr.yp.to/daemontools/faq/create.html http://thedjbway.b0llix.net/djbdns/tinydns-data.html As always, thanks for the attention and enjoy ![]()
Posted by Anatol Belski
in Bash, Dynamic DNS, FRITZ!Box, PHP5, Web
at
14:01
| Comments (0)
| Trackbacks (0)
Trackbacks
Trackback specific URI for this entry
No Trackbacks
|
QuicksearchCategoriesArchives |