(Optionally) check zones with nsd before they are written, to ensure safety

This commit is contained in:
Al Beano 2017-09-14 17:15:02 +01:00
parent 32c5b712d7
commit 42e30a2bc0
8 changed files with 70 additions and 19 deletions

View File

@ -10,6 +10,7 @@ use feature 'say';
use FindBin qw($Bin); use FindBin qw($Bin);
use YAML::Tiny; use YAML::Tiny;
use DBI; use DBI;
use Capture::Tiny ':all';
my $yml = YAML::Tiny->read("$Bin/../config.yml"); my $yml = YAML::Tiny->read("$Bin/../config.yml");
my $tld = $yml->[0]->{"tld"}; my $tld = $yml->[0]->{"tld"};
@ -25,30 +26,27 @@ $sth->execute;
my $cyberman = $sth->fetchrow_hashref; my $cyberman = $sth->fetchrow_hashref;
exit unless $cyberman->{"intserial"} > $cyberman->{"lastserial"}; exit unless $cyberman->{"intserial"} > $cyberman->{"lastserial"};
open my $out, ">", $conf->{"file"} or die $!; my $zone;
# Introduction # Introduction
say $out <<'END'; $zone .= <<"END";
; File produced by cyberman. Do not edit! ; File produced by cyberman. Do not edit!
$TTL 86400 \$TTL 86400
$ORIGIN cyb. \$ORIGIN $tld.
END END
# Write SOA # Write SOA
# Uses mostly hard-coded values for now # Uses mostly hard-coded values for now
say $out "@ 1D IN SOA $conf->{ns} $conf->{responsible} ("; $zone .= "@ 1D IN SOA $conf->{ns} $conf->{responsible} (\n" . time . "\n";
say $out time; $zone .= <<'END';
say $out <<'END';
1800 ; refresh 1800 ; refresh
15 ; retry 15 ; retry
604800 ; expire 604800 ; expire
1h ; nxdomain ttl 1h ; nxdomain ttl
) )
END
if ($conf->{"include"}->{"enabled"}) { END
say $out "\$INCLUDE $conf->{include}->{file}";
}
# Time to get the records # Time to get the records
$sth = $dbh->prepare("SELECT * FROM record"); $sth = $dbh->prepare("SELECT * FROM record");
@ -63,20 +61,41 @@ while (my $r = $sth->fetchrow_hashref) {
# domain name # domain name
if ($r->{"name"} eq '@') { if ($r->{"name"} eq '@') {
print $out $d->{"name"}, " "; $zone .= $d->{"name"} . " ";
} else { } else {
print $out $r->{"name"}, ".", $d->{"name"}, " "; $zone .= $r->{"name"} . "." . $d->{"name"} . " ";
} }
# record type # record type
print $out "IN $r->{type} "; $zone .= "IN $r->{type} ";
# value # 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; 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->bind_param(1, $cyberman->{"intserial"});
$sth->execute; $sth->execute;

View File

@ -42,6 +42,9 @@ zonewriter:
# in the zone, which allows you to define some records manually # in the zone, which allows you to define some records manually
enabled: 1 enabled: 1
file: 'human_cyb' 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: mail:
enabled: 1 enabled: 1

View File

@ -14,6 +14,7 @@ requires "Email::Simple" => "0";
requires "Email::Simple::Creator" => "0"; requires "Email::Simple::Creator" => "0";
requires "Email::Sender::Simple" => "0"; requires "Email::Sender::Simple" => "0";
requires "URI::Escape" => "0"; requires "URI::Escape" => "0";
requires "Capture::Tiny" => "0";
requires "Plack::Middleware::Deflater" => "0"; requires "Plack::Middleware::Deflater" => "0";
requires "Plack::Middleware::Session" => "0"; requires "Plack::Middleware::Session" => "0";

2
dbupdate/7.sql Normal file
View File

@ -0,0 +1,2 @@
alter table cyberman add column zonecheckstatus not null default 0;
update cyberman set dbrev=8;

View File

@ -57,6 +57,17 @@ hook 'before' => sub {
var admin => $user->{"admin"}; var admin => $user->{"admin"};
var config => config(); 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"} }) { if ($user->{"stylesheet"} && grep {$_ eq $user->{"stylesheet"}} @{ config->{"stylesheets"}->{"available"} }) {
var stylesheet => $user->{"stylesheet"}; var stylesheet => $user->{"stylesheet"};
} else { } else {

View File

@ -129,3 +129,7 @@ table.t tr:first-child {
border-radius: 2px; border-radius: 2px;
max-width: 650px; max-width: 650px;
} }
.msgBox.warning {
background-color: #ff583a;
border: 1px solid #c43d25;
}

View File

@ -3,9 +3,10 @@ create table cyberman (
id integer primary key, id integer primary key,
dbrev integer not null, dbrev integer not null,
intserial integer not null default 1, 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; drop table if exists user;
create table user ( create table user (

View File

@ -29,7 +29,17 @@
</span> </span>
</div> </div>
</div> </div>
<% END %> <% END %>
<% IF vars.zone_check_alert %>
<div class="body">
<center>
<div class="msgBox warning">
<h3>Warning! Achtung!</h3>
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.
</div>
</center>
</div>
<% END %>
<% content %> <% content %>
</div> </div>
</body> </body>