check_tlsa
changeset 6 10fc0d468fec
parent 5 3ae3e79ee936
child 7 a2ce47570096
equal deleted inserted replaced
5:3ae3e79ee936 6:10fc0d468fec
     1 #! /usr/bin/perl
     1 #! /usr/bin/perl
     2 #dig TLSA _25._tcp.ssl.schlittermann.de +dnssec +m
       
     3 #dig TLSA _25._tcp.hh.schlittermann.de
       
     4 # http://search.cpan.org/~nierlein/Monitoring-Plugin-0.39/lib/Monitoring/Plugin.pm
       
     5 # http://perldoc.perl.org/File/Basename.html
       
     6 # needs debian packet: libmonitoring-plugin-perl
       
     7 #TLSA Record generieren
       
     8 # openssl x509 -in <servername>.crt -outform DER | openssl sha256
       
     9 # neben sha256 gibt's noch sha1 sha224 sha256 sha384 sha512
       
    10 # sowie md2 md5 rmd160 (wobei ich diese nicht in betracht ziehe)
       
    11 # ssl certifikat von einem remote server anfordern
       
    12 # openssl s_client -showcerts -connect ssl.schlittermann.de:443 < /dev/null
       
    13 # https://github.com/monitoring-plugins
       
    14 # https://github.com/monitoring-plugins/monitoring-plugin-perl/blob/master/t/check_stuff.pl
       
    15 
       
    16 #openssl s_client -starttls smtp -connect ssl.schlittermann.de:25 | openssl x509  -pubkey | openssl rsa -pubin -inform PEM -outform DER | openssl sha256
       
    17 #openssl s_client -starttls smtp -connect ssl.schlittermann.de:25 | openssl x509  -outform DER | openssl sha256
       
    18 
     2 
    19 use strict;
     3 use strict;
    20 use warnings;
     4 use warnings;
    21 use feature qw(switch say);
     5 use feature qw(switch say);
    22 use if $^V >= v5.020 => (experimental => qw(smartmatch));
     6 use if $^V >= v5.020 => (experimental => qw(smartmatch));
    23 use experimental qw(smartmatch);
     7 use experimental qw(smartmatch);
    24 use Monitoring::Plugin;
     8 use Monitoring::Plugin;
    25 use File::Basename;
     9 use File::Basename;
    26 
    10 
    27 #devel
    11 
    28 use Data::Dumper;
    12 my $ME      = basename $0;
    29 
       
    30 my $VERSION = '0.1';
    13 my $VERSION = '0.1';
    31 my $blurb   = 'This Plugin is intendet to check TLSA Record';
    14 my $blurb   = 'This Plugin is intendet to check validity of TLSA Record';
    32 my $url     = 'https://schlittermann.de';
    15 my $url     = 'http://www.schlittermann.de';
    33 my $author  = 'Heike Yvonne Pesch';
    16 my $author  = 'Heike Yvonne Pesch';
    34 my $email   = '<pesch@schlittermann.de>';
    17 my $email   = '<pesch@schlittermann.de>';
       
    18 my $usage   = 'Usage: %s [ -v|--verbose ] [-H <host>] [-t <timeout>] '
       
    19             . '[ -c|--critical=<critical threshold> ] '
       
    20             . '[ -w|--warning=<warning threshold> ] '
       
    21             . '[ -p|--port=<portnumber> ] '
       
    22             . '[ -q|--queryserver=<DNS-Server-IP> ] ';
    35 my $extra   = <<_;
    23 my $extra   = <<_;
       
    24 
       
    25 NOTICE
       
    26 If you want to use a Hostlist, you have to put entrys like this:
       
    27 
       
    28 host
       
    29 host:port
       
    30 
       
    31 
       
    32 EXAMPLES
       
    33 $ME -H ssl.schlittermann.de 
       
    34 $ME -H hh.schlittermann.de -p25
       
    35 $ME -H hh.schlittermann.de:25
       
    36 $ME -f hostlist.txt
       
    37 
    36 Author: $author $email
    38 Author: $author $email
    37 For more information visit $url
    39 For more information visit $url
    38 _
    40 _
    39 my $license = 'GPLv3';
       
    40 my $usage =
       
    41     'Usage: %s [ -v|--verbose ] [-H <host>] [-t <timeout>] '
       
    42   . '[ -c|--critical=<critical threshold> ] '
       
    43   . '[ -w|--warning=<warning threshold> ] '
       
    44   . '[ -p|--port=<portnumber> ] '
       
    45   . '[ -q|--queryserver=<DNS-Server-IP> ] ';
       
    46 
    41 
    47 my $check_tlsa = Monitoring::Plugin->new(
    42 my $check_tlsa = Monitoring::Plugin->new(
    48     usage   => $usage,
    43   usage   => $usage,
    49     version => $VERSION,
    44   version => $VERSION,
    50     blurb   => $blurb,
    45   blurb   => $blurb,
    51     extra   => $extra,
    46   extra   => $extra,
    52     url     => $url,
    47   url     => $url,
    53     license => $license,
    48   plugin  => $ME,
    54     plugin  => basename $0,
    49   timeout => 120,
    55     timeout => 60,
    50 );
    56 );
    51 
    57 
    52 $check_tlsa->add_arg(
    58 $check_tlsa->add_arg(
    53   spec     => 'host|H=s',
    59     spec     => 'host|H=s',
    54   help     => q|Host/Domain to check|,
    60     help     => q|Host/Domain to check|,
    55   required => 0,
    61     required => 0,
    56 );
    62 );
    57 
    63 
    58 $check_tlsa->add_arg(
    64 $check_tlsa->add_arg(
    59   spec     => 'hostlist|f=s',
    65     spec     => 'hostlist|f=s',
    60   help     => q|Host/Domainlist in file to check|,
    66     help     => q|Host/Domainlist in file to check|,
    61   required => 0,
    67     required => 0,
    62 );
    68 );
    63 
    69 
    64 $check_tlsa->add_arg(
    70 $check_tlsa->add_arg(
    65   spec     => 'expiry|e',
    71     spec     => 'expiry|e',
    66   help     => q|check expiry of Certificate|,
    72     help     => q|check expiry of Certificate|,
    67   required => 0,
    73     required => 0,
    68 );
    74 );
    69 
    75 
    70 $check_tlsa->add_arg(
    76 $check_tlsa->add_arg(
    71   spec     => 'port|p=i',
    77     spec     => 'port|p=i',
    72   help     => q|Port of Domain to check the TLSA (default: 443)|,
    78     help     => q|Port of Domain to check the TLSA (default: 443)|,
    73   required => 0,
    79     required => 0,
    74   default  => 443,
    80     default  => 443,
    75 );
    81 );
    76 
    82 
    77 $check_tlsa->add_arg(
    83 $check_tlsa->add_arg(
    78   spec     => 'queryserver|q=s',
    84     spec => 'queryserver|q=s',
    79   required => 0,
    85     help =>
    80   help     =>
    86       q|DNS Server to ask to check the TLSA (default: defined in resolve.conf)|,
    81   q|DNS Server to ask to check the TLSA (default: defined in resolve.conf)|,
    87     required => 0,
    82 
    88 
    83 );
    89     #default => '8.8.8.8',
    84 
    90 );
    85 $check_tlsa->add_arg(
    91 
    86   spec     => 'protocol|P=s',
    92 $check_tlsa->add_arg(
    87   help     => q|Protocol to ask to check the TLSA record of domain (default: tcp)|,
    93     spec     => 'protocol|P=s',
    88   required => 0,
    94     help     => q|DNS Server to ask to check the TLSA (default: tcp)|,
    89   default  => 'tcp',
    95     required => 0,
       
    96     default  => 'tcp',
       
    97 );
    90 );
    98 
    91 
    99 $check_tlsa->getopts;
    92 $check_tlsa->getopts;
   100 
    93 
   101 my $domain     = $check_tlsa->opts->host;
    94 my $domain     = $check_tlsa->opts->host;
   102 my $domainlist = $check_tlsa->opts->hostlist;
    95 my $domainlist = $check_tlsa->opts->hostlist;
   103 my $expiry     = $check_tlsa->opts->expiry;
    96 my $expiry     = $check_tlsa->opts->expiry;
   104 
    97 
       
    98 
   105 if (!$domain && !$domainlist) {
    99 if (!$domain && !$domainlist) {
   106     my $script = basename $0;
   100     my $script = basename $0;
   107     my $excuse = "Please set -H <domain> or -f <domainlist>\n"
   101     my $excuse = "Please set -H <domain> or -f <domainlist>\n"
   108       . "For all options try $script --help";
   102     . "For all options try $script --help";
   109 
   103 
   110     say $excuse;
   104     say $excuse;
   111     exit 1;
   105     exit 1;
   112 }
   106 }
   113 
   107 
   114 my $port;
   108 my $port;
   115 my $cert;
   109 my $cert;
   116 my $check_date;
   110 my $check_date;
   117 
   111 my $pattern = '^(?<domain>\S*\.[a-z]{2,4}?):{0,1}(?<port>[0-9]*$)';
   118 if (defined $domainlist && -e $domainlist) {
   112 
   119     print get_domains();
   113 # @TODO find better way
   120 }
   114 # nearly the same check is defined in get_domains
   121 else { print check_tlsa(); }
   115 if ( defined $domain && $domain =~ /$pattern/) {
       
   116   $domain = $+{domain};
       
   117   $port   = $+{port};
       
   118 }
       
   119 
       
   120 if ( defined $domainlist && $domainlist ne '' && -e $domainlist) {
       
   121     say get_domains();
       
   122 }
       
   123 else { say check_tlsa(); }
   122 
   124 
   123 sub check_tlsa {
   125 sub check_tlsa {
   124     my $protocol = $check_tlsa->opts->protocol;
   126     my $protocol = $check_tlsa->opts->protocol;
   125 
   127 
   126     $port = $check_tlsa->opts->port unless $port;
   128     $port = $check_tlsa->opts->port unless $port;
   127 
   129 
   128     if ("$port" eq '25') {
   130     if ("$port" eq '25') {
   129         $cert = "openssl s_client -starttls smtp -connect $domain:$port "
   131         $cert = "openssl s_client -starttls smtp -connect $domain:$port "
   130           . '< /dev/null 2>/dev/null';
   132         . '< /dev/null 2>/dev/null';
   131     }
   133     }
   132     else {
   134     else {
   133         #$port           = $check_tlsa->opts->port;
       
   134         $cert = "openssl s_client -connect $domain:$port "
   135         $cert = "openssl s_client -connect $domain:$port "
   135           . '< /dev/null 2>/dev/null';
   136         . '< /dev/null 2>/dev/null';
   136     }
   137     }
   137 
   138 
   138     my $digquery  = "dig TLSA _$port._$protocol.$domain +short";
   139     my $digquery        = "dig TLSA _$port._$protocol.$domain +short";
   139     my $diganswer = qx($digquery);
   140     my $diganswer       = qx($digquery);
   140     my $dig       = substr($diganswer, 6,);
       
   141     $dig =~ s/(\S*)\s+(\S*)$/$1$2/;
       
   142     my $tlsa_usage      = substr($diganswer, 0, 1);
   141     my $tlsa_usage      = substr($diganswer, 0, 1);
   143     my $tlsa_selector   = substr($diganswer, 2, 1);
   142     my $tlsa_selector   = substr($diganswer, 2, 1);
   144     my $tlsa_match_type = substr($diganswer, 4, 1);
   143     my $tlsa_match_type = substr($diganswer, 4, 1);
       
   144     my $dig_tlsa        = substr($diganswer, 6,);
       
   145     my $valid_date      = '';
   145     my $hashit;
   146     my $hashit;
       
   147 
       
   148     $dig_tlsa =~ s/(\S*)\s+(\S*)$/$1$2/;
   146 
   149 
   147     for ($tlsa_match_type) {
   150     for ($tlsa_match_type) {
   148         when ('0') { die 'certs will be compared directly' }
   151         when ('0') { die 'certs will be compared directly' }
   149         when ('1') { $hashit = 'sha256' }
   152         when ('1') { $hashit = 'sha256' }
   150         when ('2') { $hashit = 'sha512' }
   153         when ('2') { $hashit = 'sha512' }
   151         default { $hashit = 'sha256' }
   154         default { $hashit = 'sha256' }
   152     }
   155     }
   153 
   156 
   154     my $gentlsa =
   157     my $gentlsa = 'openssl x509  -pubkey | '
   155         'openssl x509  -pubkey | '
   158        . 'openssl rsa -pubin -inform PEM -outform DER 2>/dev/null| '
   156       . 'openssl rsa -pubin -inform PEM -outform DER 2>/dev/null| '
   159        . "openssl $hashit";
   157       . "openssl $hashit";
       
   158     my $certtlsa = "$cert | $gentlsa";
   160     my $certtlsa = "$cert | $gentlsa";
   159 
   161 
   160     $check_date = 'openssl x509 -noout -startdate -enddate';
   162     $check_date = 'openssl x509 -noout -startdate -enddate';
   161     $check_date = "$cert|$check_date";
   163     $check_date = "$cert|$check_date";
   162 
   164 
   165     my $tlsa_record = qx($certtlsa) or die "nothing found!\n";
   167     my $tlsa_record = qx($certtlsa) or die "nothing found!\n";
   166     $tlsa_record =~ s/^.*= (.*$)/$1/gi;
   168     $tlsa_record =~ s/^.*= (.*$)/$1/gi;
   167     $tlsa_record = uc($tlsa_record);
   169     $tlsa_record = uc($tlsa_record);
   168 
   170 
   169     if (defined $expiry) {
   171     if (defined $expiry) {
   170         print check_cert_expiry();
   172         $valid_date = check_cert_expiry();
   171     }
   173     }
   172 
   174 
   173     if ("$tlsa_record" eq "$dig") {
   175     if ($valid_date ne '') {
   174 
   176       $valid_date = "\n$valid_date";
   175         #$return = "TLSA record is $tlsa_record and valid";
   177     }
   176         #funktioniert nich nicht optimal mit  hostliste
   178 
   177         $return = $check_tlsa->plugin_exit(OK, "$domain: TLSA record is valid")
   179     if ("$tlsa_record" eq "$dig_tlsa") {
   178           . "$domain: TLSA record is valid\n";
   180 
       
   181       #this way the script exit when file is given :(
       
   182       #$return = $check_tlsa->plugin_exit(OK, "$domain: TLSA record is valid")
       
   183       #  . "$domain: TLSA record is valid";
       
   184 
       
   185       #this way it's behaves like I want it to
       
   186       $return = "OK, $domain: TLSA record is valid $valid_date";
   179     }
   187     }
   180     else {
   188     else {
   181         $check_tlsa->plugin_exit(CRITICAL, "$domain: TLSA record NOT valid");
   189       #$check_tlsa->plugin_exit(CRITICAL, "$domain: TLSA record NOT valid");
   182     }
   190       $return = "CRITICAL, $domain: TLSA record is NOT valid";
   183     return $return;
   191     }
   184 
   192     say $return;
   185     #return $cert;
       
   186 }
   193 }
   187 
   194 
   188 sub get_domains {
   195 sub get_domains {
   189     open(my $filehandle, '<', $domainlist);
   196     open(my $filehandle, '<', $domainlist);
   190 
   197 
   191     my $pattern = '^(?<domain>\S*\.[a-z]{2,4}?):{0,1}(?<port>[0-9]*$)';
   198     my $pattern = '^(?<domain>\S*\.[a-z]{2,4}?):{0,1}(?<port>[0-9]*$)';
   192     my %domain2check;
   199     my %domain2check;
   193     while (<$filehandle>) {
   200     while (<$filehandle>) {
   194         if (/$pattern/ig) {
   201         if (/$pattern/ig) {
   195             $domain = $+{domain};
   202             $domain = $+{domain};
   196             $port   = $+{port};
   203 
   197 
   204             if ("$+{port}" =~ /^\s*$/) { $port = '443'; }
   198            #print "nunu,file ok",LF,"port: $+{port}",LF,"domain: $+{domain}",LF;
   205             else { $port   = $+{port}; }
   199             $domain2check{$domain} = $port;
   206             $domain2check{$domain} = $port;
   200 
   207 
   201             #print check_tlsa();
   208             check_tlsa($domain, $port);
   202         }
   209         }
   203         else {
   210         else {
   204             die "wrong content";
   211             die "$domainlist has wrong or malformed content\n";
   205         }
       
   206 
       
   207         foreach my $key (%domain2check) {
       
   208             $domain = $key;
       
   209             $port   = $domain2check{$key};
       
   210             print $domain, ' ', $port, "\n";
       
   211 
       
   212             if ("$port" =~ /^\s*$/) { $port = '443'; }
       
   213             print $domain, ' ', $port, "\n";
       
   214 
       
   215             check_tlsa($domain, $port);
       
   216         }
   212         }
   217 
   213 
   218     }
   214     }
   219 }
   215 }
   220 
   216 
   221 sub check_cert_expiry {
   217 sub check_cert_expiry {
   222     my $return = qx($check_date);
   218     my $return = qx($check_date);
   223     return $return;
   219     return $return;
   224 }
   220 }
   225