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 } |
|