check_tlsa
changeset 8 ebb775c59021
parent 7 a2ce47570096
child 9 2041bac74e8d
equal deleted inserted replaced
7:a2ce47570096 8:ebb775c59021
     1 #! /usr/bin/perl
       
     2 
       
     3 use strict;
       
     4 use warnings;
       
     5 use feature qw(switch say);
       
     6 use if $^V >= v5.020 => (experimental => qw(smartmatch));
       
     7 use experimental qw(smartmatch);
       
     8 use Monitoring::Plugin;
       
     9 use File::Basename;
       
    10 
       
    11 
       
    12 my $ME      = basename $0;
       
    13 my $VERSION = '0.1';
       
    14 my $blurb   = 'This Plugin is intendet to check validity of TLSA Record';
       
    15 my $url     = 'http://www.schlittermann.de';
       
    16 my $author  = 'Heike Yvonne Pesch';
       
    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> ] ';
       
    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 
       
    38 Author: $author $email
       
    39 For more information visit $url
       
    40 _
       
    41 
       
    42 my $check_tlsa = Monitoring::Plugin->new(
       
    43   usage   => $usage,
       
    44   version => $VERSION,
       
    45   blurb   => $blurb,
       
    46   extra   => $extra,
       
    47   url     => $url,
       
    48   plugin  => $ME,
       
    49   timeout => 120,
       
    50 );
       
    51 
       
    52 $check_tlsa->add_arg(
       
    53   spec     => 'host|H=s',
       
    54   help     => q|Host/Domain to check|,
       
    55   required => 0,
       
    56 );
       
    57 
       
    58 $check_tlsa->add_arg(
       
    59   spec     => 'hostlist|f=s',
       
    60   help     => q|Host/Domainlist in file to check|,
       
    61   required => 0,
       
    62 );
       
    63 
       
    64 $check_tlsa->add_arg(
       
    65   spec     => 'expiry|e',
       
    66   help     => q|check expiry of Certificate|,
       
    67   required => 0,
       
    68 );
       
    69 
       
    70 $check_tlsa->add_arg(
       
    71   spec     => 'port|p=i',
       
    72   help     => q|Port of Domain to check the TLSA (default: 443)|,
       
    73   required => 0,
       
    74   default  => 443,
       
    75 );
       
    76 
       
    77 $check_tlsa->add_arg(
       
    78   spec     => 'queryserver|q=s',
       
    79   required => 0,
       
    80   help     =>
       
    81   q|DNS Server to ask to check the TLSA (default: defined in resolve.conf)|,
       
    82 
       
    83 );
       
    84 
       
    85 $check_tlsa->add_arg(
       
    86   spec     => 'protocol|P=s',
       
    87   help     => q|Protocol to ask to check the TLSA record of domain (default: tcp)|,
       
    88   required => 0,
       
    89   default  => 'tcp',
       
    90 );
       
    91 
       
    92 $check_tlsa->getopts;
       
    93 
       
    94 my $domain     = $check_tlsa->opts->host;
       
    95 my $domainlist = $check_tlsa->opts->hostlist;
       
    96 my $expiry     = $check_tlsa->opts->expiry;
       
    97 
       
    98 
       
    99 if (!$domain && !$domainlist) {
       
   100     my $script = basename $0;
       
   101     my $excuse = "Please set -H <domain> or -f <domainlist>\n"
       
   102     . "For all options try $script --help";
       
   103 
       
   104     say $excuse;
       
   105     exit 1;
       
   106 }
       
   107 
       
   108 my $port;
       
   109 my $cert;
       
   110 my $check_date;
       
   111 my $pattern = '^(?<domain>\S*\.[a-z]{2,4}?):{0,1}(?<port>[0-9]*$)';
       
   112 
       
   113 # @TODO find better way
       
   114 # nearly the same check is defined in get_domains
       
   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(); }
       
   124 
       
   125 sub check_tlsa {
       
   126     my $protocol = $check_tlsa->opts->protocol;
       
   127 
       
   128     $port = $check_tlsa->opts->port unless $port;
       
   129 
       
   130     if ("$port" eq '25') {
       
   131         $cert = "openssl s_client -starttls smtp -connect $domain:$port "
       
   132         . '< /dev/null 2>/dev/null';
       
   133     }
       
   134     else {
       
   135         $cert = "openssl s_client -connect $domain:$port "
       
   136         . '< /dev/null 2>/dev/null';
       
   137     }
       
   138 
       
   139     my $digquery        = "dig TLSA _$port._$protocol.$domain +short";
       
   140     my $diganswer       = qx($digquery);
       
   141     my $tlsa_usage      = substr($diganswer, 0, 1);
       
   142     my $tlsa_selector   = substr($diganswer, 2, 1);
       
   143     my $tlsa_match_type = substr($diganswer, 4, 1);
       
   144     my $dig_tlsa        = substr($diganswer, 6,);
       
   145     my $valid_date      = '';
       
   146     my $hashit;
       
   147 
       
   148     $dig_tlsa =~ s/(\S*)\s+(\S*)$/$1$2/;
       
   149 
       
   150     for ($tlsa_match_type) {
       
   151         when ('0') { die 'certs will be compared directly' }
       
   152         when ('1') { $hashit = 'sha256' }
       
   153         when ('2') { $hashit = 'sha512' }
       
   154         default { $hashit = 'sha256' }
       
   155     }
       
   156 
       
   157     my $gentlsa = 'openssl x509  -pubkey | '
       
   158        . 'openssl rsa -pubin -inform PEM -outform DER 2>/dev/null| '
       
   159        . "openssl $hashit";
       
   160     my $certtlsa = "$cert | $gentlsa";
       
   161 
       
   162     $check_date = 'openssl x509 -noout -startdate -enddate';
       
   163     $check_date = "$cert|$check_date";
       
   164 
       
   165     my $return;
       
   166 
       
   167     my $tlsa_record = qx($certtlsa) or die "nothing found!\n";
       
   168     $tlsa_record =~ s/^.*= (.*$)/$1/gi;
       
   169     $tlsa_record = uc($tlsa_record);
       
   170 
       
   171     if (defined $expiry) {
       
   172         $valid_date = check_cert_expiry();
       
   173     }
       
   174 
       
   175     if ($valid_date ne '') {
       
   176       $valid_date = "\n$valid_date";
       
   177     }
       
   178 
       
   179     if ("$tlsa_record" eq "$dig_tlsa") {
       
   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";
       
   187     }
       
   188     else {
       
   189       #$check_tlsa->plugin_exit(CRITICAL, "$domain: TLSA record NOT valid");
       
   190       $return = "CRITICAL, $domain: TLSA record is NOT valid";
       
   191     }
       
   192     say $return;
       
   193 }
       
   194 
       
   195 sub get_domains {
       
   196     open(my $filehandle, '<', $domainlist);
       
   197 
       
   198     my $pattern = '^(?<domain>\S*\.[a-z]{2,4}?):{0,1}(?<port>[0-9]*$)';
       
   199     while (<$filehandle>) {
       
   200         if (/$pattern/ig) {
       
   201             $domain = $+{domain};
       
   202 
       
   203             if ("$+{port}" =~ /^\s*$/) { $port = '443'; }
       
   204             else { $port   = $+{port}; }
       
   205 
       
   206             check_tlsa($domain, $port);
       
   207         }
       
   208         else {
       
   209             die "$domainlist has wrong or malformed content\n";
       
   210         }
       
   211 
       
   212     }
       
   213 }
       
   214 
       
   215 sub check_cert_expiry {
       
   216     my $return = qx($check_date);
       
   217     return $return;
       
   218 }