Browse Source

add dnsping scripts collection

Helmut Pozimski 7 years ago
parent
commit
237b479362
5 changed files with 235 additions and 0 deletions
  1. 1 0
      README.md
  2. 108 0
      dnsping/README.md
  3. 74 0
      dnsping/dnsping.py
  4. 27 0
      dnsping/dnsping_v6.sh
  5. 25 0
      dnsping/update_ip.sh

+ 1 - 0
README.md

@@ -7,6 +7,7 @@ This repository contains a collection of scripts written by me which are not big
 * voixicron.py: Simple script that checks for available updates on a Void Linux system with xbps-install and sends a report about the available updates to the administrator via e-mail
 * backup_report.sh: Bash script that generates a report about created backups using [storeBackup](http://storebackup.org/). Can be used to track the creation of backups and detect failed backups so they can be cleaned up manually
 * cyber_generator: wsgi script that creates a random word with the prefix "cyber", currently used at [cyber-everything.de](https://cyber-everything.de/)
+* dnsping: Collection of scripts to implement a basic dynamic dns service
 
 ## Copying
 

+ 108 - 0
dnsping/README.md

@@ -0,0 +1,108 @@
+# dnsping
+
+A collection of scripts to run your own dynamic DNS service. It is based on the assumption that you already have an authoritative DNS server running and a domain you want to use for your dynamic (sub)domain entry.
+
+## Requirements
+
+dnsping requires the following components to work:
+
+* Bind9
+* apache web server with mod_wsgi
+* bash
+* python 2
+* dig,nsupdate (usually part of the package "dnsutils")
+* curl (on the client side)
+
+## Components
+
+### dnsping.py
+
+This is the main script. It runs on the web server using python and mod_wsgi. It receives the changes to the IP address from the GET parameters in it's URL and saves it to a temporary file from where they can be processed by update_ip.sh which tells the DNS server to update the zone file. Currently dnsping.py does not perform any input validation and should be secured by configuring user authentication on the web server level.
+
+### update_ip.sh
+
+This bash scripts runs on the web server. It reads the files created by dnsping.py and passes the IP addresses on to the DNS server to update the zone file. It is a separate script so (if your setup requires it) you could periodically run it as root through cron or another mechanism and the web application can run without requiring any privileges.
+
+## dnsping_v6.sh
+
+This is an optional helper script that can be run on any client to update it's IPv6 address (which might change at regular intervalls) to the dnsping service. In a typical setup, the router will be assigned a prefix and will announce it to the other computers in the network so if you want to put the IPv6 address of a specific computer in your DNS record, it's currently the best option to query it directly at the client and update from there.
+
+## Installation and configuration
+
+### Apache2 and dnsping
+
+After installing the apache web server and the wsgi module, you need to create a vhost to use for dnsping. The script "dnsping.py" is supposed to be placed directly in the document root and has to be readable by the web server. The script requires a sub-directory called "tmp" to store the data submitted to it which has to be writable by the web server. A sample vhost to use with dnsping could look like this:
+
+```
+<VirtualHost IPv4 address:443 [IPv6 address]:443 >
+    ServerName $domain
+    ServerAdmin $email_address
+
+    DocumentRoot /var/www/dnsping
+    <Directory /var/www/dnsping>
+        Options -Indexes +FollowSymLinks +MultiViews
+        AllowOverride AuthConfig
+        Require all granted
+    </Directory>
+    <IfModule ssl_module>
+        SSLEngine On
+        SSLCertificateFile    $cert_path
+        SSLCertificateKeyFile $key_path
+        Header always set Strict-Transport-Security "max-age=15768000"
+    </IfModule>
+    WSGIScriptAlias / /var/www/dnsping/dnsping.py
+    WSGIDaemonProcess dnsping processes=1 threads=1 display-name=%{GROUP}
+    WSGIProcessGroup dnsping
+    ErrorLog ${APACHE_LOG_DIR}/dnsping_error.log
+    LogLevel warn
+    CustomLog ${APACHE_LOG_DIR}/dnsping_access.log combined
+</VirtualHost>
+```
+
+This assumes that your document root is /var/www/dnsping, please adapt everything to your needs. The options for authentication, HSTS (requires mod_headers) and TLS are optional but highly recommended. The user authentication would in this case happen with a .htaccess file in the document root but could also be defined directly in the vhost configuration.
+
+After everything is configured properly, you can test the functionality with a manuall call:
+
+```
+curl --user user:password "https://domain/?ipv6=ipv6_address&ipv4=ipv4_address"
+```
+
+If the call was successful, the addresses passed in the parameters will be saved in tmp/ipv4_address and tmp/ipv6_address.
+
+### Bind9
+
+The zone that contains the (sub)domain you want to update dinamically needs to already be configured in Bind. To be able to update the zone using nsupdate, the follwoing option needs to be set in the definition of the master zone:
+
+```
+update-policy local;
+```
+
+After setting this option, you should no longer edit the zone manually, all updates should happen using "nsupdate". Should manual changes be required, they should only happen after doing a "rndc freeze" first. Please read the bind manual if you any questions regarding the specific functionality.
+
+### update_ip.sh
+
+In this scripts, there are several variables that need to be filled to adjust everything to your specific setup. It should be placed somewhere the user that is executing it can access it and be made executable. Since there is no mechanism that would propagate updates automatically implemented currently, I assume that this script is executed as root via cron. Example:
+
+```
+* * * * * root /usr/local/sbin/update_ip.sh
+```
+
+Execution and correct functionally can of course be tested manually.
+
+### dnsping_v6.sh
+
+To execute this script on a client computer, the URL and the user credentials have to be entered into the script and a cron job created to execute it automatically. The script detects the currently active public IPv6 address from the MAC address and sumbits it to the server.
+
+
+## Updating the IP address from your router
+
+Some routers support configuring dynamic DNS services other than the ones preconfigured by the manufacturer. If yours does and you want to have your router automatically update your IP(v4) address each time it changes, a sample configuration (here for a Fritz Box router from AVM) would look like this:
+
+```
+Dynamic DNS provider: Custom
+Update URL: https://domain/?ipv4=<ipaddr>
+Domain: your sub domain
+User name: user name
+Password: password
+```
+

+ 74 - 0
dnsping/dnsping.py

@@ -0,0 +1,74 @@
+#! /usr/bin/env python2
+# -*- coding: utf8 -*-
+
+import os
+
+
+def application(environ, start_response):
+    parameters_list = environ['QUERY_STRING'].split("&")
+    param_value = {}
+    for item in parameters_list:
+        param, value = item.split("=")
+        param_value[param] = value
+    ipv4_file_path = os.path.join(environ['DOCUMENT_ROOT'], "tmp/ipv4_address")
+    ipv6_file_path = os.path.join(environ['DOCUMENT_ROOT'], "tmp/ipv6_address")
+    try:
+        file_v4 = open(ipv4_file_path, "r")
+        file_v6 = open(ipv6_file_path, "r")
+    except IOError:
+        old_ipv4 = ""
+        old_ipv6 = ""
+    else:
+        old_ipv4 = file_v4.readline()
+        old_ipv6 = file_v6.readline()
+        file_v4.close()
+        file_v6.close()
+    try:
+        new_ipv4 = param_value["ipv4"]
+    except KeyError:
+        new_ipv4 = ""
+    try:
+        new_ipv6 = param_value["ipv6"]
+    except KeyError:
+        new_ipv6 = ""
+    response_status = "200 IP didn't change"
+    response_body = ""
+    error = False
+    if new_ipv4 == "" and new_ipv6 == "":
+        # TODO: maybe implement some checking if it is a correct IPv4
+        # address here
+        response_status = "400 Bad Request"
+        response_body = "The IP address you defined was empty or " \
+                        "invalid."
+    else:
+        if new_ipv4 != old_ipv4 and new_ipv4 != "":
+            try:
+                file = open(ipv4_file_path, "w")
+            except IOError:
+                error = True
+            else:
+                file.write(param_value['ipv4'])
+                file.close()
+                response_status = "200 OK"
+                response_body = "The new IP address has been saved, thanks " \
+                                "and have a nice day!"
+        if new_ipv6 != old_ipv6 and new_ipv6 != "":
+            try:
+                file = open(ipv6_file_path, "w")
+            except IOError:
+                error = True
+            else:
+                file.write(param_value["ipv6"])
+                file.close()
+                response_status = "200 OK"
+                response_body = "The new IP address has been saved, thanks " \
+                                "and have a nice day!"
+    if error is True:
+        response_status = "503 Internal server error"
+        response_body = "There has been an error serving your request."
+
+    response_headers = [('Content-Type', 'text/plain'),
+                        ('Content-Length',
+                         str(len(response_body.encode('utf8'))))]
+    start_response(response_status, response_headers)
+    return [response_body.encode('utf8')]

+ 27 - 0
dnsping/dnsping_v6.sh

@@ -0,0 +1,27 @@
+#! /bin/bash
+
+# This script will determine the current IPv6 address of the host and call
+# an external URL to update the dynamic dns entry
+
+user=""
+password=""
+server_url=""
+
+if [ -e /tmp/dnsping_oldaddr ];
+then
+	old_addr=$(cat /tmp/dnsping_oldaddr)
+else
+	old_addr=""
+fi
+
+new_addr=$(ip -o -6 addr | grep -v -e fc00 -e fd00 | grep global | grep -v "preferred_lft 0sec" | grep -v temporary | awk '{print $4}' | cut -d "/" -f 1)
+
+if [ "$old_addr" != "$new_addr" ];
+then
+	if [ ! -z "$new_addr" ];
+	then
+		echo "detected new ip address: $new_addr"
+		curl --user ${user}:${password} "https://${server_url}/?ipv6=$new_addr"
+		echo "$new_addr" > /tmp/dnsping_oldaddr
+	fi
+fi

+ 25 - 0
dnsping/update_ip.sh

@@ -0,0 +1,25 @@
+#! /bin/bash
+
+# This script is used on the dns server to do the actual update of the zone file
+
+zone=""
+subdomain=""
+old_ipv4=$(dig +short a ${subdomain} @127.0.0.1)
+new_ipv4=$(cat /var/www/dnsping/tmp/ipv4_address)
+old_ipv6=$(dig +short aaaa ${subdomain} @127.0.0.1)
+new_ipv6=$(cat /var/www/dnsping/tmp/ipv6_address)
+
+if [ "$new_ipv4" != "$old_ipv4" ] || [ "$new_ipv6" != "$old_ipv6" ];
+then
+	nsupdate -l << EOF
+zone $zone
+update delete ${subdomain} A
+update delete ${subdomain} AAAA
+update add ${subdomain} 10 A $new_ipv4
+update add ${subdomain} 10 AAAA $new_ipv6
+show
+send
+EOF
+        logger "Updated dynamic IPv4 address from $old_ipv4 to $new_ipv4"
+        logger "Updated dynamic IPv6 address from $old_ipv6 to $new_ipv6"
+fi