--- a/.hgignore Tue Dec 21 17:00:11 2010 +0100
+++ b/.hgignore Sun Jan 23 00:30:15 2011 +0100
@@ -1,6 +1,6 @@
-.hgignore
-dnssec-keytool
-update-serial
-zone-ls
-zone-mk
-zone-rm
+bind
+blib
+Build
+_build
+cover_db
+pod2htm.\.tmp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,1 @@
+9c304b77cf02c77538ad7fc983f30f04fd50ebdd 0.1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Build.PL Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,22 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Module::Build;
+
+my $build = Module::Build->new(
+ module_name => "dnstools",
+ dist_author => "schlittermann.de",
+ dist_version => "0.1",
+ create_license => 1,
+ license => "gpl",
+ requires => {
+ perl => "5.10.0",
+ "Net::LibIDN" => "0",
+ "Template" => "0"
+ },
+ build_requires => { "Pod::Coverage" => 0, "Test::Command" => "0.08" },
+ script_files => [glob "bin/*"], # avoid .swp files
+);
+
+$build->create_build_script;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MANIFEST.SKIP Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,50 @@
+
+#!start included /usr/share/perl/5.10/ExtUtils/MANIFEST.SKIP
+# Avoid version control files.
+^\.hg/
+^\.hg[^_]
+.*\.sw?$
+\bRCS\b
+\bCVS\b
+\bSCCS\b
+,v$
+\B\.svn\b
+\B\.git\b
+\B\.gitignore\b
+\b_darcs\b
+
+# Avoid Makemaker generated and utility files.
+\bMANIFEST\.bak
+\bMakefile$
+\bblib/
+\bMakeMaker-\d
+\bpm_to_blib\.ts$
+\bpm_to_blib$
+\bblibdirs\.ts$ # 6.18 through 6.25 generated this
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\b_build/
+
+# Avoid temp and backup files.
+~$
+\.old$
+\#$
+\b\.#
+\.bak$
+
+# Avoid Devel::Cover files.
+\bcover_db\b
+#!end included /usr/share/perl/5.10/ExtUtils/MANIFEST.SKIP
+
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\bBuild.bat$
+\b_build
+\bBuild.COM$
+\bBUILD.COM$
+\bbuild.com$
+
+# Avoid archives of this distribution
+\bdnstools-[\d\.\_]+
--- a/Makefile Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-ALL = zone-ls zone-mk zone-rm \
- update-serial \
- dnssec-keytool
-
-CLEANFILES = $(ALL)
-
-.PHONY: all clean distclean install
-
-all: $(ALL)
-
-clean:
-distclean:
- rm -f $(CLEANFILES)
-
-
-%: %.pl
- @perl -c $<
- @cp -f $< $@
- @chmod a+x-w $@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/.perltidyrc Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,1 @@
+../.perltidyrc
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/dnssec-keytool Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,349 @@
+#! /usr/bin/perl
+
+use v5.10;
+use warnings;
+use strict;
+use File::Temp;
+use Getopt::Long;
+use Pod::Usage;
+use File::Basename;
+use if $ENV{DEBUG} => "Smart::Comments";
+use DNStools::Config qw(get_config);
+
+my $ME = basename $0;
+
+sub read_conf(@);
+sub read_argv($);
+sub rm_keys($@);
+sub ck_zone($@);
+sub create_ksk($@);
+sub create_zsk($@);
+sub post_create($@);
+
+MAIN: {
+ ### reading config
+ my %conf = get_config();
+
+ my ($cmd, @zones) = read_argv($conf{master_dir});
+
+ given ($cmd) {
+ when ("rm") { rm_keys($conf{master_dir}, @zones); exit }
+ when ("ck") { ck_zone($conf{master_dir}, @zones) }
+ when ("ksk") { create_ksk($conf{master_dir}, @zones) }
+ };
+
+ create_zsk($conf{master_dir}, @zones);
+ post_create($conf{master_dir}, @zones);
+}
+
+sub read_argv ($) {
+ my ($master_dir) = @_;
+ my ($cmd, @zones); # return
+
+ GetOptions(
+ "zsk" => sub { $cmd = "zsk" },
+ "ksk" => sub { $cmd = "ksk" },
+ "rm" => sub { $cmd = "rm" },
+ "ck|check" => sub { $cmd = "ck" },
+ "h|help" => sub { pod2usage(-exitvalue => 0, -verbose => 1) },
+ "m|man" => sub {
+ pod2usage(
+ -exitvalue => 0,
+ -noperldoc => system("perldoc -V &>/dev/null"),
+ -verbose => 2
+ );
+ },
+ )
+ and @ARGV
+ or pod2usage;
+
+ # checks the zones in argv if there are managed zones
+ foreach (@ARGV) {
+ chomp(my $zone = `idn --quiet "$_"`);
+
+ die "zone $zone is not managed\n"
+ if not -f "$master_dir/$zone/$zone";
+
+ push @zones, $zone;
+ }
+ return ($cmd, @zones);
+}
+
+
+sub rm_keys ($@) {
+
+ # deletes all the keys were handed over -rm in argv
+ my ($master_dir, @zone) = @_;
+
+ for (@zone) {
+ my $zone = $_;
+
+ my $zpf = "$master_dir/$zone";
+ my $ep = 0;
+
+ if (-e "$zpf/$zone.signed") {
+ unlink "$zpf/$zone.signed" and $ep = 1;
+ }
+ if (-e "$zpf/.keycounter") {
+ unlink "$zpf/.keycounter" and $ep = 1;
+ }
+ if (-e "$zpf/.index.ksk") {
+ unlink "$zpf/.index.ksk" and $ep = 1;
+ }
+ if (-e "$zpf/.index.zsk") {
+ unlink "$zpf/.index.zsk" and $ep = 1;
+ }
+ if (-e "$zpf/dsset-$zone.") {
+ unlink "$zpf/dsset-$zone." and $ep = 1;
+ }
+ if (-e "$zpf/keyset-$zone.") {
+ unlink "$zpf/keyset-$zone." and $ep = 1;
+ }
+
+ for (glob("$zpf/K$zone*")) {
+ chomp($_);
+ unlink("$_");
+ }
+
+ if ($ep == 1) {
+ print " * $zone: removed key-set\n";
+ }
+
+ open(my $old, "$zpf/$zone") or die "$zpf/$zone: $!\n";
+ my $fh = File::Temp->new(DIR => $zpf) or die "Can't create tmpfile: $!\n";
+ print $fh grep { not /^\s*\$INCLUDE.*"K$zone.*\.key"/i } <$old>;
+ rename($fh->filename => "$zpf/$zone")
+ or die "Can't rename " . $fh->filename . " to $zpf/$zone: $!\n";
+ }
+}
+
+sub create_ksk ($@) {
+ my ($master_dir, @zone) = @_;
+ my @index;
+ my $keyname;
+
+ for (@zone) {
+ my $zone = $_;
+ my $zpf = "$master_dir/$zone";
+
+ $keyname =
+ `cd $zpf && dnssec-keygen -a RSASHA1 -b 2048 -f KSK -n ZONE $zone`;
+
+ unless (-f "$zpf/.index.ksk") { @index = (); }
+ else {
+ open(INDEX, "$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+ @index = <INDEX>;
+ close(INDEX);
+ }
+
+ push @index, $keyname;
+ if (@index > 2) { shift(@index); }
+
+ {
+ my $fh = File::Temp->new(DIR => "$zpf")
+ or die "Can't create tmpdir: $!\n";
+ print $fh join "" => @index, "";
+ rename($fh->filename => "$zpf/.index.ksk")
+ or die "Can't rename "
+ . $fh->filename
+ . " to $zpf/.index.ksk: $!\n";
+ }
+
+ chomp($keyname);
+ print " * $zone: new KSK $keyname\n";
+ print "!! THE KSK must be published !! \n";
+
+ }
+}
+
+sub create_zsk ($@) {
+ my ($master_dir, @zone) = @_;
+ my @index;
+ my $keyname;
+
+ for (@zone) {
+ my $zone = $_;
+ my $zpf = "$master_dir/$zone";
+
+ $keyname = `cd $zpf && dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`;
+
+ unless (-f "$zpf/.index.zsk") {
+ @index = ();
+ }
+ else {
+ open(INDEX, "$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+ @index = <INDEX>;
+ close(INDEX);
+ }
+
+ push @index, $keyname;
+ if (@index > 2) { shift(@index); }
+
+ {
+ my $fh = File::Temp->new(DIR => "$zpf")
+ or die "Can't create tmpdir: $!\n";
+ print $fh join "" => @index, "";
+ rename($fh->filename => "$zpf/.index.zsk")
+ or die "Can't rename "
+ . $fh->filename
+ . " to $zpf/.index.zsk: $!\n";
+ }
+ chomp($keyname);
+ print " * $zone: new ZSK $keyname\n";
+
+ open(KC, ">$zpf/.keycounter") or die "$zpf/keycounter: $!\n";
+ print KC "0";
+ close(KC);
+ }
+}
+
+sub ck_zone ($@) {
+ my ($master_dir, @zone) = @_;
+
+ for (@zone) {
+ my $zone = $_;
+ my $zpf = "$master_dir/$zone";
+ my $keyfile;
+ my @content;
+ my @keylist;
+
+ for (<$zpf/*>) {
+ if (m#(K$zone.*\.key)#) {
+ $keyfile = $1;
+ open(KEYFILE, "<", "$zpf/$keyfile")
+ or die "$zpf/$keyfile: $!\n";
+ @content = <KEYFILE>;
+ close(KEYFILE);
+ for (@content) {
+ if (m#DNSKEY.257#) {
+ push @keylist, $keyfile;
+ }
+ }
+ }
+ }
+
+ open(INDEX, ">$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+ for (@keylist) {
+ s#\.key##;
+ print INDEX "$_\n";
+ }
+ close(INDEX);
+
+ print " * $zone: new .index.ksk created\n";
+ if (-f "$zpf/.index.zsk") {
+ unlink("$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+ }
+ }
+}
+
+sub post_create ($@) {
+ my ($master_dir, @zone) = @_;
+ for (@zone) {
+ my $zone = $_;
+ `touch $master_dir/$zone/$zone`;
+ &kill_useless_keys($zone, $master_dir);
+ &key_to_zonefile($zone, $master_dir);
+ }
+}
+
+sub kill_useless_keys ($@) {
+
+ # the function deletes all keys that are not available in the zone
+
+ my $zone = $_[0];
+ my $master_dir = $_[1];
+ my @keylist = ();
+ my $zpf = "$master_dir/$zone";
+
+ open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+ @keylist = <INDEX>;
+ close(INDEX);
+ open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+ push @keylist, <INDEX>;
+
+ # shortened the key name from the index file on the id in order to
+ # be able to compare
+ for (@keylist) {
+ chomp;
+ s#K.*\+.*\+(.*)#$1#;
+ }
+
+ # reviewed every key file (KSK, ZSK), whether they are described in
+ # the respective index file. if not they will be deleted.
+ for (glob("$master_dir/$zone/K*")) {
+ chomp;
+ my $file = $_;
+ my $rm_count = 1;
+ my $keyname;
+ for (@keylist) {
+ if ($file =~ /$_/) { $rm_count = 0; }
+ }
+ if ($rm_count == 1) {
+ unlink "$file";
+ if ($file =~ /$zpf\/(.*\.key)/) {
+ print " * $zone: Key $1 removed \n";
+ }
+ }
+ }
+}
+
+sub key_to_zonefile ($@) {
+
+ # the function added all keys to the indexfile
+ my $zone = $_[0];
+ my $master_dir = $_[1];
+ my $zpf = "$master_dir/$zone";
+ my @old_content;
+ my @new_content = ();
+
+ open(ZONEFILE, "<$zpf/$zone");
+ @old_content = <ZONEFILE>;
+ close(ZONEFILE);
+
+ for (@old_content) {
+ unless (m#INCLUDE.*key#) { push @new_content, $_; }
+ }
+
+ for (<$zpf/*>) {
+ if (m#(.*\/)(K.*\.key)#) {
+ push @new_content, "\$INCLUDE \"$2\"\n";
+ }
+ }
+ open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
+ print ZONEFILE @new_content;
+ close(ZONEFILE);
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dnssec-keytool
+
+=head1 SYNOPSIS
+
+dnssec-keytool {-z|-k|-r|-c} zone
+
+=head1 DESCRIPTION
+
+Blabla.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-z> created a new ZSK
+
+=item B<-k> created a new ZSK and KSK
+
+=item B<-r> delete the key-set of a zone
+
+=item B<-c> created configuration files for the dnstools and a new ZSK for an existing KSK
+
+=back
+
+=cut
+
+# vim:sts=4 sw=4 aw ai sm:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/update-serial Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,548 @@
+#!/usr/bin/perl
+
+use v5.10;
+use strict;
+use warnings;
+
+use File::Basename;
+use Pod::Usage;
+use Getopt::Long;
+use File::Temp;
+use IO::File;
+use POSIX qw(strftime);
+use if $ENV{DEBUG} => "Smart::Comments";
+use DNStools::Config qw(get_config);
+
+sub uniq(@);
+sub zones(@);
+sub changed_zones();
+sub update_index($);
+sub signature_expired($);
+sub need_rollover();
+sub done_rollover();
+sub begin_rollover(@);
+sub end_rollover(@);
+sub unlink_unused_keys($);
+sub include_keys($);
+sub sign($);
+sub update_serial($);
+
+sub mk_zone_conf;
+sub file_entry;
+sub server_reload;
+
+my %config;
+my %opt;
+
+MAIN: {
+
+ GetOptions(
+ "sign-alert-time=i" => \$opt{sign_alert_time},
+ "key-counter-end=i" => \$opt{key_counter_end},
+ "h|help" => sub { pod2usage(-exit 0, -verbose => 1) },
+ "m|man" => sub {
+ pod2usage(
+ -exit 0,
+ -verbose => 2,
+ -noperldoc => system("perldoc -v &>/dev/null")
+ );
+ },
+ ) or pod2usage;
+
+ # merge the config and the defined options from commandline
+ %config = get_config("$ENV{DNSTOOLS_CONF}", "dnstools.conf",
+ "$ENV{HOME}/.dnstools.conf", "/etc/dnstools.conf", \%opt);
+
+ our $bind_dir = $config{bind_dir};
+ our $conf_dir = $config{zone_conf_dir};
+
+ my @candidates = @ARGV ? zones(@ARGV) : changed_zones;
+ push @candidates, update_index($config{indexzone});
+ push @candidates, signature_expired($config{sign_alert_time});
+
+ my @need_rollover = need_rollover;
+ my @done_rollover = done_rollover;
+
+ push @candidates, begin_rollover(@need_rollover);
+ push @candidates, end_rollover(@done_rollover);
+
+ foreach my $zone (uniq(@candidates)) {
+ update_serial($zone);
+ sign($zone);
+ }
+ say "Need to ... file_entry, mk_zone_conf, server_reload";
+ exit;
+
+ file_entry; # bearbeitet die file-eintraege der konfigurations-datei
+ mk_zone_conf; # konfiguration zusammenfuegen
+ server_reload; # server neu laden
+
+}
+
+sub uniq(@) {
+
+ # remove duplicate entries
+ my %all;
+ @all{@_} = ();
+ keys %all;
+}
+
+sub zones(@) {
+
+ # check whether the zones in argv are managed zones and
+ # insert them into the list new_serial
+
+ my @r;
+
+ foreach (@_) {
+ chomp(my $zone = `idn --quiet "$_"`);
+ die "$zone is not managed\n"
+ if not -e "$config{master_dir}/$zone/$zone";
+ push @r, $zone;
+ }
+
+ return @r;
+}
+
+sub changed_zones() {
+
+ # find candidates in our master dir
+ my @r;
+
+ while (glob "$config{master_dir}/*") {
+ my $zone = basename($_);
+
+ if (not -e "$_/.stamp") {
+ say " * $zone: no .stamp file found"; # NOCH IN NEW_SERIAL PUSHEN
+ push @r, $zone;
+ next;
+ }
+
+ my $stamp_age = -M _;
+ my $file_age = -M "$_/$zone";
+
+ next if $stamp_age <= $file_age; # should be only <
+
+ push @r, $zone;
+ say " * $zone: zone file modified";
+ }
+ return @r;
+}
+
+sub signature_expired($) {
+ my $sign_alert_time = shift; # the time between the end and the new signing
+ # (see external configuration)
+ my @r;
+
+# erzeugt $time (die zeit ab der neu signiert werden soll)
+# ... warum eigentlich nur bis zu den Stunden und nicht auch Minuten und Sekunden?
+ my $time = strftime("%Y%m%d%H" => localtime time + 3600 * $sign_alert_time);
+
+ ## vergleicht fuer alle zonen im ordner $config{master_dir} mit einer
+ ## <zone>.signed-datei den zeitpunkt in $time mit dem ablaufdatum der
+ ## signatur, welcher aus der datei <zone>.signed ausgelesen wird.
+ ZONE: while (my $dir = glob "$config{master_dir}/*") {
+ my $zone = basename $dir;
+
+ next if not -e "$dir/$zone.signed";
+
+ open(my $fh, "$dir/$zone.signed")
+ or die "Can't open $dir/$zone.signed: $!\n";
+ push @r, $zone
+ if /RRSIG\s+SOA[\d ]+(\d{10})\d{4}\s+\(/ ~~ [<$fh>]
+ and $1 < $time;
+ }
+
+ return @r;
+}
+
+sub sign($) {
+
+ my $zone = shift;
+ my $dir = "$config{master_dir}/$zone";
+
+ my $pid = fork // die "Can't fork: $!";
+
+ if ($pid == 0) {
+ chdir $dir or die "Can't chdir to $dir: $!\n";
+ exec "dnssec-signzone" => $zone;
+ die "Can't exec: $!\n";
+ }
+
+ wait == $pid or die "Child is lost: $!";
+ die "Can't sign zone!" if $?;
+
+ say " * $zone neu signiert";
+
+ open(my $fh, "+>>$dir/.keycounter")
+ or die "Can't open $dir/.keycounter for update: $!\n";
+ seek($fh, 0, 0);
+ my $kc = <$fh>;
+ truncate($fh, 0);
+ say $fh ++$kc;
+}
+
+sub update_serial($) {
+
+ my $zone = shift;
+
+ my $file = "$config{master_dir}/$zone/$zone";
+ my $in = IO::File->new($file) or die "Can't open $file: $!\n";
+ my $out = File::Temp->new(DIR => dirname $file)
+ or die "Can't open tmpfile: $!\n";
+ my $_ = join "" => <$in>;
+
+ my $serial;
+ s/^(\s+)(\d{10})(?=\s*;\s*serial)/$1 . ($serial = new_serial($2))/emi
+ or die "Serial number not found for replacement!";
+
+ print $out $_;
+
+ close($in);
+ close($out);
+
+ rename($out->filename => $file)
+ or die "Can't rename tmp to $file: $!\n";
+
+ $serial =~ s/\s*//g;
+ say " * $zone: serial incremented to $serial";
+
+ open(my $stamp, ">", dirname($file) . "/.stamp");
+ print $stamp time() . " " . localtime() . "\n";
+
+ say " * $zone: stamp aktualisiert";
+}
+
+sub new_serial($) {
+
+ my ($date, $cnt) = $_[0] =~ /(\d{8})(\d\d)/;
+
+ state $now = strftime("%4Y%02m%02d", localtime);
+
+ return $date eq $now
+ ? sprintf "%s%02d", $date, $cnt + 1
+ : "${now}00";
+
+}
+
+sub mk_zone_conf {
+
+ # erzeugt eine named.conf-datei aus den entsprechenden vorlagen.
+ our $bind_dir;
+ our $conf_dir;
+
+ open(TO, ">$bind_dir/named.conf.zones")
+ or die "$bind_dir/named.conf.zones: $!\n";
+ while (<$conf_dir/*>) {
+ open(FROM, "$_") or die "$_: $! \n";
+ print TO <FROM>;
+ close(FROM);
+ }
+ close(TO);
+ print "** zonekonfiguration erzeugt\n";
+}
+
+sub update_index($) {
+ my $indexzone = shift;
+
+ my @iz;
+
+ {
+ open(my $fh, "$config{master_dir}/$indexzone/$indexzone")
+ or die "$config{master_dir}/$indexzone/$indexzone: $!\n";
+ chomp(@iz = grep !/ZONE::/ => <$fh>);
+ }
+
+ for my $dir (glob "$config{master_dir}/*") {
+ my $zone = basename($dir);
+ my $info = -e ("$dir/.keycounter") ? "sec-on" : "sec-off";
+ push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info;
+ }
+
+ {
+ my $fh = File::Temp->new(DIR => "$config{master_dir}/$indexzone")
+ or die "Can't create tmpdir: $!\n";
+ print $fh join "\n" => @iz, "";
+ rename($fh->filename => "$config{master_dir}/$indexzone/$indexzone")
+ or die "Can't rename "
+ . $fh->filename
+ . " to $config{master_dir}/$indexzone/$indexzone: $!\n";
+ }
+
+ say "** index-zone aktualisiert";
+ return $indexzone;
+}
+
+sub file_entry {
+
+ # prueft jede domain, die ein verzeichnis in $config{master_dir} hat, ob sie
+ # dnssec nutzt.
+ # passt die eintraege in $config_file falls noetig an.
+ our $conf_dir;
+
+ while (glob "$config{master_dir}/*") {
+ s#($config{master_dir}/)(.*)#$2#;
+ my $zone = $_;
+ my $zone_file = "$config{master_dir}/$zone/$zone";
+ my $conf_file = "$conf_dir/$zone";
+ my @c_content;
+
+ unless (-f "$conf_file") {
+ die "$conf_file: $! \n";
+ }
+
+ if (-e "$config{master_dir}/$zone/.keycounter") {
+ open(FILE, "<$conf_file") or die "$conf_file: $!\n";
+ @c_content = <FILE>;
+ close(FILE);
+ for (@c_content) {
+ if (m{(.*)($zone_file)(";)}) {
+ print
+ " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n";
+ $_ = "$1$2.signed$3\n";
+ }
+ }
+ open(FILE, ">$conf_file") or die "$conf_file: $!\n";
+ print FILE @c_content;
+ close(FILE);
+ }
+ else {
+ open(FILE, "<$conf_file") or die "$conf_file: $!\n";
+ @c_content = <FILE>;
+ close(FILE);
+ for (@c_content) {
+ if (m{(.*)($zone_file)\.signed(.*)}) {
+ print
+ " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n";
+ $_ = "$1$2$3\n";
+ }
+ }
+ open(FILE, ">$conf_file") or die "$conf_file: $!\n";
+ print FILE @c_content;
+ close(FILE);
+ }
+ }
+}
+
+sub server_reload {
+ if (`rndc reload`) { print "** reload dns-server \n" }
+}
+
+sub need_rollover() {
+
+ # gibt alle zonen mit abgelaufenen keycounter
+ my @r;
+
+ while (my $kc = glob "$config{master_dir}/*/.keycounter") {
+ my $zone = basename dirname $kc;
+ my $key;
+
+ {
+ open(my $fh, $kc) or die "$kc: $!\n";
+ chomp($key = <$fh>);
+ }
+
+ push @r, $zone if $config{key_counter_end} <= $key;
+ }
+
+ return @r;
+}
+
+sub done_rollover() {
+
+ # funktion ueberprueft ob ein keyrollover fertig ist
+ # die bedingung dafuer ist das:
+ # - eine datei .index.zsk vorhanden ist
+ # - die datei .index.zsk älter ist, als die rollover-Zeit
+ # - die datei .index.zsk ueber mehr als eine zeile gross ist
+ # (also mehr als einen Schlüssel enthält)
+ my @r;
+ my $now = time;
+
+ while (my $dir = glob "$config{master_dir}/*") {
+ my $zone = basename $dir;
+
+ my @index = ();
+ my $index_wc;
+
+ # prueft nach der ".index.zsk"-datei und erstellt den zeitpunkt
+ # an dem das key-rollover endet.
+ # rollover is done when mtime of the .index.zsk + abl_zeit is
+ # in the past
+ next if not -e "$dir/.index.zsk";
+ next if (stat _)[9] + 3600 * $config{abl_zeit} >= $now;
+
+ # prueft die anzahl der schluessel in der .index.zsk
+ open(my $fh, "$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
+ (<$fh>);
+ push @r, $zone if $. > 1;
+ }
+
+ return @r;
+}
+
+sub begin_rollover(@) {
+ my @zones = @_;
+ my @r;
+
+ # anfang des key-rollovers
+
+ foreach my $zone (@zones) {
+
+ # erzeugt zsks
+ my $dir = "$config{master_dir}/$zone";
+ my ($keyname, @keys);
+
+ # create a new key
+ { # need to change the direcoty, thus some more effort
+ # alternativly: $keyname = `cd $dir && dnssec-keygen ...`;
+ # would do, but is more fragile on shell meta characters
+
+ open(my $keygen, "-|") or do {
+ chdir $dir or die "Can't chdir to $dir: $!\n";
+ exec "dnssec-keygen",
+ -a => "RSASHA1",
+ -b => 512,
+ -n => "ZONE",
+ $zone;
+ die "Can't exec: $!";
+ };
+ chomp($keyname = <$keygen>);
+ close($keygen) or die "dnssec-keygen failed: $@";
+ }
+
+ open(my $fh, "+>>$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
+ seek($fh, 0, 0);
+ chomp(@keys = <$fh>);
+
+ ### @keys
+
+ push @keys, $keyname;
+ shift @keys if @keys > 2;
+
+ truncate($fh, 0) or die "truncate";
+ print $fh join "\n" => @keys;
+
+ print " * $zone: neuer ZSK $keyname erstellt\n";
+
+ open($fh, ">$dir/.keycounter") or die "$dir/.keycounter: $!\n";
+ say $fh 0;
+ close($fh);
+
+ unlink_unused_keys($zone);
+ include_keys($zone);
+ push @r, $zone;
+ }
+
+ return @r;
+}
+
+sub include_keys($) {
+
+ # die funktion fugt alle schluessel in eine zonedatei
+ my $zone = shift;
+ my $dir = "$config{master_dir}/$zone";
+
+ my $in = IO::File->new("$dir/$zone") or die "Can't open $dir/$zone: $!\n";
+ my $out = File::Temp->new(DIR => $dir) or die "Can't open tmpfile: $!\n";
+
+ print $out grep { !/\$include\s+.*key/i } $in;
+ print $out map { "\$INCLUDE @{[basename $_]}\n" } glob "$dir/K*key";
+
+ close $in;
+ close $out;
+ rename($out->filename => "$dir/$zone")
+ or die "Can't rename tmp to $dir/$zone: $!\n";
+
+}
+
+sub unlink_unused_keys($) {
+
+ # die funktion loescht alle schluessel die nicht in der index.zsk
+ # der uebergebenen zone stehen
+ my $zone = shift;
+
+ my @keys;
+ my $dir = "$config{master_dir}/$zone";
+
+ {
+
+ # collect the keys and cut everything except the key id
+ # we cut the basenames (w/o the .private|.key suffix)
+ open(my $zsk, "<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n";
+ open(my $ksk, "<$dir/.index.ksk") or die "$dir/.index.ksk: $!\n";
+ @keys = (<$zsk>, <$ksk>);
+ }
+
+ # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen
+ # indexdatei beschrieben sind. wenn nicht werden sie geloescht.
+ for my $file (glob "$dir/K*.key $dir/K*.private") {
+ unlink $file if basename($file, ".key", ".private") ~~ @keys;
+ }
+}
+
+sub end_rollover(@) {
+
+ my @zones = @_;
+ my @r;
+
+ foreach my $zone (@zones) {
+
+ my $dir = "$config{master_dir}/$zone";
+
+ open(my $fh, "+>>$dir/.index.zsk")
+ or die "Can't open $dir/.index.zsk: $!\n";
+ seek($fh, 0, 0);
+ chomp(my @keys = <$fh>);
+
+ if (@keys > 1) {
+ truncate($fh, 0);
+ say $fh $keys[-1];
+ }
+ close($fh);
+
+ unlink_unused_keys($zone);
+ include_keys($zone);
+ push @r => $zone;
+ }
+
+ return @r;
+}
+
+__END__
+
+=head1 NAME
+
+ update-serial - updates the serial numbers and re-signs the zone files
+
+=head1 SYNOPSIS
+
+ update-serial [options] [zone...]
+
+=head1 DESCRIPTION
+
+B<update-serial> scans the configured directories for modified zone files. On any
+file found it increments the serial number and signs the zone, if approbiate.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--sign-alert-time> I<days>
+
+=item B<--key-counter-end> I<integer>
+
+Maximum number if key usages.
+
+
+=back
+
+The common options B<-h>|B<--help>|B<-m>|B<--man> are supported.
+
+=head1 AUTHOR
+
+L<andre.suess@pipkin.cc>
+
+=cut
+
+# vim:sts=4 sw=4 aw ai sm:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/zone-ls Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,144 @@
+#! /usr/bin/perl
+
+use v5.10;
+use strict;
+use warnings;
+use Pod::Usage;
+use File::Basename;
+use Time::Local;
+use Getopt::Long;
+use if $ENV{DEBUG} => "Smart::Comments";
+use DNStools::Config qw(get_config);
+
+my %config;
+my $opt_expiry = undef;
+
+MAIN: {
+ my %info; # will hold the information we collected
+
+ GetOptions(
+ "e|expiry" => \$opt_expiry,
+ "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) },
+ "m|man" => sub {
+ pod2usage(
+ -exit => 0,
+ -verbose => 2,
+ -noperldoc => system("perldoc -V &>/dev/null")
+ );
+ },
+ ) or pod2usage;
+
+ %config = get_config();
+ die "$config{master_dir}: $!\n" if not -d $config{master_dir};
+
+ foreach my $dir (grep { -d } glob "$config{master_dir}/*") {
+
+ my $zone = basename($dir);
+ $info{$zone} = { status => "OK" };
+
+ if (not -f "$dir/.index.zsk") {
+ $info{$zone}{zsk} = 0;
+ $info{$zone}{ksk} = 0;
+ $info{$zone}{kc} = 0;
+ $info{$zone}{end} = "-";
+ $info{$zone}{expiry} = undef;
+ next;
+ }
+
+ # prueft wie viele zsks genutzt werden
+ {
+ open(my ($fh), $_ = "<$dir/.index.zsk")
+ or die "Can't open $_: $!\n";
+ () = <$fh>;
+ $info{$zone}{zsk} = $.
+ }
+
+ # prueft wie viele ksks genutzt werden
+ {
+ open(my ($fh), $_ = "<$dir/.index.ksk")
+ or die "Can't open $_: $!\n";
+ () = <$fh>;
+ $info{$zone}{ksk} = $.
+ }
+
+ # prueft wie oft die schluessel zum signieren genutzt wurden
+ {
+ open(my ($fh), $_ = "<$dir/.keycounter")
+ or die "Can't open $_: $!\n";
+ chomp($info{$zone}{kc} = <$fh>);
+ }
+
+ # prueft das ablaufdatum
+ if (!-f "$dir/$zone.signed") {
+ $info{$zone}{end} = "-";
+ next;
+ }
+
+ open(my ($fh), $_ = "<$dir/$zone.signed") or die "Can't open $_: $!\n";
+ while (<$fh>) {
+ next if not /RSIG\s+SOA\s.*\s
+ (?<year>\d\d\d\d)
+ (?<mon>\d\d)
+ (?<day>\d\d)
+ (?<hour>\d\d)
+ (?<min>\d\d)\d+\s\(/ix;
+ $info{$zone}{end} = "$+{day}.$+{mon}.$+{year} $+{hour}:$+{min}";
+ $info{$zone}{expiry} =
+ timelocal(0, $+{min}, $+{hour}, $+{day}, $+{mon} - 1, $+{year});
+ }
+ }
+
+ { # output
+
+ my $sort_by =
+ $opt_expiry
+ ? sub { ($info{$a}{expiry} // 2**64) <=> ($info{$b}{expiry} // 2**64) }
+ : sub { $a cmp $b };
+
+ my $format_h = "%-35s %-8s %1s/%1s %3s %7s\n";
+ my $format_l = "%-35s %-8s %1d/%1d %5d %19s\n";
+
+ printf $format_h => qw(Domain Status ZSK KSK Used Sig-end);
+
+ foreach my $zone (sort $sort_by keys %info) {
+ printf $format_l => $zone,
+ @{ $info{$zone} }{qw(status zsk ksk kc end)};
+ }
+ }
+}
+
+__END__
+
+=head1 NAME
+
+ zone-ls -- lists all zones
+
+=head1 SYNOPSIS
+
+ zone-ls [-e|--expiry]
+
+=head1 DESCRIPTION
+
+This B<zone-ls> lists all zones under control of our dnstools suite. The output is ordered by domain name.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-e>|B<--expiry>
+
+Order the output by expiry date. The sooner the key expires, the more top the
+domain is listed.
+
+=back
+
+Additionally the common B<-h>|B<--help>|B<-m>|B<--man> options, which should be
+self explanatory.
+
+=head1 AUTHORS
+
+L<andre.suess@pipkin.cc>
+
+=cut
+
+# vim:ts=4 sw=4 ai si aw:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/zone-mk Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+
+use 5.010;
+use warnings;
+use strict;
+use Pod::Usage;
+use if $ENV{DEBUG} => "Smart::Comments";
+use Cwd qw(abs_path);
+use File::Path qw(make_path);
+use File::Basename;
+use Getopt::Long;
+use Net::LibIDN qw(:all);
+use DNStools::Config qw(get_config);
+use Template;
+
+my $CHARSET = "UTF-8";
+
+my $opt_force = 0;
+
+MAIN: {
+
+ my %cf = get_config;
+
+ GetOptions(
+ "f|force" => \$opt_force,
+ "h|help" => sub { pod2usage(-verbose => 1, -exit => 0) },
+ "m|man" => sub {
+ pod2usage(
+ -verbose => 2,
+ -noperldoc => system("perldoc -V &>/dev/null"),
+ -exit => 0
+ );
+ },
+ )
+ and @ARGV >= 2
+ or pod2usage;
+
+ my $customer = shift;
+
+ die "$cf{master_dir}: $!" if not -d -r -x $cf{master_dir};
+ die "$cf{zone_conf_dir}: $!" if not -d -r -x $cf{zone_conf_dir};
+
+ # legt fuer jede domain in @ARGV ein verzeichnis in $master_dir an.
+ # schreibt aus den angegebenen templates die dateien $zonefile und $config
+ # in die entsprechenden verzeichnisse.
+ for my $utf8domain (@ARGV) {
+
+ my $domain = idn_to_ascii($utf8domain, $CHARSET);
+ my $zonefile = "$cf{master_dir}/$domain/$domain";
+ my $configfile = "$cf{zone_conf_dir}/$domain";
+ my $now = time;
+
+ make_path dirname $zonefile;
+
+ if (-f $zonefile and not $opt_force) {
+ say "skipping $utf8domain: zone file '$zonefile' exists.";
+ next;
+ }
+
+ if (-f $configfile and not $opt_force) {
+ say "skipping $utf8domain: config file '$configfile' exists.";
+ next;
+ }
+
+ say "domain $utf8domain ($domain) for $customer.";
+
+ my %vars = (
+ domain => $domain,
+ utf8domain => $utf8domain,
+ now => $now,
+ zonefile => abs_path($zonefile),
+ customer => $customer,
+ hostmaster => $cf{hostmaster},
+ primary => $cf{primary},
+ secondary => $cf{secondary},
+ );
+
+ my $tt = Template->new(INCLUDE_PATH => $cf{template_dir})
+ or die "$Template::ERROR\n";
+
+ $tt->process("named.zone", \%vars, $zonefile) || die $tt->error;
+ $tt->process("named.config", \%vars, $configfile) || die $tt->error;
+
+ }
+
+}
+
+__END__
+
+=head1 NAME
+
+ zone-mk - create a new zone
+
+=head1 SYNOPSIS
+
+ zone-mk [-f|--force] <customer-id> <domain>...
+
+=head1 DESCRIPTION
+
+B<zone-mk> creates a new DNS zone file and the config snipped.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-f>|B<--force>
+
+Crate zone file and config even if they exist. (default: off)
+
+=item B<-h>|B<--help>
+
+=item B<-m>|B<--man>
+
+=back
+
+=head1 FILES
+
+The F<dnstools.conf> is used.
+
+=cut
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/zone-rm Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use File::Path;
+use DNStools::Config qw(get_config);
+
+# liest die Konfiguration ein
+my %config = get_config();
+
+my $master_dir = $config{"master_dir"};
+my $conf_dir = $config{"zone_conf_dir"};
+
+for (@ARGV) {
+ chomp(my $zone = `idn --quiet "$_"`);
+
+ if (-d "$master_dir/$zone") {
+ rmtree "$master_dir/$zone/"
+ and print "zone-dir for $zone removed\n";
+ }
+ else {
+ print "$master_dir/$zone: $!\n";
+ }
+
+ if (-e "$conf_dir/$zone") {
+ unlink "$conf_dir/$zone"
+ and print "configuration-file for $zone removed\n";
+ }
+ else {
+ print "$conf_dir/$zone: $!\n";
+ }
+}
--- a/dnssec-keytool.pl Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +0,0 @@
-#! /usr/bin/perl
-
-use warnings;
-use strict;
-use FindBin;
-
-sub del_double {
- my %all;
- grep { $all{$_} = 0 } @_;
- return (keys %all);
-}
-
-sub read_conf {
-
- # read configuration
- my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
- our %config;
-
- for (grep { -f } @configs) {
- open(CONFIG, $_) or die "Can't open $_: $!\n";
- }
- unless (seek(CONFIG, 0, 0)) {
- die "Can't open config (searched: @configs)\n";
- }
- while (<CONFIG>) {
- chomp;
- s/#.*//;
- s/\t//g;
- s/\s//g;
-
- next unless length;
- my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
- $config{$cname} = $ccont;
- }
- close(CONFIG);
-}
-
-sub read_argv {
- # evaluate argv or print the help
- my $arg = shift @ARGV;
- my $zone;
- our $do;
- our @zones;
- our $master_dir;
-
- if (!defined $arg) {
- print " usage: dnssec-keytool <option> zone\n";
- print " -z created a new ZSK\n";
- print " -k created a new ZSK and KSK\n";
- print " -rm deletes the key-set of a zone\n";
- print " -c created configuration files for the dnstools\n";
- print " and a new ZSK for an existing KSK\n";
- print "\n";
-
- exit;
- }
- elsif ($arg eq "-k") { $do = "ksk"; }
- elsif ($arg eq "-rm") { $do = "rm"; }
- elsif ($arg eq "-c") { $do = "ck"; }
- elsif ($arg eq "-z") { $do = "zsk"; }
- else {
- print "not a valid option.\n";
- exit;
- }
-
- # checks the zones in argv if there are managed zones
- for (@ARGV) {
- chomp($zone = `idn --quiet "$_"`);
- if (-e "$master_dir/$zone/$zone") {
- push @zones, $zone;
- }
- }
-}
-
-sub rm_keys {
- # deletes all the keys were handed over -rm in argv
- our @zones;
- our $master_dir;
- my $zone;
- my @new_zone_content;
- my @old_zone_content;
-
- for (@zones) {
- $zone = $_;
-
- my $zpf = "$master_dir/$zone";
- my $ep = 0;
-
- if (-e "$zpf/$zone.signed") {
- unlink "$zpf/$zone.signed" and $ep = 1;
- }
- if (-e "$zpf/.keycounter") {
- unlink "$zpf/.keycounter" and $ep = 1;
- }
- if (-e "$zpf/.index.ksk") {
- unlink "$zpf/.index.ksk" and $ep = 1;
- }
- if (-e "$zpf/.index.zsk") {
- unlink "$zpf/.index.zsk" and $ep = 1;
- }
- if (-e "$zpf/dsset-$zone.") {
- unlink "$zpf/dsset-$zone." and $ep = 1;
- }
- if (-e "$zpf/keyset-$zone.") {
- unlink "$zpf/keyset-$zone." and $ep = 1;
- }
-
- for (glob("$zpf/K$zone*")) {
- chomp($_);
- unlink ("$_");
- }
-
- if ($ep == 1) {
- print " * $zone: removed key-set\n";
- }
-
- open(ZONE, "$zpf/$zone")
- or die "$zpf/$zone: $!\n";
- @old_zone_content = <ZONE>;
- close(ZONE);
-
- for (@old_zone_content) {
- unless (m#\$INCLUDE.*\"K$zone.*\.key\"#) {
- push @new_zone_content, $_;
- }
- }
-
- open(ZONE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
- print ZONE @new_zone_content;
- close(ZONE);
- }
-}
-
-sub creat_ksk {
- our @zones;
- our $master_dir;
- my @index;
- my $zone;
- my $keyname;
- my $zpf;
-
- for (@zones) {
- $zone = $_;
- $zpf = "$master_dir/$zone";
-
- chdir "$zpf" or die "$zpf: $!\n";
- $keyname = `dnssec-keygen -a RSASHA1 -b 2048 -f KSK -n ZONE $zone`;
-
- unless (-f ".index.ksk") { @index = (); }
- else {
- open(INDEX, ".index.ksk") or die "$zpf/.index.ksk: $!\n";
- @index = <INDEX>;
- close(INDEX);
- }
-
- push @index, $keyname;
- if (@index > 2) { shift(@index); }
-
- open(INDEX, ">.index.ksk") or die "$zpf/.index.ksk: $!\n";
- print INDEX @index;
- close(INDEX);
-
- chomp($keyname);
- print " * $zone: new KSK $keyname\n";
-
- print "!! THE KSK must be published !! \n";
-
- }
-}
-
-sub creat_zsk {
- our @zones;
- our $master_dir;
- my @index;
- my $zone;
- my $keyname;
- my $zpf;
-
- for (@zones) {
- $zone = $_;
- $zpf = "$master_dir/$zone";
-
- chdir "$zpf" or die "$zpf: $!\n";
- $keyname = `dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`;
-
- unless (-f ".index.zsk") { @index = (); }
- else {
- open(INDEX, ".index.zsk") or die "$zpf/.index.zsk: $!\n";
- @index = <INDEX>;
- close(INDEX);
- }
-
- push @index, $keyname;
- if (@index > 2) { shift(@index); }
-
- open(INDEX, ">.index.zsk") or die "$zpf/.index.zsk: $!\n";
- print INDEX @index;
- close(INDEX);
-
- chomp($keyname);
- print " * $zone: new ZSK $keyname\n";
-
- open(KC, ">.keycounter") or die "$zpf/keycounter: $!\n";
- print KC "0";
- close(KC);
-
- }
-}
-
-sub ck_zone {
- our @zones;
- our $master_dir;
- my $zone;
-
- for (@zones) {
- $zone = $_;
- my $zpf = "$master_dir/$zone";
- my $keyfile;
- my @content;
- my @keylist;
-
- for (<$zpf/*>) {
- if (m#(K$zone.*\.key)#) {
- $keyfile = $1;
- open(KEYFILE, "<$zpf/$keyfile");
- @content = <KEYFILE>;
- close(KEYFILE);
- for (@content) {
- if (m#DNSKEY.257#) {
- push @keylist, $keyfile;
- }
- }
- }
- }
-
- open(INDEX, ">.index.ksk") or die "$zpf/.index.ksk: $!\n";
- for (@keylist) {
- s#\.key##;
- print INDEX "$_\n";
- }
- close(INDEX);
-
- print " * $zone: new .index.ksk created\n";
-
- if (-f "$zpf/.index.zsk") {
- unlink("$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
- }
- }
-}
-
-sub post_creat {
- our @zones;
- our $master_dir;
-
- for (@zones) {
- my $zone = $_;
- `touch $master_dir/$zone/$zone`;
-
- &kill_useless_keys($zone);
- &key_to_zonefile($zone);
- }
-
-}
-
-sub kill_useless_keys {
-
- # the function deletes all keys that are not available in the zone
- # of index.zsk
- our $master_dir;
- my $zone = $_[0];
- my @keylist = ();
- my $zpf = "$master_dir/$zone";
-
- open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
- @keylist = <INDEX>;
- close(INDEX);
- open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
- push @keylist, <INDEX>;
-
- # shortened the key name from the index file on the id in order to
- # be able to compare
- for (@keylist) {
- chomp;
- s#K.*\+.*\+(.*)#$1#;
- }
-
- # reviewed every key file (KSK, ZSK), whether they are described in
- # the respective index file. if not they will be deleted.
- for ( glob("$master_dir/$zone/K*") {
- chomp;
- my $file = $_;
- my $rm_count = 1;
- my $keyname;
- for (@keylist) {
- if ($file =~ /$_/) { $rm_count = 0; }
- }
- if ($rm_count == 1) {
- unlink "$file";
- if ($file =~ /$zpf\/(.*\.key)/) {
- print " * $zone: Schluessel $1 entfernt \n";
- }
- }
- }
-}
-
-sub key_to_zonefile {
-
- # the function added all keys to the indexfile
- our $master_dir;
- my $zone = $_[0];
- my $zpf = "$master_dir/$zone";
- my @old_content;
- my @new_content = ();
-
- open(ZONEFILE, "<$zpf/$zone");
- @old_content = <ZONEFILE>;
- close(ZONEFILE);
-
- for (@old_content) {
- unless (m#INCLUDE.*key#) { push @new_content, $_; }
- }
-
- for (<$zpf/*>) {
- if (m#(.*\/)(K.*\.key)#) {
- push @new_content, "\$INCLUDE \"$2\"\n";
- }
- }
- open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
- print ZONEFILE @new_content;
- close(ZONEFILE);
-}
-
-&read_conf;
-
-our %config;
-our $do; # statements from argv
-our @zones; # list of zones from argv
-our $master_dir = $config{master_dir};
-our $bind_dir = $config{bind_dir};
-our $conf_dir = $config{zone_conf_dir};
-our $sign_alert_time = $config{sign_alert_time};
-our $indexzone = $config{indexzone};
-our $key_counter_end = $config{key_counter_end};
-our $ablauf_zeit = $config{abl_zeit};
-
-&read_argv;
-
-# completed the program, if not a valid zones was handed over
-unless (@zones) { exit; }
-
-if ($do eq "rm") { &rm_keys; exit; }
-if ($do eq "ck") { &ck_zone; }
-if ($do eq "ksk") { &creat_ksk; }
-
-&creat_zsk;
-&post_creat;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-dnssec-keytool
-
-=head1 SYNOPSIS
-
-dnssec-keytool <option> zone
-
-=head1 DESCRIPTION
--- a/dnstools.conf Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-bind_dir = /etc/bind # bind-Hauptverzeichnis
-master_dir = /etc/bind/master # Verzeichnis für die einzelnen Zonen-Verzeichnisse
-zone_conf_dir = /etc/bind/zones.d # Verzeichnis für die Zonen-Konfigurationdateien
-
-key_counter_end = 15 # Anzahl der Signierungen bis zum Key-Rollover
-sign_alert_time = 168 # Warn-Zeitraum vor dem Ablauf einer Zone-Signatur in h
-abl_zeit = 24 # Dauer des Key-Rollover (2 Schluessel) in h
-
-secondary = hh.schlittermann.de
-primary = pu.schlittermann.de
-
-indexzone = idx.net.schlittermann.de # Name der Indexdatei
-
-#this_host
-#this_ip
-#this_domain
-#secondary_ip
-#hostmaster
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dnstools.conf.example Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,21 @@
+bind_dir = ./bind # bind's config directory (named.conf)
+master_dir = ./bind/master # location of the master zone directories
+zone_conf_dir = ./bind/zones.d # location of configs for the zones
+
+key_counter_end = 15 # Anzahl der Signierungen bis zum Key-Rollover
+sign_alert_time = 168 # Warn-Zeitraum vor dem Ablauf einer Zone-Signatur in h
+abl_zeit = 24 # Dauer des Key-Rollover (2 Schluessel) in h
+
+secondary = hh.schlittermann.de
+primary = pu.schlittermann.de
+hostmaster = hostmaster@schlittermann.de
+
+indexzone = idx.net.schlittermann.de # Name der Indexdatei
+
+template_dir = ./templates
+
+#this_host
+#this_ip
+#this_domain
+#secondary_ip
+#hostmaster
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/DNStools/.perltidyrc Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,1 @@
+../../.perltidyrc
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/DNStools/Config.pm Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,82 @@
+package DNStools::Config;
+
+use strict;
+use warnings;
+
+use base "Exporter";
+
+our $VERSION = 0.0;
+our @EXPORT_OK = qw(get_config);
+
+sub get_config(@) {
+
+ # read configuration
+ my @configs =
+ @_ ? @_
+ : defined $ENV{DNSTOOLS_CONF} ? $ENV{DNSTOOLS_CONF}
+ : ("dnstools.conf", "$ENV{HOME}/.dnstools.conf", "/etc/dnstools.conf");
+ my %config;
+
+ # the first config FILE
+ my ($_) = grep { -f } @configs;
+
+ die "no config file found, searched for @configs\n" if not $_;
+ open(my $cf, $_) or die "Can't open $_: $!\n";
+
+ while (<$cf>) {
+ s/#.*//;
+ s/\s//g;
+ next unless length;
+ my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
+ $config{$cname} = $ccont;
+ }
+
+ # now merge the config hashes
+ foreach my $o (grep { ref eq "HASH" } @configs) {
+ %config =
+ (%config, map { $_ => $o->{$_} } grep { defined $o->{$_} } keys %$o);
+ }
+ return %config;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+ DNStools::Config - config parser
+
+=head1 SYNOPSIS
+
+ use DNStools::Config qw(get_config);
+ %config = get_config();
+ %config = get_config($file1, $file2, ...);
+
+=head1 DESCRIPTION
+
+Simple config file parser. The format is simple:
+
+ key = value
+
+All spaces are ignored.
+
+=head1 FUNCTIONS
+
+=over
+
+=item B<get_config>(I<list of config files>)
+
+Read the first file of the list (or dies if none of the files is found).
+Returns a hash with the config keys and values.
+
+If the list is empty, the configuration file is search in some default
+locations: C<$DNSTOOLS_CONF>, F<./dnstools.conf>,
+F<$HOME/.dnstools.conf>, F</etc/dnstools.conf>.
+
+=back
+
+=cut
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/t/00-config.t Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,45 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+use Pod::Coverage;
+use File::Temp;
+
+BEGIN {
+ use_ok "DNStools::Config" => qw(get_config);
+}
+
+can_ok("DNStools::Config" => "get_config");
+
+eval { get_config("xxx|xxx", "yyy|yyy") };
+ok($@, "dies on missing config");
+
+my ($tmp, %cf);
+
+# prepare some simple sample config
+$tmp = File::Temp->new();
+print {$tmp} <<__EOF;
+# comment
+abc = xyz
+other = value with space
+__EOF
+close($tmp);
+
+# the files is specified, it should find the first
+# existing
+%cf = get_config("xxx|xxx", $tmp->filename);
+ok(%cf, "got config");
+is($cf{abc} => "xyz", "simple value");
+is($cf{other} => "valuewithspace", "spaced value");
+
+# it should find the file specified in $ENV{DNSTOOLS_CONF}
+$ENV{DNSTOOLS_CONF} = $tmp->filename;
+%cf = ();
+%cf = get_config("xxx|xxx", $tmp->filename);
+ok(%cf, "got config from \$DNSTOOLS_CONF");
+is($cf{abc} => "xyz", "simple value");
+is($cf{other} => "valuewithspace", "spaced value");
+
+
+done_testing();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/t/01-zone-mk Sun Jan 23 00:30:15 2011 +0100
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+
+use Test::More;
+use File::Temp;
+use Net::LibIDN qw(:all);
+
+my $CMD = "perl -Mblib blib/script/zone-mk";
+
+system("$CMD -h &>/dev/null");
+is($? => 0, "exit on help");
+
+system("$CMD -m &>/dev/null");
+is($? => 0, "exit on man");
+
+system("$CMD &>/dev/null");
+ok($? > 0, "error on missing args");
+
+my $master_dir = File::Temp->newdir;
+my $config_dir = File::Temp->newdir;
+my $tmp = File::Temp->new;
+$ENV{DNSTOOLS_CONF} = $tmp->filename;
+
+print $tmp <<__EOF;
+master_dir = $master_dir
+zone_conf_dir = $config_dir
+hostmaster = hostmaster\@schlittermann.de
+template_dir = ./templates
+__EOF
+
+system("$CMD xxx müller.de &>/dev/null");
+is($? => 0, "created zone");
+
+my $domain = idn_to_ascii("müller.de", "UTF-8");
+ok(-s "$master_dir/$domain/$domain", "zone file $domain");
+ok(-s "$config_dir/$domain", "config file $domain");
+
+
+done_testing;
--- a/templates/named.config Tue Dec 21 17:00:11 2010 +0100
+++ b/templates/named.config Sun Jan 23 00:30:15 2011 +0100
@@ -1,10 +1,10 @@
-zone "<domain>" {
-// Start: <start>
-// Invoice: <customer>
-// UTF8: <utf8domain>
+[% USE iso = date(format => '%Y-%m-%dT%H:%M:%S') -%]
+zone "[% domain %]" {
+// Start: [% iso.format(now) %]
+// Invoice: [% customer %]
+// UTF8: [% utf8domain %]
type master;
- file "<file>";
- allow-transfer { localhost; <primary_ip>; <secondary_ip>; };
+ file "[% zonefile %]";
+ allow-transfer { localhost; slaves; };
allow-query { any; };
- also-notify { <primary_ip>; };
};
--- a/templates/named.zone Tue Dec 21 17:00:11 2010 +0100
+++ b/templates/named.zone Sun Jan 23 00:30:15 2011 +0100
@@ -1,17 +1,19 @@
-$ORIGIN <domain>.
+[% USE iso = date(format => '%Y-%m-%dT%H:%M:%S') %]
+[% USE serial = date(format => '%Y%m%d00') -%]
+$ORIGIN [% domain %].
$TTL 1d
-@ IN SOA <primary>. <hostmaster>. (
- <time> ; serial
+@ IN SOA [% primary %]. [% hostmaster %]. (
+ [% serial.format(now) %] ; serial
1d ; refresh
2h ; retry
7d ; expire
- 1d ; default ttl
+ 5m ; negative ttl
)
- IN TXT "invoice: <customer>"
- IN TXT "start: <start>"
- IN TXT "utf8: <utf8domain>"
+ IN TXT "invoice: [% customer %]"
+ IN TXT "start: [% now %] [% iso.format(now) %]
+ IN TXT "utf8: [% utf8domain %]"
- IN NS <primary>.
- IN NS <secondary>.
+ IN NS [% primary %].
+ IN NS [% secondary %].
--- a/update-serial.pl Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,583 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use FindBin;
-use File::Basename;
-
-sub del_double {
-
- # remove duplicate entries
- my %all;
- grep { $all{$_} = 0 } @_;
- return (keys %all);
-}
-
-sub read_conf {
-
- # read the configuration
- my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
- our %config;
-
- for (grep { -f } @configs) {
- open(CONFIG, $_) or die "Can't open $_: $!\n";
- }
- unless (seek(CONFIG, 0, 0)) {
- die "Can't open config (searched: @configs)\n";
- }
- while (<CONFIG>) {
- chomp;
- s/#.*//;
- s/\t//g;
- s/\s//g;
-
- next unless length;
- my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
- $config{$cname} = $ccont;
- }
- close(CONFIG);
-}
-
-sub add_argv {
- # checked whether the zones in argv are managed zones and
- #inserted them into the list new_serial
- our @new_serial;
- our $master_dir;
- my $zone;
-
- for (@ARGV) {
- chomp($zone = `idn --quiet "$_"`);
- if (-e "$master_dir/$zone/$zone") {
- push @new_serial, $zone;
- }
- }
-}
-
-sub changed_zone {
- our $master_dir;
- our @new_serial;
-
- for (<$master_dir/*>) {
- my $zone = basename($_);
-
- if (-e "$master_dir/$zone/.stamp") {
- my $stamptime = (-M "$master_dir/$zone/.stamp");
- my $filetime = (-M "$master_dir/$zone/$zone");
- if ($stamptime > $filetime) {
- push @new_serial, $zone;
- print " * $zone: zonedatei wurde geaendert\n";
- }
- }
- else {
- print " * $zone: keine .stamp-datei gefunden\n"
- ; # NOCH IN NEW_SERIAL PUSHEN
- push @new_serial, $zone;
- }
- }
-
-}
-
-sub sign_end {
- our $sign_alert_time; # the time between the end and the new signing
- # (see external configuration)
- our $master_dir;
- our @new_serial;
-
- # erzeugt $time (die zeit ab der neu signiert werden soll)
- chomp(my $unixtime = `date +%s`);
- $unixtime = $unixtime + (3600 * $sign_alert_time);
- my $time = `date -d \@$unixtime +%Y%m%d%H`;
-
- ## vergleicht fuer alle zonen im ordner $master_dir mit einer
- ## <zone>.signed-datei den zeitpunkt in $time mit dem ablaufdatum der
- ## signatur, welcher aus der datei <zone>.signed ausgelesen wird.
- for (<$master_dir/*>) {
- s#($master_dir/)(.*)#$2#;
- my $zone = $_;
-
- if (-e "$master_dir/$zone/$zone.signed") {
- open(ZONE, "$master_dir/$zone/$zone.signed");
- my @zone_sig_content = <ZONE>;
- close(ZONE);
-
- for (@zone_sig_content) {
- if (m#SOA.*[0-9]{14}#) {
- s#.*([0-9]{10})([0-9]{4}).*#$1#;
- if ($_ < $time) {
- push @new_serial, $zone;
- }
- }
- }
- }
- }
-}
-
-sub sign_zone {
-
- # signiert die zonen und erhoeht den wert in der keycounter-datei
- our @new_serial;
- our $master_dir;
- my $zone;
- my $kc;
-
- for (&del_double(@new_serial)) {
- $zone = $_;
-
- unless (-e "$master_dir/$zone/.index.zsk") {
- next;
- }
-
- chdir "$master_dir/$zone";
- if (`dnssec-signzone $zone 2>/dev/null`) {
- print " * $zone neu signiert \n";
-
- # erhoeht den keycounter
- if ("$master_dir/$zone/.keycounter") {
- open(KC, "$master_dir/$zone/.keycounter");
- $kc = <KC>;
- close(KC);
- $kc += 1;
- }
- else {
- $kc = 1;
- }
- open(KC, ">$master_dir/$zone/.keycounter");
- print KC $kc;
- close(KC);
- }
- else { print "$zone konnte nicht signiert werden \n"; }
- }
-}
-
-sub update_serial {
- our $master_dir;
- our @new_serial;
- chomp(my $date = `date +%Y%m%d`);
- my @new_content;
- my $sdate;
- my $scount;
- my $serial;
-
- for (&del_double(@new_serial)) {
-
- # erhoeht den serial
- my $zone = $_;
- my $file = "$master_dir/$zone/$zone";
- my @new_content = ();
-
- open(SER, "<$file") or die "$file: $!\n";
- for (<SER>) {
- if (/^\s+(\d+)(\d{2})\s*;\s*serial/i) {
- $sdate = $1;
- $scount = $2;
- $serial = "$sdate$scount";
- if ($date eq $sdate) {
- $scount++;
- }
- else {
- $sdate = $date;
- $scount = "00";
- }
- }
- if ($serial) {
- s/$serial/$sdate$scount/;
- }
- push @new_content, $_;
- }
- close(SER);
-
- open(RES, ">$file") or die "$file: $!\n";
- print RES @new_content;
- close(RES);
- print " * $zone: serial erhoeht \n";
-
- open(STAMP, ">$master_dir/$zone/.stamp")
- or die "$master_dir/$zone/.stamp: $!\n";
- close(STAMP);
- print " * $zone: stamp aktualisiert \n";
- }
-}
-
-sub mk_zone_conf {
-
- # erzeugt eine named.conf-datei aus den entsprechenden vorlagen.
- our $bind_dir;
- our $conf_dir;
-
- open(TO, ">$bind_dir/named.conf.zones")
- or die "$bind_dir/named.conf.zones: $!\n";
- while (<$conf_dir/*>) {
- open(FROM, "$_") or die "$_: $! \n";
- print TO <FROM>;
- close(FROM);
- }
- close(TO);
- print "** zonekonfiguration erzeugt\n";
-}
-
-sub update_index {
-
- # aktualisiert die indexzone;
- our @new_serial;
- our $indexzone;
- our $master_dir;
- my @iz_content_old;
- my @iz_content_new;
-
- open(INDEXZONE, "$master_dir/$indexzone/$indexzone")
- or die "$master_dir/$indexzone/$indexzone: $!\n";
- @iz_content_old = <INDEXZONE>;
- close(INDEXZONE);
-
- for (@iz_content_old) {
- unless (m#ZONE::#) {
- push @iz_content_new, $_;
- }
- }
-
- for my $dir (glob "$master_dir/*") {
- my $zone = basename($dir);
- my $info_end = "::sec-off";
-
- if (-e "$dir/.keycounter") {
- $info_end = "::sec-on";
- }
-
- my $iz_line = "\t\tIN TXT\t\t\"ZONE::$zone$info_end\"\n";
-
- push @iz_content_new, $iz_line;
- }
-
- open(INDEXZONE, ">$master_dir/$indexzone/$indexzone")
- or die "$master_dir/$indexzone/$indexzone: $!\n";
- print INDEXZONE @iz_content_new;
- close(INDEXZONE);
-
- # fuegt die index-zone in die liste damit der serial erhoet wird
- push @new_serial, $indexzone;
-
- print "** index-zone aktualisiert \n";
-}
-
-sub file_entry {
-
- # prueft jede domain, die ein verzeichnis in $master_dir hat, ob sie
- # dnssec nutzt.
- # passt die eintraege in $config_file falls noetig an.
- our $master_dir;
- our $conf_dir;
-
- while (<$master_dir/*>) {
- s#($master_dir/)(.*)#$2#;
- my $zone = $_;
- my $zone_file = "$master_dir/$zone/$zone";
- my $conf_file = "$conf_dir/$zone";
- my @c_content;
-
- unless (-f "$conf_file") {
- die "$conf_file: $! \n";
- }
-
- if (-e "$master_dir/$zone/.keycounter") {
- open(FILE, "<$conf_file") or die "$conf_file: $!\n";
- @c_content = <FILE>;
- close(FILE);
- for (@c_content) {
- if (m{(.*)($zone_file)(";)}) {
- print
- " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n";
- $_ = "$1$2.signed$3\n";
- }
- }
- open(FILE, ">$conf_file") or die "$conf_file: $!\n";
- print FILE @c_content;
- close(FILE);
- }
- else {
- open(FILE, "<$conf_file") or die "$conf_file: $!\n";
- @c_content = <FILE>;
- close(FILE);
- for (@c_content) {
- if (m{(.*)($zone_file)\.signed(.*)}) {
- print
- " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n";
- $_ = "$1$2$3\n";
- }
- }
- open(FILE, ">$conf_file") or die "$conf_file: $!\n";
- print FILE @c_content;
- close(FILE);
- }
- }
-}
-
-sub server_reload {
- if (`rndc reload`) { print "** reload dns-server \n" }
-}
-
-sub to_begin_ro {
-
- # gibt alle zonen mit abgelaufenen keycounter in die liste @begin_ro_list
- our @begin_ro_list;
- our $master_dir;
- our $key_counter_end;
- our @new_serial;
- my $zone;
-
- while (<$master_dir/*>) {
- chomp($zone = $_);
- my $key;
-
- unless (-f "$zone/.keycounter") { next; }
-
- open(KEY, "$zone/.keycounter") or die "$zone/.keycounter: $!\n";
- $key = <KEY>;
- close(KEY);
-
- # vergleicht den wert aus der keycount-datei mit dem wert aus der
- #dnstools.conf (key_counter_end)
- if ($key_counter_end <= $key) {
- $zone =~ s#($master_dir/)(.*)#$2#;
- push @begin_ro_list, $zone;
- }
- }
-}
-
-sub to_end_ro {
-
- # funktion ueberprueft ob ein keyrollover fertig ist
- # die bedingung dafuer ist das:
- # - eine datei .index.zsk vorhanden ist
- # - die datei .index.zsk vor mehr x stunden geaendert wurde
- # - die datei .index.zsk ueber mehr als zwei zeilen gross ist
- our $master_dir;
- our @end_ro_list;
- our $ablauf_zeit;
- chomp(my $now_time = `date +%s`);
-
- for (<$master_dir/*>) {
- my $zone = $_;
- $zone =~ s#($master_dir/)(.*)#$2#;
-
- my @index = ();
- my $index_wc;
- my @status;
-
- # prueft nach der ".index.zsk"-datei und erstellt den zeitpunkt
- # an dem das key-rollover endet. - $status[9]
- if (-e "$master_dir/$zone/.index.zsk") {
- @status = stat("$master_dir/$zone/.index.zsk");
- $status[9] += (3600 * $ablauf_zeit);
- }
- else { next; }
-
- # $status[9] ist der zeitpunkt an dem der key-rollover endet
- # prueft ob das key-rollover-ende erreicht ist
- unless ($status[9] < $now_time) { next; }
-
- # prueft die anzahl der schluessel in der .index.zsk
- open(INDEX, "$master_dir/$zone/.index.zsk")
- or die "$master_dir/$zone/.index.zsk: $!\n";
- @index = <INDEX>;
- $index_wc = @index;
- close(INDEX);
- if ($index_wc > 1) { push @end_ro_list, $zone; }
- }
-}
-
-sub begin_ro {
-
- # anfang des key-rollovers
- our @begin_ro_list;
- our $master_dir;
- our @new_serial;
-
- for (&del_double(@begin_ro_list)) {
-
- #erzeugt zsks
- my $zone = $_;
- my $zpf = "$master_dir/$zone";
- my @index;
-
- chdir "$zpf" or die "$zpf: $!\n";
- my $keyname = `dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`;
-
- open(INDEX, ".index.zsk") or die "$zpf/.index.zsk: $!\n";
- @index = <INDEX>;
- close(INDEX);
-
- push @index, $keyname;
- if (@index > 2) { shift(@index); }
-
- open(INDEX, ">.index.zsk") or die "$zpf/.index.zsk: $!\n";
- print INDEX @index;
- close(INDEX);
-
- chomp($keyname);
- print " * $zone: neuer ZSK $keyname erstellt\n";
-
- open(KC, ">.keycounter") or die "$zpf/keycounter: $!\n";
- print KC "0";
- close(KC);
-
- &kill_useless_keys($zone);
- &key_to_zonefile($zone);
- push @new_serial, $zone;
- }
-}
-
-sub key_to_zonefile {
-
- # die funktion fugt alle schluessel in eine zonedatei
- our $master_dir;
- my $zone = $_[0];
- my $zpf = "$master_dir/$zone";
- my @old_content;
- my @new_content = ();
-
- open(ZONEFILE, "<$zpf/$zone");
- @old_content = <ZONEFILE>;
- close(ZONEFILE);
-
- for (@old_content) {
- unless (m#INCLUDE.*key#) { push @new_content, $_; }
- }
-
- for (<$zpf/*>) {
- if (m#(.*\/)(K.*\.key)#) {
- push @new_content, "\$INCLUDE \"$2\"\n";
- }
- }
- open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
- print ZONEFILE @new_content;
- close(ZONEFILE);
-}
-
-sub kill_useless_keys {
-
- # die funktion loescht alle schluessel die nicht in der index.zsk
- # der uebergebenen zone stehen
- our $master_dir;
- my $zone = $_[0];
- my @keylist = ();
- my $zpf = "$master_dir/$zone";
-
- open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
- @keylist = <INDEX>;
- close(INDEX);
- open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
- push @keylist, <INDEX>;
-
- # kuerzt die schluessel-bezeichnung aus der indexdatei auf die id um sie
- # besser vergleichen zu koennen.
- for (@keylist) {
- chomp;
- s#K.*\+.*\+(.*)#$1#;
- }
-
- # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen
- # indexdatei beschrieben sind. wenn nicht werden sie geloescht.
- for (`ls $master_dir/$zone/K*[key,private]`) {
- chomp;
- my $file = $_;
- my $rm_count = 1;
- my $keyname;
- for (@keylist) {
- if ($file =~ /$_/) { $rm_count = 0; }
- }
- if ($rm_count == 1) {
- unlink "$file";
- if ($file =~ /$zpf\/(.*\.key)/) {
- print " * $zone: Schluessel $1 entfernt \n";
- }
- }
- }
-}
-
-sub end_ro {
- our @end_ro_list;
- our $master_dir;
- our @new_serial;
- my @content;
-
- for (@end_ro_list) {
- my $zone = $_;
- my $count = 0;
- my @content;
- my $last_key;
-
- open(INDEX, "<$master_dir/$zone/.index.zsk");
- @content = <INDEX>;
- close(INDEX);
-
- for (@content) {
- $count++;
- $last_key = $_;
- }
- if ($count > 1) {
- open(INDEX, ">$master_dir/$zone/.index.zsk");
- print INDEX $last_key;
- close(INDEX);
- }
- &kill_useless_keys($zone);
- &key_to_zonefile($zone);
- push @new_serial, $zone;
- }
-}
-
-&read_conf;
-
-our %config;
-our @new_serial; # liste fuer neuen serial
-our @begin_ro_list; # liste mit zonen deren key-rollover beginnt
-our @end_ro_list; # liste mit zonen deren key-rollover fertig ist
-our $master_dir = $config{master_dir};
-our $bind_dir = $config{bind_dir};
-our $conf_dir = $config{zone_conf_dir};
-our $sign_alert_time = $config{sign_alert_time};
-our $indexzone = $config{indexzone};
-our $key_counter_end = $config{key_counter_end};
-our $ablauf_zeit = $config{abl_zeit};
-
-&add_argv;
-&changed_zone;
-&sign_end;
-
-&to_begin_ro; # prueft nach beginnenden rollover-verfahren
-&to_end_ro; # prueft nach endenden rollover-verfahren
-
-if (@begin_ro_list) {
- &begin_ro; # eine rollover-beginn-sequenz
-}
-
-if (@end_ro_list) {
- &end_ro; # eine rollover-end-squenz
-}
-
-if (@new_serial) {
- &update_index; # index zone aktuallisieren
- &update_serial; # serial aktuallisieren
- &sign_zone; # zone signieren
-}
-
-&file_entry; # bearbeitet die file-eintraege der konfigurations-datei
-&mk_zone_conf; # konfiguration zusammenfuegen
-&server_reload; # server neu laden
-
-
-
-__END__
-
-=pod
-
-=head1 TITLE
-
-update-serial
-
-=head1 SYNTAX
-
-update-serial
-
-=head1 BESCHREIBUNG
-
-=cut
--- a/zone-ls.pl Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-#! /usr/bin/perl
-
-use v5.10;
-use strict;
-use warnings;
-use Pod::Usage;
-use File::Basename;
-use FindBin;
-use Time::Local;
-use Getopt::Long;
-
-my %config;
-my $opt_expiry = undef;
-
-MAIN: {
- my %info; # will hold the information we collected
-
- GetOptions(
- "e|expiry" => \$opt_expiry,
- "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) },
- "m|man" => sub {
- pod2usage(
- -exit => 0,
- -verbose => 2,
- -noperldoc => system("perldoc -V &>/dev/null")
- );
- },
- ) or pod2usage;
-
- { # find and read/parse the config (could use some common config parser)
- my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
- ($_) = grep { -f } @configs;
- open(my $config, $_) or die "Can't open $_: $!\n";
-
- while (<$config>) {
- chomp;
- s/#.*//;
- s/\s//g;
- my ($k, $v) = split(/\s*=\s*/, $_, 2) or next;
- $config{$k} = $v;
- }
- }
-
- die "$config{master_dir}: $!\n" if not -d $config{master_dir};
-
- foreach my $dir (grep { -d } glob "$config{master_dir}/*") {
-
- my $zone = basename($dir);
- $info{$zone} = { status => "OK" };
-
- if (not -f "$dir/.index.zsk") {
- $info{$zone}{zsk} = 0;
- $info{$zone}{ksk} = 0;
- $info{$zone}{kc} = 0;
- $info{$zone}{end} = "-";
- $info{$zone}{expiry} = undef;
- next;
- }
-
- # prueft wie viele zsks genutzt werden
- {
- open(my ($fh), $_ = "<$dir/.index.zsk")
- or die "Can't open $_: $!\n";
- () = <$fh>;
- $info{$zone}{zsk} = $.
- }
-
- # prueft wie viele ksks genutzt werden
- {
- open(my ($fh), $_ = "<$dir/.index.ksk")
- or die "Can't open $_: $!\n";
- () = <$fh>;
- $info{$zone}{ksk} = $.
- }
-
- # prueft wie oft die schluessel zum signieren genutzt wurden
- {
- open(my ($fh), $_ = "<$dir/.keycounter")
- or die "Can't open $_: $!\n";
- chomp($info{$zone}{kc} = <$fh>);
- }
-
- # prueft das ablaufdatum
- if (!-f "$dir/$zone.signed") {
- $info{$zone}{end} = "-";
- next;
- }
-
- open(my ($fh), $_ = "<$dir/$zone.signed") or die "Can't open $_: $!\n";
- while (<$fh>) {
- next if not /RSIG.*SOA.*\s
- (?<year>\d\d\d\d)
- (?<mon>\d\d)
- (?<day>\d\d)
- (?<hour>\d\d)
- (?<min>\d\d)\d+\s\(/ix;
- $info{$zone}{end} = "$+{day}.$+{mon}.$+{year} $+{hour}:$+{min}";
- $info{$zone}{expiry} =
- timelocal(0, $+{min}, $+{hour}, $+{day}, $+{mon} - 1, $+{year});
- }
- }
-
- { # output
-
- my $sort_by =
- $opt_expiry
- ? sub { ($info{$a}{expiry} // 2**64) <=> ($info{$b}{expiry} // 2**64) }
- : sub { $a cmp $b };
-
- my $format_h = "%-35s %-8s %1s/%1s %3s %7s\n";
- my $format_l = "%-35s %-8s %1d/%1d %5d %19s\n";
-
- printf $format_h => qw(Domain Status ZSK KSK Used Sig-end);
-
- foreach my $zone (sort $sort_by keys %info) {
- printf $format_l => $zone,
- @{ $info{$zone} }{qw(status zsk ksk kc end)};
- }
- }
-}
-
-__END__
-
-=head1 NAME
-
- zone-ls -- lists all zones
-
-=head1 SYNOPSIS
-
- zone-ls [-e|--expiry]
-
-=head1 DESCRIPTION
-
-This B<zone-ls> lists all zones under control of our dnstools suite. The output is ordered by domain name.
-
-=head1 OPTIONS
-
-=over
-
-=item B<-e>|B<--expiry>
-
-Order the output by expiry date. The sooner the key expires, the more top the
-domain is listed.
-
-=back
-
-Additionally the common B<-h>|B<--help>|B<-m>|B<--man> options, which should be
-self explanatory.
-
-=head1 AUTHORS
-
-L<andre.suess@pipkin.cc>
-
-=cut
-
-# vim:ts=4 sw=4 ai si aw:
--- a/zone-mk.pl Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-#!/usr/bin/perl
-
-use warnings;
-use strict;
-use FindBin;
-
-if (@ARGV < 2) {
- print "usage: zone-mk kundennummer domain ... \n";
- exit 1;
-}
-
-# oeffnet Konfigurations- und Templatefiles - relativ oder absolut
-my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
-my @templc = (
- "$FindBin::Bin/templates/named.config",
- "/etc/dnstools/templates/named.config"
-);
-my @templz =
- ("$FindBin::Bin/templates/named.zone", "/etc/dnstools/templates/named.zone");
-my %config;
-
-for (grep { -f } @configs) {
- open(CONFIG, $_) or die "Can't open $_: $!\n";
-}
-unless (seek(CONFIG, 0, 0)) {
- die "Can't open config (searched: @configs)\n";
-}
-
-for (grep { -f } @templc) {
- open(TEMPCONF, $_) or die "Can't open $_: $!\n";
-}
-unless (seek(TEMPCONF, 0, 0)) {
- die "Can't open template (searched: @templc)\n";
-}
-
-for (grep { -f } @templz) {
- open(TEMPZONE, $_) or die "Can't open $_: $!\n";
-}
-unless (seek(TEMPZONE, 0, 0)) {
- die "Can't open template (searched: @templz)\n";
-}
-
-while (<CONFIG>) {
- chomp;
- s/#.*//;
- s/\t//g;
- s/\s//g;
- next unless length;
- my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
- $config{$cname} = $ccont;
-}
-close(CONFIG);
-
-my $primary = $config{primary};
-my $secondary = $config{secondary};
-my $zone_conf_dir = $config{zone_conf_dir};
-my $master_dir = $config{master_dir};
-my $customer = shift @ARGV;
-chomp(my $primary_ip = `dig +short $primary`);
-chomp(my $secondary_ip = `dig +short $secondary`);
-chomp(my $this_host = `hostname -f`);
-chomp(my $this_ip = `hostname -i`);
-chomp(my $this_domain = `hostname -d`);
-chomp(my $time = `date +%Y%m%d00`);
-chomp(my $start = `date -I`);
-my $hostmaster = "hostmaster.$this_domain";
-
-unless (-d $master_dir and -r $master_dir) {
- die "$master_dir: $!\n";
-}
-
-unless (-d $zone_conf_dir and -r $zone_conf_dir) {
- die "$master_dir: $!\n";
-}
-
-# legt fuer jede domain in @ARGV ein verzeichnis in $master_dir an.
-# schreibt aus den angegebenen templates die dateien $zonefile und $config
-# in die entsprechenden verzeichnisse.
-for (@ARGV) {
-
- chomp(my $domain = `idn --quiet "$_"`);
- my $zonefile = "$master_dir/$domain/$domain";
- my $config = "$zone_conf_dir/$domain";
- my $utf8domain = "$_";
-
- unless (-d "$master_dir/$domain") {
- `mkdir $master_dir/$domain`;
- }
-
- if (-f $zonefile) {
- $zonefile =~ s#/.*/##;
- print "$zonefile exists. Skipping $domain\n";
- next;
- }
- if (-f $config) {
- $config =~ s#/.*/##;
- print "$config exists. Skipping $domain\n";
- next;
- }
-
- print "$domain ($_) for $customer \n";
-
- my @tempzone = <TEMPZONE>;
- for (@tempzone) {
- s#<start>#$start#;
- s#<domain>#$domain#;
- s#<time>#$time#;
- s#<primary>#$primary#;
- s#<secondary>#$secondary#;
- s#<hostmaster>#$hostmaster#;
- s#<customer>#$customer#;
- s#<utf8domain>#$utf8domain#;
- }
-
- open(ZONEOUT, ">$zonefile");
- print ZONEOUT @tempzone;
- close(ZONEOUT);
-
- my @tempconf = <TEMPCONF>;
- for (@tempconf) {
- s#<domain>#$domain#;
- s#<start>#$start#;
- s#<customer>#$customer#;
- s#<utf8domain>#$utf8domain#;
- s#<file>#$master_dir/$domain/$domain#;
- s#<primary_ip>#$primary_ip#;
- s#<secondary_ip>#$secondary_ip#;
- }
-
- open(CONFOUT, ">$config");
- print CONFOUT @tempconf;
- close(CONFOUT);
-}
--- a/zone-rm.pl Tue Dec 21 17:00:11 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#!/usr/bin/perl
-
-use warnings;
-use strict;
-use File::Path;
-use FindBin;
-
-# liest die Konfiguration ein
-my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
-my %config;
-
-foreach (grep { -f } @configs) {
- open(CONFIG, $_) or die "Can't open $_: $!\n";
-}
-
-unless (seek(CONFIG, 0, 0)) {
- die "Can't open config (searched: @configs)\n";
-}
-
-while (<CONFIG>) {
- chomp;
- s/#.*//;
- s/\t//g;
- s/\s//g;
- next unless length;
- my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
- $config{$cname} = $ccont;
-}
-close(CONFIG);
-
-my $master_dir = $config{"master_dir"};
-my $conf_dir = $config{"zone_conf_dir"};
-
-for (@ARGV) {
- chomp(my $zone = `idn --quiet "$_"`);
-
- if (-d "$master_dir/$zone") {
- rmtree "$master_dir/$zone/"
- and print "zone-dir for $zone removed\n";
- }
- else {
- print "$master_dir/$zone: $!\n";
- }
-
- if (-e "$conf_dir/$zone") {
- unlink "$conf_dir/$zone"
- and print "configuration-file for $zone removed\n";
- }
- else {
- print "$conf_dir/$zone: $!\n";
- }
-}