ISPConfig Dynamic DNS
22 May 2019
3 minute read

[recipe, sysadmin]: How to create and update a DDNS entry in ISPConfig

If you manage your DNS server(s) with ISPConfig you may want a dynamic entry that gets updated automatically every time the target host changes its IP address.

Doing so in ISPConfig is quite straight forward: we can use the ISPConfig SOAP API to update an existing record with a PHP script to be run frequently by the target host.

In this recipe I will consider updating an A record.

  1. First we need to create a remote user on the ISPConfig master server to authenticate, and grant it remote access and some functions:

    In “System -> Remote User” add a new remote user, enable remote access and grantDNS zone functions” and each DNS functions corresponding to the type of record you want to update: so if you want to update an A record you must grant the generic “DNS zone functions” and the “DNS a functions”

  2. We then have to write a PHP script that will make a SOAP function call to the ISPConfig endpoint. The target host, or the host executing the script must have the PHP SOAP module installed.

    Create the update.php script as follow:

<?php
require('config.php');

if ($argc<3) {
	print "Usage: php ./update.php HOST DOMAIN [IP]\n";
	print "If not given, the ip address will be queried from icanhazip.com\n";
	die();
}
// First parameter: hostname of DNS entry
$ddns_host = $argv[1];
// Second: domain name of DNS zone
$domain = $argv[2];
// If third parameter is present
if ($argc>3) {
	// Third parameter: IP address of target host
	$ip = $argv[3];
// Otherwise
} else {
	// Figure out the public IP of this host:
	$ip = trim(file_get_contents("http://icanhazip.com/"));
	if (filter_var($ip, FILTER_VALIDATE_IP) === false) {
	die("Unable to retrieve public IP address (icanhazip.com returned $ip)\n");
	}
}

print("Setting DDNS host $ddns_host.$domain to IP $ip\n");

// Using the SOAP module initialize a SoapClient
$client = new SoapClient(null,
	array('location'   => $soap_location,
		'uri'        => $soap_uri,
		'trace'      => 1,
		'exceptions' => 1));

try {
	// Login to SOAP server
	$session_id = $client->login($soap_user, $soap_password);

	// Grab DNS zone ID
	$zone_id = $client->dns_zone_get_id($session_id, $domain);
	// Grab DNS zone
	$zone = $client->dns_zone_get($session_id, $zone_id);
	// Grab DNS records
	$records = $client->dns_rr_get_all_by_zone($session_id, $zone_id);
	
	// Find right record: hostname must match and type must be A
	$dns_record = null;
	foreach ($records as $rec) {
		if ($rec['type']=='A' && $rec['name']==$ddns_host) {
			$dns_record = $rec;
		}
	}
	// If no record found
	if (is_null($dns_record)) {
		//Logout from SOAP server
		$client->logout($session_id);
		die("Unable to find DNS record for host $ddns_host in domain $domain on the server...\n");
	}
	// If IP stored in record is different from current IP
	if ($dns_record['data'] != $ip) {
		// Set new IP
		$dns_record['data'] = $ip;
		// Increment record serial number
		$dns_record['serial'] = $dns_record['serial']+1;
		// Update modified record in DNS server
		$client->dns_a_update($session_id, null, $dns_record['id'], $dns_record);
		
		// Increment zone serial number
		$zone['serial'] = $zone['serial'] + 1;
		// Update modified zone in DNS server
		$client->dns_zone_update($session_id, 0, $zone_id, $zone);
		
		print("Successfully set DNS entry for host $ddns_host in domain $domain to $ip.\n");
	// Otherwise
	} else {
		print("IP address of $ddns_host.$domain already set to $ip.\n");
	}
	
	//Logout from SOAP server
	$client->logout($session_id);
	
} catch (SoapFault $e) {
	die('SOAP Error: '.$e->getMessage()."\n");
}

?>
Create the `config.php` as follow:
<?php
$soap_location = 'https://ispconfig.example.com:8080/remote/index.php';
$soap_uri = 'https://ispconfig.example.com:8080/remote/';
$soap_user = 'username';
$soap_password = 'password';
  1. Schedule the execution of update.php

END.