×
Dustin Lennon

Dustin Lennon

Applied Scientist
dlennon.org

 
namecheap ddclient dynamic ip address multiple sites

Namecheap, Dynamic IP Addresses, and Hosting Multiple Sites

This post shows how to augment Namecheap's ddclient script to support multiple hosts on a dynamic IP.


Dustin Lennon
August 2015
https://dlennon.org/archive/20150816_sites
August 2015


 

This work was originally published as an Inferentialist blog post.

Introduction

Introduction

Last week, I went looking for a solution to the following problem: I have a single Linux server with a dynamically assigned IP address and I want to host several sites on this server. My registrar is Namecheap.com, and their advice is to use a Linux tool called ddclient.

Unfortunately, namecheap.com’s example doesn’t cover multiple hosts. A Google search pointed me to thornelabs.net, where the author describes a patch that can be applied to ddclient. Ddclient is written in Perl, so patching is a possibility, but one that feels a bit unsatisfactory.

Ddclient: The Post-execution Hook

Ddclient: The Post-execution Hook

Digging into the Perl script, I discovered that it provides a post-execution hook. This suggests a non-patch strategy: simply chain subsequent ddclient calls.

Here’s my base ddclient.conf file:

# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

use=web, web=dynamicdns.park-your-domain.com/getip
protocol=namecheap
login=inferentialist.com
postscript=/usr/sbin/ddpost     # the post-execution hook
password=CD012345678901234567890123456789
@
ddpost

ddpost

ddpost is the following python script, to be edited for your particular use case.

#!/usr/bin/python

import argparse
import tempfile
import os
import subprocess
import syslog
import sys

parser = argparse.ArgumentParser(description='run ddclient on secondary hosts')
parser.add_argument('ip_addr', help='script should be passed current ip address')
args = parser.parse_args()
ip_addr = args.ip_addr

host_passwords = {
    'inferentialist.com': 'AA012345678901234567890123456789',
    'dlennon.org'       : 'BB012345678901234567890123456789'
    }

host_subdomains = {
    'inferentialist.com': ['blog', 'api'],
    'dlennon.org': ['@']
}

config_template = """
    use=ip
    ip={ip_addr}
    protocol=namecheap
    login={host}
    password={password}
    {subdomain}
"""

ddconfig_template = """ddclient -file /tmp/{host}.conf -cache /tmp/{host}.cache -quiet"""

for host in host_passwords.keys():
    password = host_passwords[host]
    for subdomain in host_subdomains[host]:
        
        config_name = "/tmp/{0}.conf".format(host)
        cache_name = "/tmp/{0}.cache".format(host)

        config = config_template.format(**locals())
        with open(config_name, "w") as f:
            f.write(config)

        ddconfig_cmd = ddconfig_template.format(**locals())
        sys_msg = None
        try:
            subprocess.check_call(ddconfig_cmd.split(' '))
            sys_msg = "SUCCESS:  [ddclient postscript] updating {subdomain}.{host}: good: IP address set to {ip_addr}".format(**locals())
        except subprocess.CalledProcessError:
            sys_msg = "FAILED:  [ddclient postscript] updating {subdomain}.{host}".format(**locals())

        syslog.syslog(sys_msg)
            
        for fname in [config_name, cache_name]:
            try:
                os.unlink(fname)
            except OSError:
                pass

This will dynamically update four sites. inferentialist.com is handled by the initial call to ddclient. blog.inferentialist.com, api.inferentialist.com, and dlennon.org are updated via the ddpost script.

Note that each site specified in this pipeline should have a corresponding “A” record on namecheap.com. Moreover, subdomains should use the same password as “@” (e.g. root) domains.

The python script also pushes event records to /var/log/syslog.