More work on DNSSEC

This commit is contained in:
Jamie Cameron
2008-12-03 00:30:55 +00:00
parent 6cdf7b274f
commit 2a04f7ad27
5 changed files with 195 additions and 1 deletions

View File

@ -27,6 +27,8 @@ else {
$bind_version = &get_bind_version();
}
$dnssec_cron_cmd = "$module_config_directory/renew.pl";
# get_bind_version()
# Returns the BIND verison number, or undef if unknown
sub get_bind_version
@ -2627,11 +2629,20 @@ if (!$pid) {
exit(1);
}
# Work out zone key size
local $zonesize;
if ($single) {
(undef, $zonesize) = &compute_dnssec_key_size($alg, 1);
}
else {
$zonesize = $size;
}
# Create the zone key
local $dom = $z->{'members'} ? $z->{'values'}->[0] : $z->{'name'};
local $out = &backquote_logged(
"cd ".quotemeta($fn)." && ".
"$config{'keygen'} -a ".quotemeta($alg)." -b ".quotemeta($size).
"$config{'keygen'} -a ".quotemeta($alg)." -b ".quotemeta($zonesize).
" -n ZONE $dom 2>&1");
if ($?) {
kill('KILL', $pid);
@ -2681,6 +2692,73 @@ foreach my $key (@keys) {
return undef;
}
# resign_dnssec_key(&zone|&zone-name)
# Re-generate the zone key, and re-sign everything. Returns undef on success or
# an error message on failure.
sub resign_dnssec_key
{
local ($z) = @_;
local $fn = &get_zone_file($z);
$fn || return "Could not work out records file!";
local $dir = $fn;
$dir =~ s/\/[^\/]+$//;
local $dom = $z->{'members'} ? $z->{'values'}->[0] : $z->{'name'};
# Get the old zone key record
local @recs = &read_zone_file($fn, $dom);
locla $zonerec;
foreach my $r (@recs) {
if ($r->{'type'} eq 'DNSKEY' && $r->{'values'}->[0] % 2 == 0) {
$zonerec = $r;
}
}
$zonerec || return "Could not find DNSSEC zone key record";
local @keys = &get_dnssec_keys($z);
@keys == 2 || return "Expected to find 2 keys, but found ".scalar(@keys);
local ($zonekey) = grep { !$_->{'ksk'} } @keys;
$zonekey || return "Could not find DNSSEC zone key";
# Fork a background job to do lots of IO, to generate entropy
local $pid = fork();
if (!$pid) {
exec("find / -type f >/dev/null 2>&1");
exit(1);
}
# Work out zone key size
local $zonesize;
(undef, $zonesize) = &compute_dnssec_key_size($alg, 1);
local $alg = $zonekey->{'algorithm'};
# Generate a new zone key
local $out = &backquote_logged(
"cd ".quotemeta($dir)." && ".
"$config{'keygen'} -a ".quotemeta($alg)." -b ".quotemeta($zonesize).
" -n ZONE $dom 2>&1");
kill('KILL', $pid);
if ($?) {
return "Failed to generate new zone key : $out";
}
# Delete the old key file
&unlink_file($zonekey->{'privatefile'});
&unlink_file($zonekey->{'publicfile'});
# Update the zone file with the new key
@keys = &get_dnssec_keys($z);
local ($newzonekey) = grep { !$_->{'ksk'} } @keys;
$newzonekey || return "Could not find new DNSSEC zone key";
&modify_record($fn, $dom.".", undef, "IN", "DNSKEY",
join(" ", @{$newzonekey->{'values'}}));
&bump_soa_record($fn, \@recs);
# Re-sign everything
local $err = &sign_dnssec_zone($z);
return "Re-signing failed : $err" if ($err);
return undef;
}
# delete_dnssec_key(&zone|&zone-name)
# Deletes the key for a zone, and all DNSSEC records
sub delete_dnssec_key

View File

@ -753,6 +753,7 @@ log_thaw=Un-froze zone $1
log_zonekeyon=Enabled DNSSEC for zone $1
log_zonekeyoff=Disabled DNSSEC for zone $1
log_sign=Updated DNSSEC signatures for zone $1
log_resign=Re-signed DNSSEC key for zone $1
convert_err=Failed to convert zone
convert_efile=A records file must be specified before a slave zone can be converted to a master.
@ -999,6 +1000,8 @@ dnssec_enabled=Automatic key re-signing enabled?
dnssec_period=Period between re-signs?
dnssec_days=days
dnssec_desc=Zones signed with DNSSEC typically have two keys - a zone key which must be re-generated and signed regularly, and a key signing key which remains constant. This page allows you to configure Webmin to perform this re-signing automatically.
dnssec_err=Failed to save DNSSEC key re-signing
dnssec_eperiod=Missing or invalid number of days between re-signs
zonekey_title=Setup DNSSEC Key
zonekey_desc=This zone does not have a DNSSEC signing key yet. You can use this form to have Webmin create one, so that clients resolving this zone are protected against DNS spoofing attacks.
@ -1031,3 +1034,6 @@ zonekey_signdesc=Immediately re-sign this zone, so that any changes to records m
sign_err=Failed to sign zone
sign_emsg=DNSSEC signing after records change failed : $1
resign_err=Failed to re-sign zone

46
bind8/resign.pl Normal file
View File

@ -0,0 +1,46 @@
#!/usr/local/bin/perl
# Called from cron to re-sign all zones that are too old
$no_acl_check++;
require './bind8-lib.pl';
if ($ARGV[0] eq "--debug") {
$debug = 1;
}
if (!$config{'dnssec_period'}) {
print STDERR "Maximum age not set\n" if ($debug);
exit(0);
}
@zones = &list_zone_names();
$errcount = 0;
foreach $z (@zones) {
# Get the key
next if ($z->{'type'} ne 'master');
print STDERR "Considering zone $z->{'name'}\n" if ($debug);
@keys = &get_dnssec_keys($z);
print STDERR " Key count ",scalar(@keys),"\n" if ($debug);
next if (@keys != 2);
($zonekey) = grep { !$_->{'ksk'} } @keys;
next if (!$zonekey);
print STDERR " Zone key in ",$zonekey->{'privatefile'},"\n"
if ($debug);
# Check if old enough
@st = stat($key->{'privatefile'});
$old = (time() - $st[9]) / (24*60*60)
print STDERR " Age in days $old\n" if ($debug);
if ($old > $config{'dnssec_period'}) {
# Too old .. signing
$err = &resign_dnssec_key($z);
if ($err) {
print STDERR " Re-signing failed : $err\n";
$errcount++;
}
elsif ($debug) {
print STDERR " Re-signed OK\n";
}
}
}
exit($errcount);

21
bind8/resign_zone.cgi Executable file
View File

@ -0,0 +1,21 @@
#!/usr/local/bin/perl
# Re-generate the zone key and re-sign a zone
require './bind8-lib.pl';
&error_setup($text{'resign_err'});
&ReadParse();
$zone = &get_zone_name($in{'index'}, $in{'view'});
$dom = $zone->{'name'};
&can_edit_zone($zone) ||
&error($text{'master_ecannot'});
# Do the signing
&lock_file(&make_chroot(&absolute_path($zone->{'file'})));
$err = &resign_dnssec_zone($zone);
&error($err) if ($err);
&unlock_file(&make_chroot(&absolute_path($zone->{'file'})));
# Return to master page
&webmin_log("resign", undef, $dom);
&redirect("edit_master.cgi?index=$in{'index'}&view=$in{'view'}");

43
bind8/save_dnssec.cgi Normal file
View File

@ -0,0 +1,43 @@
#!/usr/local/bin/perl
# Turn on or off the DNSSEC key rotation cron job
require './bind8-lib.pl';
&foreign_require("cron", "cron-lib.pl");
&ReadParse();
&error_setup($text{'dnssec_err'});
$access{'defaults'} || &error($text{'dnssec_ecannot'});
$in{'period'} =~ /^[1-9]\d*$/ || &error($text{'dnssec_eperiod'});
# Create or delete the cron job
$job = &get_dnssec_cron_job();
if ($job && !$in{'enabled'}) {
# Turn off cron job
&lock_file(&cron::cron_file($job));
&cron::delete_cron_job($job);
&unlock_file(&cron::cron_file($job));
}
elsif (!$job && $in{'enabled'}) {
# Turn on cron job
$job = { 'user' => 'root',
'active' => 1,
'command' => $dnssec_cron_cmd,
'mins' => int(rand()*60),
'hours' => int(rand()*24),
'days' => '*',
'months' => '*',
'weekdays' => '*' };
&lock_file(&cron::cron_file($job));
&cron::create_cron_job($job);
&unlock_file(&cron::cron_file($job));
}
&cron::create_wrapper($dnssec_cron_cmd, $module_name, "renew.pl");
&lock_file($module_config_file);
$config{'dnssec_period'} = $in{'period'};
&save_module_config();
&unlock_file($module_config_file);
&webmin_log("dnssec");
&redirect("");