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