Dynamic DNS using OpenBSD syslog

Dynamic DNS server using NSD DNS server on OpenBSD with httpd and syslogd

Setup NSD

Create new zone file

vi /var/nsd/etc/nsd.conf

zone:
    name: "example.com"
    # under /var/nsd/zones/
    zonefile: "master/example.com.zone"

craete zone file(example)
/var/nsd/zones/master/example.com.zone

$ORIGIN example.com.  ; default zone domain
$TTL 86400            ; default time to live

@ IN SOA example.com. admin.example.com. (
           1663917795;serial
           28800       ; Refresh
           7200        ; Retry
           864000      ; Expire
           86400       ; Min TTL
           )

             NS          ns1.example.com.
             MX     10   smtp.example.com.

@            CAA    128  issue "letsencrypt.org"
@      300   IN     A    1.1.1.1;dynamic
;@      300   IN     AAAA 2002:0114:a244:1:5a9c:fcff:fe0f:fb16;dynamic6
home   300   IN     A    1.1.1.1;dynamic
smtp   300   IN     A    1.1.1.1;dynamic
dns          CNAME       nsd.example.com.

Enable NSD

rcctl enable nsd
rcctl start nsd

Script detect IP

vi /root/update-dns

#!/usr/bin/perl

my $dns_host = "example.com";
my $dns_url  = "/dns/update";
my $file     = '/var/nsd/zones/master/example.com.zone';

my $ipv6_host = '2002:%02x%02x:%02x%02x:1:5a9c:fcff:fe0f:fb16';
my $serial    = 'serial';
my $record    = 'dynamic';
my $record6   = 'dynamic6';

sub update {
	my $ip  = shift;
	my $ip6 = shift;

	open( $fh, "<$file" ) or die "File not found";
	my @lines = <$fh>;
	close($fh);

	my $change = 1;
	foreach (@lines) {
		if (/\d+;$serial/) {
			my $t = time;
			$_ =~ s/\d+;\Q$serial\E/$t;$serial/;
		}
		elsif ( $ip && /(\d+\.\d+\.\d+\.\d+);\Q$record\E/ ) {
			next if ( $ip eq $1 );
			$_ =~ s/\d+\.\d+\.\d+\.\d+;\Q$record\E/$ip;$record/;
			$change = 1;
		}
		elsif ( $ip6 && /(\S+);\Q$record6\E/ ) {
			next if ( $ip6 eq $1 );
			my $ip6 = sprintf( $ipv6_host, split( /\./, $ip ) );
			$_ =~ s/\S+;\Q$record6\E/$ip6;$record6/;
			$change = 1;
		}
	}

	if ($change) {
		open( $fh, ">$file" ) or die "File not found";
		print $fh @lines;
		close($fh);
	}

	return $change;
}

while (<STDIN>) {
	if (/\Q$dns_host\E (\d+\.\d+\.\d+\.\d+) .*GET \Q$dns_url\E/) {
		qx(rcctl reload nsd) if update( $1, 0 );
	}
	if (/\Q$dns_host\E ([\:0-9a-fA-F]) .*GET \Q$dns_url\E/) {
		qx(rcctl reload nsd) if update( 0, $1 );
	}
}

Detect via syslog

Dummy file

mkdir -p /var/www/htdocs/update-dns/dns/
echo "OK" > /var/www/htdocs/update-dns/dns/update

Config syslog

vi /etc/syslog.conf

daemon.info  | /usr/bin/doas /usr/bin/perl /root/update-dns
rcctl reload syslogd

Config httpd

vi /etc/httpd.conf

server "dns.example.com" {
  listen on * tls port 443
  tls {
    certificate "/root/.acme.sh/*.example.com/fullchain.cer"
    key "/root/.acme.sh/*.example.com/*.example.com.key"
  }
  default type text/plain
  root "/htdocs/update-dns"
  log syslog
}
rcctl enable httpd
rcctl start httpd
Last updated on 2022-11-14T00:00:00+00:00