From 9cfed6df0f0cd449847bd5041afcee634faa57ad Mon Sep 17 00:00:00 2001 From: Peter Palfrader Date: Mon, 19 May 2008 15:57:00 +0200 Subject: [PATCH] [project @ peter@palfrader.org-20080519135700-ekglreng7ttedbqd] * Add dsa-check-statusfile. * Add weak-ssh-keys-check to be run from cron.daily. Depend on libberkeleydb-perl and dsa-ssh-weak-keys now. * Remove up /var/cache/dsa/nagios/weak-ssh-keys in postrm purge. --- dsa-nagios-nrpe-config/debian/changelog | 9 + dsa-nagios-nrpe-config/debian/control | 2 +- dsa-nagios-nrpe-config/debian/copyright | 10 + dsa-nagios-nrpe-config/debian/cron.daily | 3 + dsa-nagios-nrpe-config/debian/dirs | 2 + dsa-nagios-nrpe-config/debian/postrm | 23 ++ dsa-nagios-nrpe-config/debian/rules | 5 +- dsa-nagios-nrpe-config/dsa-check-statusfile | 81 ++++++ dsa-nagios-nrpe-config/weak-ssh-keys-check | 290 ++++++++++++++++++++ 9 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 dsa-nagios-nrpe-config/debian/cron.daily create mode 100644 dsa-nagios-nrpe-config/debian/postrm create mode 100755 dsa-nagios-nrpe-config/dsa-check-statusfile create mode 100755 dsa-nagios-nrpe-config/weak-ssh-keys-check diff --git a/dsa-nagios-nrpe-config/debian/changelog b/dsa-nagios-nrpe-config/debian/changelog index 6271668..8f21cff 100644 --- a/dsa-nagios-nrpe-config/debian/changelog +++ b/dsa-nagios-nrpe-config/debian/changelog @@ -1,3 +1,12 @@ +dsa-nagios-nrpe-config (41) unstable; urgency=low + + * Add dsa-check-statusfile. + * Add weak-ssh-keys-check to be run from cron.daily. Depend on + libberkeleydb-perl and dsa-ssh-weak-keys now. + * Remove up /var/cache/dsa/nagios/weak-ssh-keys in postrm purge. + + -- Peter Palfrader Mon, 19 May 2008 15:55:43 +0200 + dsa-nagios-nrpe-config (40) unstable; urgency=low * Teach dsa-check-hpacucli about rebuilding. diff --git a/dsa-nagios-nrpe-config/debian/control b/dsa-nagios-nrpe-config/debian/control index dad9749..14e8564 100644 --- a/dsa-nagios-nrpe-config/debian/control +++ b/dsa-nagios-nrpe-config/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.7.2 Package: dsa-nagios-nrpe-config Architecture: all -Depends: nagios-nrpe-server, nagios-plugins-basic, ${misc:Depends}, binutils +Depends: nagios-nrpe-server, nagios-plugins-basic, ${misc:Depends}, binutils, libberkeleydb-perl, dsa-ssh-weak-keys Suggests: hpacucli Description: debian.org nagios-nrpe-server configuration This package provides the debian.org configuration for the diff --git a/dsa-nagios-nrpe-config/debian/copyright b/dsa-nagios-nrpe-config/debian/copyright index 28c92c2..e956f83 100644 --- a/dsa-nagios-nrpe-config/debian/copyright +++ b/dsa-nagios-nrpe-config/debian/copyright @@ -32,3 +32,13 @@ dsa-check-running-kernel: dsa-check-hpacucli: Copyright: 2008 Peter Palfrader License: MIT + +######################################################################## +dsa-check-statusfile: + Copyright: 2008 Peter Palfrader + License: MIT + +######################################################################## +weak-ssh-keys-check: + Copyright: 2008 Florian Weimer, Alexander Wirt + License: ISC diff --git a/dsa-nagios-nrpe-config/debian/cron.daily b/dsa-nagios-nrpe-config/debian/cron.daily new file mode 100644 index 0000000..c37e41e --- /dev/null +++ b/dsa-nagios-nrpe-config/debian/cron.daily @@ -0,0 +1,3 @@ +#!/bin/sh + +[ -x /usr/share/dsa/weak-ssh-keys-check ] && /usr/share/dsa/weak-ssh-keys-check -s /var/cache/dsa/nagios/weak-ssh-keys diff --git a/dsa-nagios-nrpe-config/debian/dirs b/dsa-nagios-nrpe-config/debian/dirs index 775d720..2e42e42 100644 --- a/dsa-nagios-nrpe-config/debian/dirs +++ b/dsa-nagios-nrpe-config/debian/dirs @@ -1,2 +1,4 @@ etc/nagios usr/lib/nagios/plugins +usr/share/dsa +var/cache/dsa/nagios diff --git a/dsa-nagios-nrpe-config/debian/postrm b/dsa-nagios-nrpe-config/debian/postrm new file mode 100644 index 0000000..272a7cc --- /dev/null +++ b/dsa-nagios-nrpe-config/debian/postrm @@ -0,0 +1,23 @@ +#!/bin/sh +# postrm script for dsa-ssh-weak-keys +# +# see: dh_installdeb(1) + +set -e + +case "$1" in + purge) + rm -f /var/cache/dsa/nagios/weak-ssh-keys + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/dsa-nagios-nrpe-config/debian/rules b/dsa-nagios-nrpe-config/debian/rules index 691a753..cad941f 100755 --- a/dsa-nagios-nrpe-config/debian/rules +++ b/dsa-nagios-nrpe-config/debian/rules @@ -24,12 +24,15 @@ install: install -m 755 dsa-check-dabackup $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins install -m 755 dsa-check-udldap-freshness $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins + install -m 755 weak-ssh-keys-check $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/share/dsa/weak-ssh-keys-check + binary-indep: install dh_testdir dh_testroot - dh_installchangelogs + dh_installchangelogs dh_installdocs + dh_installcron dh_compress dh_fixperms dh_installdeb diff --git a/dsa-nagios-nrpe-config/dsa-check-statusfile b/dsa-nagios-nrpe-config/dsa-check-statusfile new file mode 100755 index 0000000..a69d977 --- /dev/null +++ b/dsa-nagios-nrpe-config/dsa-check-statusfile @@ -0,0 +1,81 @@ +#!/usr/bin/ruby + +# Relay the status of a check that was previously run and which stored +# its result in a file to nagios. +# +# Copyright 2008 Peter Palfrader +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +require 'resolv' +require 'optparse' + +NAGIOS_STATUS = { "OK" => 0, "WARNING" => 1, "CRITICAL" => 2, "UNKNOWN" => 3 } +UNITS_TO_SECONDS = { 's' => 1, 'm' => 60, 'h' => 60*60, 'd' => 24*60*60 } + +def show_help(parser, code=0, io=STDOUT) + program_name = File.basename($0, '.*') + io.puts "Usage: #{program_name} [options] " + io.puts parser.summarize + exit(code) +end + +max_age = "26h" +ARGV.options do |opts| + opts.on_tail("-h", "--help" , "Display this help screen") { show_help(opts) } + opts.on("-a", "--age=AGE" , String, "maximum age, in seconds (or use Nm, Nh or Nd) - default is 26h") { |max_age| } + opts.parse! +end +show_help(ARGV.options, 1, STDERR) if ARGV.length != 1 + +statusfile = ARGV.shift + +# find out what the max age is that we accept +unless (m = /^([0-9]+)([smhd])?$/.match max_age) + STDERR.puts "Invalid age #{age}." + show_help(ARGV.options, 1, STDERR) if ARGV.length != 1 +end +max_age = m[1].to_i * UNITS_TO_SECONDS[m[2] ? m[2] : 's'] + +# let's see if it exists +unless File.exists? statusfile + puts "UNKNOWN: #{statusfile} does not exist." + exit NAGIOS_STATUS['UNKNOWN'] +end + + +mtime = File.stat(statusfile).mtime +if mtime + max_age < Time.now + puts "WARNING: #{statusfile} is old: #{mtime}" + exit NAGIOS_STATUS['WARNING'] +end + +status = File.new(statusfile) +returnvalue = status.readline.chomp + +unless NAGIOS_STATUS.has_key? returnvalue + puts "UNKNOWN: #{statusfile} has invalid return value: #{returnvalue}" + exit NAGIOS_STATUS['UNKNOWN'] +end + +status.readlines.each do |line| + print line +end +exit NAGIOS_STATUS[returnvalue] diff --git a/dsa-nagios-nrpe-config/weak-ssh-keys-check b/dsa-nagios-nrpe-config/weak-ssh-keys-check new file mode 100755 index 0000000..3e07842 --- /dev/null +++ b/dsa-nagios-nrpe-config/weak-ssh-keys-check @@ -0,0 +1,290 @@ +#!/usr/bin/perl + +# This cheak is based on code from the Debian/OpenSSL Weak Key Detector +# written by Florian Weimer . +# The code has been modified and enhanced by Alexander Wirt +# to use it as a nagios check. +# +# Copyright (c) 2008, Florian Weimer for the original +# Debian/OpenSSL Weak Key Detector +# (http://security.debian.org/project/extra/dowkd/dowkd.pl.gz) +# +# Copyright (c) 2008, Alexander Wirt for check_weakkeys +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +=pod + +=head1 NAME + +B - checks system for weak ssh keys + +=cut + +=head1 SYNOPSIS + +B [options] + +=cut + +=head1 DESCRIPTION + +B checks for all users if there id_rsa, id_dsa or +authorized_key files if they contain weak ssh keys created by a Debian with a +broken libssl (see DSA-1571 for more informations). Optionally +can spit out a warning of there are any DSA keys left in key or authorized_key +files. To work it needs a database of precomputed hashes of known weak keys. +This file is expected as an bdb database with the hash (like +03:a2:f0:46:7f:13:9f:5f:96:71:a9:b8:a0:1c:01:05) as key. See for +such a database generator. outputs his data to STDOUT or to a +file. It meaned to be picked up by an nagios check like B +from Peter Palfrader. + +=cut + +=head1 OPTIONS + +=over 4 + +=item B<-h, --help> + +Prints out a brief help + +=item B<-s, --statusfile> "statusfile" + +Use 'F' instead of 'F'. + +=item B<-f, --fprdb> "database" (default: /var/lib/dsa/ssh-weak-keys.db) + +Use 'F' instead of 'F' +as fingerprint database. + +=item B<-n, --dsa_nowarn> + +Don't warn for DSA keys + +=back + +=cut + +use strict; +use warnings; + +use File::Temp; +use BerkeleyDB; +use Pod::Usage; +use Getopt::Long; +use IPC::Open3; + +my $fprdb_fname = "/var/lib/dsa/ssh-weak-keys.db" ; +my ($outfile, $help); +my $dsa_nowarn = 0; + +GetOptions( 'help|h' => \$help, #Help function + 'statusfile|s=s' => \$outfile, + 'fprdb|f=s' => \$fprdb_fname, + 'n|dsa_nowarn' => \$dsa_nowarn, +); + +pod2usage(1) if $help; + +my $fh; +if ($outfile) { + open ($fh, '>', $outfile) + or die "Could not open statusfile '$outfile' for writing: $!"; +} else { + $fh = *STDOUT; +} + +my %fpr_hash; +tie %fpr_hash, 'BerkeleyDB::Btree', + -Filename => $fprdb_fname, + -Flags => DB_RDONLY + or die "Cannot open fingerprint db $fprdb_fname: $! $BerkeleyDB::Error\n"; + + +my ($weak_keys,$checked_keys) = 0; +my $dsa_keys = 0; +my $text = ''; +my %key_sizes; + + + +&from_user_all; +&from_ssh_host(qw(localhost)); + +my $status="OK"; +if ($weak_keys) { + $status = "CRITICAL"; +} elsif ($dsa_keys && ! $dsa_nowarn) { + $status = "WARNING"; +} + +print $fh "$status\n"; +print $fh "Checked $checked_keys keys - $weak_keys weak - $dsa_keys dsa keys\n"; +print $fh "Sizes: "; +foreach my $size (sort(keys(%key_sizes))) { + print $fh "$size:$key_sizes{$size} "; +} + +print $fh "\n"; +print $fh "$text" if $text; + + + +sub safe_backtick (@) { + my @args = @_; + + my ($wtr, $fh, $err); + + open3($wtr,$fh,$err, @args) + or die "error: failed to spawn $args[0]: $!\n"; + my @result; + if (wantarray) { + @result = <$fh>; + } else { + local $/; + @result = scalar(<$fh>); + } + close $fh; + $? == 0 or return undef; + if (wantarray) { + return @result; + } else { + return $result[0]; + } +} + +sub ssh_fprint_file ($) { + my $name = shift; + my $data = safe_backtick qw/ssh-keygen -l -f/, $name; + defined $data or return (); + my @data = $data =~ /^(\d+) ([0-9a-f]{2}(?::[0-9a-f]{2}){15})/; + return @data if @data == 2; + return (); +} + +sub ssh_fprint_check ($$$) { + my ($name, $length, $hash) = @_; + if (exists $key_sizes{$length}) { + $key_sizes{$length}++; + } else { + $key_sizes{$length}=1; + } + $checked_keys++; + if (exists $fpr_hash{$hash}) { + $weak_keys++; + $text .= "$name weak ($hash)\n"; + } +} + + +sub from_ssh_key_file ($) { + my $name = shift; + if (open (my $FH, '<', $name)) { + my $key = <$FH>; + if ($key =~ m/^ssh-dss/) { + $dsa_keys++; + $text .= "$name is a DSA key\n"; + } + } else { + $text .= "Could not open $name: $!"; + } + my ($length, $hash) = ssh_fprint_file $name; + if ($length && $hash) { + ssh_fprint_check "$name:1", $length, $hash; + } else { + $text .= "$name:1: warning: failed to parse SSH key file\n"; + } +} + +sub clear_tmp ($) { + my $tmp = shift; + seek $tmp, 0, 0 or die "seek: $!"; + truncate $tmp, 0 or die "truncate: $!"; +} + +sub from_ssh_auth_file ($) { + my $name = shift; + my $auth; + unless (open $auth, '<', $name) { + warn "$name:0: error: open failed: $!\n"; + return; + } + my $tmp = new File::Temp; + while (my $line = <$auth>) { + chomp $line; + my $lineno = $.; + clear_tmp $tmp; + next if $line =~ m/^#/; # ignore comments + if ($line =~ m/^ssh-dss/) { + $dsa_keys++; + $text .= "$name:$lineno is a DSA key\n"; + } + print $tmp "$line\n" or die "print: $!"; + $tmp->flush; + my ($length, $hash) = ssh_fprint_file "$tmp"; + if ($length && $hash) { + ssh_fprint_check "$name:$lineno", $length, $hash; + } else { + $text .= "$name:$lineno: warning: unparsable line\n"; + } + } +} + +sub from_ssh_host (@) { + my @names = @_; + my @lines; + push @lines, safe_backtick qw|ssh-keyscan -t rsa|, @names; + push @lines, safe_backtick qw|ssh-keyscan -t dsa|, @names; + + my $tmp = new File::Temp; + for my $line (@lines) { + next if $line =~ /^#/; + my ($host, $data) = $line =~ /^(\S+) (.*)$/; + clear_tmp $tmp; + print $tmp "$data\n" or die "print: $!"; + $tmp->flush; + my ($length, $hash) = ssh_fprint_file "$tmp"; + if ($length && $hash) { + ssh_fprint_check "$host", $length, $hash; + } else { + $text .= "$host: warning: unparsable line\n"; + } + } +} + +sub from_user ($) { + my $user = shift; + my ($name,$passwd,$uid,$gid, + $quota,$comment,$gcos,$dir,$shell,$expire) = getpwnam($user); + my $file = "$dir/.ssh/authorized_keys"; + from_ssh_auth_file $file if -r $file; + $file = "$dir/.ssh/authorized_keys2"; + from_ssh_auth_file $file if -r $file; + $file = "$dir/.ssh/id_rsa.pub"; + from_ssh_key_file $file if -r $file; + $file = "$dir/.ssh/id_dsa.pub"; + from_ssh_key_file $file if -r $file; +} + +sub from_user_all () { + setpwent; + while (my $name = getpwent) { + from_user $name; + } + endpwent; +} + + -- 2.20.1