mirror of
https://github.com/webmin/webmin.git
synced 2025-08-16 14:51:18 +00:00
More work on DNSSEC
This commit is contained in:
@ -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
|
||||
|
@ -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
46
bind8/resign.pl
Normal 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
21
bind8/resign_zone.cgi
Executable 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
43
bind8/save_dnssec.cgi
Normal 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("");
|
||||
|
Reference in New Issue
Block a user