From 42e30a2bc04cd60e115a561e9a0adbd37b496dac Mon Sep 17 00:00:00 2001 From: Al Beano Date: Thu, 14 Sep 2017 17:15:02 +0100 Subject: [PATCH] (Optionally) check zones with nsd before they are written, to ensure safety --- bin/writezone.pl | 51 +++++++++++++++++++++++++++++-------------- config.yml | 3 +++ cpanfile | 1 + dbupdate/7.sql | 2 ++ lib/cyberman.pm | 11 ++++++++++ public/css/light.css | 4 ++++ schema.sql | 5 +++-- views/layouts/main.tt | 12 +++++++++- 8 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 dbupdate/7.sql diff --git a/bin/writezone.pl b/bin/writezone.pl index 7f3a2f8..7cffd36 100755 --- a/bin/writezone.pl +++ b/bin/writezone.pl @@ -10,6 +10,7 @@ use feature 'say'; use FindBin qw($Bin); use YAML::Tiny; use DBI; +use Capture::Tiny ':all'; my $yml = YAML::Tiny->read("$Bin/../config.yml"); my $tld = $yml->[0]->{"tld"}; @@ -25,30 +26,27 @@ $sth->execute; my $cyberman = $sth->fetchrow_hashref; exit unless $cyberman->{"intserial"} > $cyberman->{"lastserial"}; -open my $out, ">", $conf->{"file"} or die $!; +my $zone; # Introduction -say $out <<'END'; +$zone .= <<"END"; ; File produced by cyberman. Do not edit! -$TTL 86400 -$ORIGIN cyb. +\$TTL 86400 +\$ORIGIN $tld. + END # Write SOA # Uses mostly hard-coded values for now -say $out "@ 1D IN SOA $conf->{ns} $conf->{responsible} ("; -say $out time; -say $out <<'END'; +$zone .= "@ 1D IN SOA $conf->{ns} $conf->{responsible} (\n" . time . "\n"; +$zone .= <<'END'; 1800 ; refresh 15 ; retry 604800 ; expire 1h ; nxdomain ttl ) -END -if ($conf->{"include"}->{"enabled"}) { - say $out "\$INCLUDE $conf->{include}->{file}"; -} +END # Time to get the records $sth = $dbh->prepare("SELECT * FROM record"); @@ -63,20 +61,41 @@ while (my $r = $sth->fetchrow_hashref) { # domain name if ($r->{"name"} eq '@') { - print $out $d->{"name"}, " "; + $zone .= $d->{"name"} . " "; } else { - print $out $r->{"name"}, ".", $d->{"name"}, " "; + $zone .= $r->{"name"} . "." . $d->{"name"} . " "; } # record type - print $out "IN $r->{type} "; + $zone .= "IN $r->{type} "; # value - say $out $r->{value}; + $zone .= "$r->{value}\n"; } +if ($conf->{"validate"}) { + my $checkzone_exit; + capture { + open my $checkzone, "| nsd-checkzone $tld -" or die $!; + print $checkzone $zone; + close $checkzone; + $checkzone_exit = $?; + }; + if ($checkzone_exit != 0) { + $sth = $dbh->prepare("UPDATE cyberman SET zonecheckstatus=1"); + $sth->execute; + exit; + } +} + +if ($conf->{"include"}->{"enabled"}) { + $zone .= "\$INCLUDE $conf->{include}->{file}\n"; +} + +open my $out, ">", $conf->{"file"} or die $!; +say $out $zone; close $out; -$sth = $dbh->prepare("UPDATE cyberman SET lastserial=?"); +$sth = $dbh->prepare("UPDATE cyberman SET lastserial=?, zonecheckstatus=0"); $sth->bind_param(1, $cyberman->{"intserial"}); $sth->execute; diff --git a/config.yml b/config.yml index 4bc5db8..0421604 100644 --- a/config.yml +++ b/config.yml @@ -42,6 +42,9 @@ zonewriter: # in the zone, which allows you to define some records manually enabled: 1 file: 'human_cyb' + # if true, written zones will be checked with nsd-checkzone, just in case + # requires nsd to be installed and nsd-checkzone to be in path + validate: 1 mail: enabled: 1 diff --git a/cpanfile b/cpanfile index df4a01a..6eebf79 100644 --- a/cpanfile +++ b/cpanfile @@ -14,6 +14,7 @@ requires "Email::Simple" => "0"; requires "Email::Simple::Creator" => "0"; requires "Email::Sender::Simple" => "0"; requires "URI::Escape" => "0"; +requires "Capture::Tiny" => "0"; requires "Plack::Middleware::Deflater" => "0"; requires "Plack::Middleware::Session" => "0"; diff --git a/dbupdate/7.sql b/dbupdate/7.sql new file mode 100644 index 0000000..ad1ad69 --- /dev/null +++ b/dbupdate/7.sql @@ -0,0 +1,2 @@ +alter table cyberman add column zonecheckstatus not null default 0; +update cyberman set dbrev=8; diff --git a/lib/cyberman.pm b/lib/cyberman.pm index 7f0a28b..71e2e65 100644 --- a/lib/cyberman.pm +++ b/lib/cyberman.pm @@ -57,6 +57,17 @@ hook 'before' => sub { var admin => $user->{"admin"}; var config => config(); + if ($user->{"admin"}) { + my $zone_check = database->quick_lookup( + "cyberman", + {}, + "zonecheckstatus", + ); + if ($zone_check != 0) { + var zone_check_alert => 1; + } + } + if ($user->{"stylesheet"} && grep {$_ eq $user->{"stylesheet"}} @{ config->{"stylesheets"}->{"available"} }) { var stylesheet => $user->{"stylesheet"}; } else { diff --git a/public/css/light.css b/public/css/light.css index c9cd645..b7c93c4 100644 --- a/public/css/light.css +++ b/public/css/light.css @@ -129,3 +129,7 @@ table.t tr:first-child { border-radius: 2px; max-width: 650px; } +.msgBox.warning { + background-color: #ff583a; + border: 1px solid #c43d25; +} diff --git a/schema.sql b/schema.sql index acc5961..10bb62c 100644 --- a/schema.sql +++ b/schema.sql @@ -3,9 +3,10 @@ create table cyberman ( id integer primary key, dbrev integer not null, intserial integer not null default 1, - lastserial integer not null default 0 + lastserial integer not null default 0, + zonecheckstatus integer not null default 0, ); -insert into cyberman (dbrev) values (7); +insert into cyberman (dbrev) values (8); drop table if exists user; create table user ( diff --git a/views/layouts/main.tt b/views/layouts/main.tt index 80a3516..57887c2 100644 --- a/views/layouts/main.tt +++ b/views/layouts/main.tt @@ -29,7 +29,17 @@ - <% END %> + <% END %> + <% IF vars.zone_check_alert %> +
+
+
+

Warning! Achtung!

+ The zone produced by the zone writer was found to be invalid by nsd; something has gone wrong! Please rectify the problem, and this message will disappear. +
+
+
+ <% END %> <% content %>