updates for more flags
This commit is contained in:
23
DDNS.pm
23
DDNS.pm
@@ -235,6 +235,27 @@ sub type {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Set (create or replace) an existing control-plane record. This is used by
|
||||||
|
# the add-* scripts when --edit is specified.
|
||||||
|
# It deletes the existing TXT RDATA for the fqdn and writes the new value.
|
||||||
|
sub set {
|
||||||
|
my ($this, $dom, $value, $type) = @_;
|
||||||
|
_validateOrDie($dom);
|
||||||
|
my $fqdn = _fqdn($dom, $type);
|
||||||
|
|
||||||
|
# If it doesn't exist, fail explicitly; callers can decide whether to add.
|
||||||
|
my $existingtype = $this->type($dom);
|
||||||
|
die "Domain $dom does not exist; cannot edit"
|
||||||
|
unless $existingtype;
|
||||||
|
die "Domain $dom exists as type $existingtype; cannot edit as $type"
|
||||||
|
if (defined $type && $existingtype ne $type);
|
||||||
|
|
||||||
|
$this->__docmd("update delete $fqdn TXT");
|
||||||
|
$this->__docmd("update add $fqdn 60 TXT " . _quote_txt_rdata($value));
|
||||||
|
$this->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
sub add {
|
sub add {
|
||||||
my ($this, $dom, $master, $type) = @_;
|
my ($this, $dom, $master, $type) = @_;
|
||||||
_validateOrDie($dom);
|
_validateOrDie($dom);
|
||||||
@@ -270,7 +291,7 @@ sub cleanup {
|
|||||||
# Merge the .jnl file in with the domain file
|
# Merge the .jnl file in with the domain file
|
||||||
system("rndc sync -clean private.invalid");
|
system("rndc sync -clean private.invalid");
|
||||||
# Rebuild and reload the master vhosts files
|
# Rebuild and reload the master vhosts files
|
||||||
system("/usr/local/bin/sync-master-vhosts");
|
system("/usr/local/bin/sync-unified-master");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,47 +2,64 @@
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Martnet::DDNS;
|
|
||||||
use Regexp::Common qw/net/;
|
|
||||||
use Getopt::Long qw/GetOptions/;
|
|
||||||
use JSON::PP qw/encode_json true false/;
|
|
||||||
|
|
||||||
sub usage {
|
use Getopt::Long qw/GetOptions/;
|
||||||
die "Usage: add-custom [--enable-dnssec|--disable-dnssec] --master <IPv4|IPv6> <zone.>\n";
|
use JSON::PP ();
|
||||||
}
|
use Regexp::Common qw/net/;
|
||||||
|
use Martnet::DDNS;
|
||||||
|
|
||||||
my $master;
|
my $master;
|
||||||
|
my $edit = 0;
|
||||||
|
my @ddns_keys;
|
||||||
my $enable_dnssec = 0;
|
my $enable_dnssec = 0;
|
||||||
my $disable_dnssec = 0;
|
my $disable_dnssec = 0;
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'master|m=s' => \$master,
|
'master|m=s' => \$master,
|
||||||
'enable-dnssec' => \$enable_dnssec,
|
'edit!' => \$edit,
|
||||||
'disable-dnssec' => \$disable_dnssec,
|
'ddns-keyname=s' => \@ddns_keys,
|
||||||
) or usage();
|
'enable-dnssec!' => \$enable_dnssec,
|
||||||
|
'disable-dnssec!' => \$disable_dnssec,
|
||||||
|
) or die "Usage: add-custom --master <ip> [--edit] [--ddns-keyname <key>]... [--enable-dnssec|--disable-dnssec] <zone.>\n";
|
||||||
|
|
||||||
usage() unless defined $master;
|
my $zone = shift || die "No zone provided\n";
|
||||||
|
|
||||||
die "Cannot specify both --enable-dnssec and --disable-dnssec\n"
|
die "Zone must end in a dot\n"
|
||||||
if ($enable_dnssec && $disable_dnssec);
|
unless ($zone =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
||||||
|
|
||||||
my $host = shift || die "No zonename provided\n";
|
die "--master is required\n"
|
||||||
|
unless defined $master && $master ne '';
|
||||||
my $ddns = Martnet::DDNS->new();
|
|
||||||
|
|
||||||
die "Zonename must end in a dot\n"
|
|
||||||
unless ($host =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
|
||||||
|
|
||||||
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
||||||
die "Master must be an IPv4 or IPv6 address\n"
|
die "Master must be an IPv4 or IPv6 address\n"
|
||||||
unless ($master =~ /^$regex$/);
|
unless ($master =~ /^$regex$/);
|
||||||
|
|
||||||
my $payload = { master => $master };
|
die "Specify only one of --enable-dnssec or --disable-dnssec\n"
|
||||||
if ($enable_dnssec) {
|
if ($enable_dnssec && $disable_dnssec);
|
||||||
$payload->{dnssec} = true;
|
|
||||||
} elsif ($disable_dnssec) {
|
# Build canonical JSON payload; operators do not hand-author JSON.
|
||||||
$payload->{dnssec} = false;
|
my %payload = (
|
||||||
|
master => $master,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (@ddns_keys) {
|
||||||
|
$payload{ddns_keys} = [ @ddns_keys ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ddns->add($host, encode_json($payload), '_custom');
|
if ($enable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::true;
|
||||||
|
} elsif ($disable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::false;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $json = JSON::PP->new->canonical(1)->encode(\%payload);
|
||||||
|
|
||||||
|
my $ddns = Martnet::DDNS->new();
|
||||||
|
|
||||||
|
if ($edit) {
|
||||||
|
$ddns->set($zone, $json, '_custom');
|
||||||
|
} else {
|
||||||
|
$ddns->add($zone, $json, '_custom');
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|||||||
@@ -2,47 +2,64 @@
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Martnet::DDNS;
|
|
||||||
use Regexp::Common qw/net/;
|
|
||||||
use Getopt::Long qw/GetOptions/;
|
|
||||||
use JSON::PP qw/encode_json true false/;
|
|
||||||
|
|
||||||
sub usage {
|
use Getopt::Long qw/GetOptions/;
|
||||||
die "Usage: add-slave [--enable-dnssec|--disable-dnssec] --master <IPv4|IPv6> <zone.>\n";
|
use JSON::PP ();
|
||||||
}
|
use Regexp::Common qw/net/;
|
||||||
|
use Martnet::DDNS;
|
||||||
|
|
||||||
my $master;
|
my $master;
|
||||||
|
my $edit = 0;
|
||||||
|
my @ddns_keys;
|
||||||
my $enable_dnssec = 0;
|
my $enable_dnssec = 0;
|
||||||
my $disable_dnssec = 0;
|
my $disable_dnssec = 0;
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'master|m=s' => \$master,
|
'master|m=s' => \$master,
|
||||||
'enable-dnssec' => \$enable_dnssec,
|
'edit!' => \$edit,
|
||||||
'disable-dnssec' => \$disable_dnssec,
|
'ddns-keyname=s' => \@ddns_keys,
|
||||||
) or usage();
|
'enable-dnssec!' => \$enable_dnssec,
|
||||||
|
'disable-dnssec!' => \$disable_dnssec,
|
||||||
|
) or die "Usage: add-slave --master <ip> [--edit] [--ddns-keyname <key>]... [--enable-dnssec|--disable-dnssec] <zone.>\n";
|
||||||
|
|
||||||
usage() unless defined $master;
|
my $zone = shift || die "No zone provided\n";
|
||||||
|
|
||||||
die "Cannot specify both --enable-dnssec and --disable-dnssec\n"
|
die "Zone must end in a dot\n"
|
||||||
if ($enable_dnssec && $disable_dnssec);
|
unless ($zone =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
||||||
|
|
||||||
my $host = shift || die "No zonename provided\n";
|
die "--master is required\n"
|
||||||
|
unless defined $master && $master ne '';
|
||||||
my $ddns = Martnet::DDNS->new();
|
|
||||||
|
|
||||||
die "Zonename must end in a dot\n"
|
|
||||||
unless ($host =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
|
||||||
|
|
||||||
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
||||||
die "Master must be an IPv4 or IPv6 address\n"
|
die "Master must be an IPv4 or IPv6 address\n"
|
||||||
unless ($master =~ /^$regex$/);
|
unless ($master =~ /^$regex$/);
|
||||||
|
|
||||||
my $payload = { master => $master };
|
die "Specify only one of --enable-dnssec or --disable-dnssec\n"
|
||||||
if ($enable_dnssec) {
|
if ($enable_dnssec && $disable_dnssec);
|
||||||
$payload->{dnssec} = true;
|
|
||||||
} elsif ($disable_dnssec) {
|
# Build canonical JSON payload; operators do not hand-author JSON.
|
||||||
$payload->{dnssec} = false;
|
my %payload = (
|
||||||
|
master => $master,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (@ddns_keys) {
|
||||||
|
$payload{ddns_keys} = [ @ddns_keys ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ddns->add($host, encode_json($payload), '_pureslave');
|
if ($enable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::true;
|
||||||
|
} elsif ($disable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::false;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $json = JSON::PP->new->canonical(1)->encode(\%payload);
|
||||||
|
|
||||||
|
my $ddns = Martnet::DDNS->new();
|
||||||
|
|
||||||
|
if ($edit) {
|
||||||
|
$ddns->set($zone, $json, '_pureslave');
|
||||||
|
} else {
|
||||||
|
$ddns->add($zone, $json, '_pureslave');
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|||||||
@@ -2,47 +2,64 @@
|
|||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Martnet::DDNS;
|
|
||||||
use Regexp::Common qw/net/;
|
|
||||||
use Getopt::Long qw/GetOptions/;
|
|
||||||
use JSON::PP qw/encode_json true false/;
|
|
||||||
|
|
||||||
sub usage {
|
use Getopt::Long qw/GetOptions/;
|
||||||
die "Usage: add-vhost [--enable-dnssec|--disable-dnssec] --master <IPv4|IPv6> <zone.>\n";
|
use JSON::PP ();
|
||||||
}
|
use Regexp::Common qw/net/;
|
||||||
|
use Martnet::DDNS;
|
||||||
|
|
||||||
my $master;
|
my $master;
|
||||||
|
my $edit = 0;
|
||||||
|
my @ddns_keys;
|
||||||
my $enable_dnssec = 0;
|
my $enable_dnssec = 0;
|
||||||
my $disable_dnssec = 0;
|
my $disable_dnssec = 0;
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'master|m=s' => \$master,
|
'master|m=s' => \$master,
|
||||||
'enable-dnssec' => \$enable_dnssec,
|
'edit!' => \$edit,
|
||||||
'disable-dnssec' => \$disable_dnssec,
|
'ddns-keyname=s' => \@ddns_keys,
|
||||||
) or usage();
|
'enable-dnssec!' => \$enable_dnssec,
|
||||||
|
'disable-dnssec!' => \$disable_dnssec,
|
||||||
|
) or die "Usage: add-vhost --master <ip> [--edit] [--ddns-keyname <key>]... [--enable-dnssec|--disable-dnssec] <zone.>\n";
|
||||||
|
|
||||||
usage() unless defined $master;
|
my $zone = shift || die "No zone provided\n";
|
||||||
|
|
||||||
die "Cannot specify both --enable-dnssec and --disable-dnssec\n"
|
die "Zone must end in a dot\n"
|
||||||
if ($enable_dnssec && $disable_dnssec);
|
unless ($zone =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
||||||
|
|
||||||
my $host = shift || die "No zonename provided\n";
|
die "--master is required\n"
|
||||||
|
unless defined $master && $master ne '';
|
||||||
my $ddns = Martnet::DDNS->new();
|
|
||||||
|
|
||||||
die "Zonename must end in a dot\n"
|
|
||||||
unless ($host =~ /^[a-zA-Z0-9\.\-\_]+\.$/);
|
|
||||||
|
|
||||||
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
my $regex = $RE{net}{IPv4} . '|' . $RE{net}{IPv6};
|
||||||
die "Master must be an IPv4 or IPv6 address\n"
|
die "Master must be an IPv4 or IPv6 address\n"
|
||||||
unless ($master =~ /^$regex$/);
|
unless ($master =~ /^$regex$/);
|
||||||
|
|
||||||
my $payload = { master => $master };
|
die "Specify only one of --enable-dnssec or --disable-dnssec\n"
|
||||||
if ($enable_dnssec) {
|
if ($enable_dnssec && $disable_dnssec);
|
||||||
$payload->{dnssec} = true;
|
|
||||||
} elsif ($disable_dnssec) {
|
# Build canonical JSON payload; operators do not hand-author JSON.
|
||||||
$payload->{dnssec} = false;
|
my %payload = (
|
||||||
|
master => $master,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (@ddns_keys) {
|
||||||
|
$payload{ddns_keys} = [ @ddns_keys ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ddns->add($host, encode_json($payload), '_vhosts');
|
if ($enable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::true;
|
||||||
|
} elsif ($disable_dnssec) {
|
||||||
|
$payload{dnssec} = JSON::PP::false;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $json = JSON::PP->new->canonical(1)->encode(\%payload);
|
||||||
|
|
||||||
|
my $ddns = Martnet::DDNS->new();
|
||||||
|
|
||||||
|
if ($edit) {
|
||||||
|
$ddns->set($zone, $json, '_vhosts');
|
||||||
|
} else {
|
||||||
|
$ddns->add($zone, $json, '_vhosts');
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use Martnet::DDNS;
|
|||||||
|
|
||||||
# Hidden-master unified include generator.
|
# Hidden-master unified include generator.
|
||||||
#
|
#
|
||||||
# - Generates /etc/bind/unified.zones.9
|
# - Generates /var/lib/bind/unified.zones.9
|
||||||
# - Creates missing zonefiles for any emitted zones (minimal skeleton)
|
# - Creates missing zonefiles for any emitted zones (minimal skeleton)
|
||||||
# - Deterministic output; only rewrites include file if content changes
|
# - Deterministic output; only rewrites include file if content changes
|
||||||
#
|
#
|
||||||
@@ -19,13 +19,15 @@ use Martnet::DDNS;
|
|||||||
# - Does NOT attempt to infer DNSSEC (you are removing _dnssec entirely).
|
# - Does NOT attempt to infer DNSSEC (you are removing _dnssec entirely).
|
||||||
# - Filters out private.invalid (core static config) and _pureslave (slave-only).
|
# - Filters out private.invalid (core static config) and _pureslave (slave-only).
|
||||||
|
|
||||||
my $out = '/etc/bind/unified.zones.9';
|
my $out = '/var/lib/bind/unified.zones.9';
|
||||||
my $self_master = undef; # if unset, uses $ddns->default_master()
|
my $self_master = undef; # if unset, uses $ddns->default_master()
|
||||||
my $force_zf = 0; # overwrite existing zonefiles too
|
my $force_zf = 0; # overwrite existing zonefiles too
|
||||||
my $force_out = 0; # rewrite unified include even if identical
|
my $force_out = 0; # rewrite unified include even if identical
|
||||||
my $reload_cmd = '/usr/sbin/rndc';
|
my $reload_cmd = '/usr/sbin/rndc';
|
||||||
my $reconfig = 1; # use rndc reconfig (safer for include file updates)
|
my $reconfig = 1; # use rndc reconfig (safer for include file updates)
|
||||||
|
|
||||||
|
my $default_ddns_key = 'master-ddns-tsig';
|
||||||
|
|
||||||
GetOptions(
|
GetOptions(
|
||||||
'out=s' => \$out,
|
'out=s' => \$out,
|
||||||
'self=s' => \$self_master,
|
'self=s' => \$self_master,
|
||||||
@@ -160,10 +162,37 @@ for my $r (@zones) {
|
|||||||
my $type = $r->{type} // '';
|
my $type = $r->{type} // '';
|
||||||
my $zf = zonefile_for($zone, $type);
|
my $zf = zonefile_for($zone, $type);
|
||||||
|
|
||||||
# Stable, single-line formatting
|
|
||||||
|
# Always enable DDNS updates via the default key, plus any additional keys recorded in JSON.
|
||||||
|
my @keys = ($default_ddns_key);
|
||||||
|
if (defined($r->{payload}) && ref($r->{payload}) eq 'HASH') {
|
||||||
|
my $ak = $r->{payload}->{ddns_keys};
|
||||||
|
if (defined $ak) {
|
||||||
|
if (ref($ak) eq 'ARRAY') {
|
||||||
|
push @keys, grep { defined($_) && $_ ne '' } @$ak;
|
||||||
|
} elsif (!ref($ak) && $ak ne '') {
|
||||||
|
push @keys, $ak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Deduplicate while preserving order.
|
||||||
|
my %seen;
|
||||||
|
@keys = grep { !$seen{$_}++ } @keys;
|
||||||
|
|
||||||
|
my $allow_transfer = 'allow-transfer { trusted; ' . join(' ', map { 'key "' . $_ . '";' } @keys) . ' };';
|
||||||
|
my $update_policy = 'update-policy { ' . join(' ', map { 'grant ' . $_ . ' zonesub ANY;' } @keys) . ' };';
|
||||||
|
my $journal = 'journal "' . $zf . '.jnl";';
|
||||||
|
my $serial_update = 'serial-update-method unixtime;';
|
||||||
|
|
||||||
|
my $dnssec = 0;
|
||||||
|
if (defined($r->{payload}) && ref($r->{payload}) eq 'HASH' && $r->{payload}->{dnssec}) {
|
||||||
|
$dnssec = 1;
|
||||||
|
}
|
||||||
|
my $dnssec_opts = $dnssec ? ' auto-dnssec maintain; inline-signing yes;' : '';
|
||||||
|
|
||||||
$content .= sprintf(
|
$content .= sprintf(
|
||||||
"zone \"%s\" { type master; file \"%s\"; };\n",
|
"zone \"%s\" { type master; file \"%s\";%s %s %s %s %s };\n",
|
||||||
$zone, $zf
|
$zone, $zf, $dnssec_opts, $allow_transfer, $update_policy, $journal, $serial_update
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user