Completed SSL monitor

This commit is contained in:
Jamie Cameron
2008-06-22 23:10:18 +00:00
parent 1ea2d1c983
commit ee4532f1f2
3 changed files with 100 additions and 32 deletions

View File

@@ -55,3 +55,4 @@ Email, SMS and SNMP messages sent by Webmin can be customized, with multiple mes
The process check monitor can now be limited to a particular user.
---- Changes since 1.420 ----
Allow saving of remote Webmin monitors when the remote host is down.
Added a new monitor type for checking the expiry and validity of SSL certificates in a local file or on any SSL website.

View File

@@ -460,10 +460,19 @@ dtmpls_err=Failed to delete templates
dtmpls_enone=None selected
dtmpls_eusers=$1 is in use by the following monitors : $2
sslcert_url=SSL URL to check certificate for
sslcert_src=Certificate location
sslcert_url=From HTTPS URL
sslcert_file=From file on server
sslcert_eurl=Missing, invalid or non-SSL URL
sslcert_efile=Missing or invalid certificate file
sslcert_ecert=Could not get certificate
sslcert_days=Days before expiry to fail
sslcert_when=When expired
sslcert_mismatch=Also detect hostname mismatch?
sslcert_edays=Missing or invalid number of days
sslcert_eopenssl=The <tt>openssl</tt> command is needed to check certificates
sslcert_estart=Not valid until $1
sslcert_eend=Not valid after $1
sslcert_left=Up - $1 days until expiry
sslcert_ematch=Hostname $1 does not match certificate $2
sslcert_emismatch=Certificate hostname checking can only be used when checking a URL

View File

@@ -4,54 +4,97 @@ sub get_sslcert_status
{
local $up = 0;
local $desc;
eval "use Net::SSLeay";
local $certfile;
local ($host, $port, $page, $ssl);
# Parse the URL and connect
local ($host, $port, $page, $ssl) = &parse_http_url($_[0]->{'url'});
if ($_[0]->{'url'}) {
# Parse the URL and connect
($host, $port, $page, $ssl) = &parse_http_url($_[0]->{'url'});
# Run the openssl command to connect
local $cmd = "openssl s_client -host ".quotemeta($host).
" -port ".quotemeta($port)." </dev/null 2>&1";
local $out = &backquote_with_timeout($cmd, 10);
if ($?) {
# Connection failed
return { 'up' => -1 };
}
# Run the openssl command to connect
local $cmd = "openssl s_client -host ".quotemeta($host).
" -port ".quotemeta($port)." </dev/null 2>&1";
local $out = &backquote_with_timeout($cmd, 10);
if ($?) {
# Connection failed
return { 'up' => -1 };
}
# Extract the cert part and save
local $temp = &transname();
if ($out =~ /(-----BEGIN CERTIFICATE-----\n(.*\n)+-----END CERTIFICATE-----\n)/) {
local $cert = $1;
print STDERR "cert=$cert\n";
&open_tempfile(CERT, ">$temp", 0, 1);
&print_tempfile(CERT, $cert);
&close_tempfile(CERT);
# Extract the cert part and save
$certfile = &transname();
if ($out =~ /(-----BEGIN CERTIFICATE-----\n(.*\n)+-----END CERTIFICATE-----\n)/) {
local $cert = $1;
&open_tempfile(CERT, ">$certfile", 0, 1);
&print_tempfile(CERT, $cert);
&close_tempfile(CERT);
}
else {
# No cert?
return { 'up' => 0,
'desc' => $text{'sslcert_ecert'} };
}
}
else {
# No cert?
return { 'up' => 0,
'desc' => $text{'sslcert_ecert'} };
# Cert is already in a file
$certfile = $_[0]->{'file'};
}
# Get end date with openssl x509 -in cert.pem -inform PEM -text -noout -enddate
local $info = &backquote_command("openssl x509 -in ".quotemeta($temp).
local $info = &backquote_command("openssl x509 -in ".quotemeta($certfile).
" -inform PEM -text -noout -enddate ".
" </dev/null 2>&1");
print STDERR "info=$info\n";
# Check dates
# XXX (before and after)
&foreign_require("mailboxes", "mailboxes-lib.pl");
local ($start, $end);
if ($info =~ /Not\s*Before\s*:\s*(.*)/i) {
$start = &mailboxes::parse_mail_date("$1");
}
if ($info =~ /Not\s+After\s*:\s*(.*)/i) {
$end = &mailboxes::parse_mail_date("$1");
}
local $now = time();
print STDERR "start=$start end=$end now=$now\n";
if ($start && $now < $start) {
# Too new?!
$desc = &text('sslcert_estart', &make_date($start));
}
elsif ($end && $now > $end-$_[0]->{'days'}*24*60*60) {
# Too old
$desc = &text('sslcert_eend', &make_date($end));
}
elsif ($_[0]->{'mismatch'} && $_[0]->{'url'} &&
$info =~ /Subject:.*CN=([a-z0-9\.\-\_\*]+)/i) {
# Check hostname
local $cn = $1;
local $match = $1;
$match =~ s/\*/\.\*/g; # Make perl RE
if ($host !~ /^$match$/i) {
$desc = &text('sslcert_ematch', "<tt>$host</tt>",
"<tt>$cn</tt>");
}
}
$up = 1;
if (!$desc) {
# All OK!
$desc = &text('sslcert_left', int(($end-$now)/(24*60*60)));
$up = 1;
}
return { 'up' => $up, 'desc' => $desc };
}
sub show_sslcert_dialog
{
# URK to check
print &ui_table_row($text{'sslcert_url'},
&ui_textbox("url", $_[0]->{'url'}, 50), 3);
# URL or file to check
print &ui_table_row($text{'sslcert_src'},
&ui_radio_table("src", $_[0]->{'file'} ? 1 : 0,
[ [ 0, $text{'sslcert_url'},
&ui_textbox("url", $_[0]->{'url'}, 50) ],
[ 1, $text{'sslcert_file'},
&ui_textbox("file", $_[0]->{'file'}, 50)." ".
&file_chooser_button("file") ] ]), 3);
# Days before expiry to warn
print &ui_table_row($text{'sslcert_days'},
@@ -64,9 +107,21 @@ print &ui_table_row($text{'sslcert_mismatch'},
sub parse_sslcert_dialog
{
# Parse URL
$in{'url'} =~ /^https:\/\/(\S+)$/ || &error($text{'sslcert_eurl'});
$_[0]->{'url'} = $in{'url'};
&has_command("openssl") || &error($text{'sslcert_eopenssl'});
if ($in{'src'} == 0) {
# Parse URL
$in{'url'} =~ /^https:\/\/(\S+)$/ || &error($text{'sslcert_eurl'});
$_[0]->{'url'} = $in{'url'};
delete($_[0]->{'file'});
}
else {
# Parse file
$in{'file'} =~ /^\// && -r $in{'file'} ||
&error($text{'sslcert_efile'});
$_[0]->{'file'} = $in{'file'};
delete($_[0]->{'url'});
}
# Parse number of days
if ($in{'days_def'}) {
@@ -79,5 +134,8 @@ else {
# Check hostname
$_[0]->{'mismatch'} = $in{'mismatch'};
if ($in{'mismatch'} && $in{'src'}) {
&error($text{'sslcert_emismatch'});
}
}