Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / ssl / ssl-certificate-expiry @ 09b88141

Historique | Voir | Annoter | Télécharger (6,11 ko)

1 59f057f8 Olivier Mehani
#!/bin/sh -u
2 a4c30808 Olivier Mehani
# -*- sh -*-
3 793b75b9 Olivier Mehani
# shellcheck disable=SC2039
4 a4c30808 Olivier Mehani
5
: << =cut
6
7
=head1 NAME
8
9 59f057f8 Olivier Mehani
ssl-certificate-expiry - Plugin to monitor Certificate expiration on multiple services and ports
10 a4c30808 Olivier Mehani
11
=head1 CONFIGURATION
12
13 81e19668 Olivier Mehani
  [ssl-certificate-expiry]
14 3aa213d4 ruliane
    env.services www.service.tld blah.example.net_PORT foo.example.net_PORT_STARTTLS
15
16
PORT is the TCP port number
17
STARTTLS is passed to openssl as "-starttls" argument. Useful for services like SMTP or IMAP implementing StartTLS.
18
  Current known values are ftp, imap, pop3 and smtp
19
  PORT is mandatory if STARTTLS is used.
20 a4c30808 Olivier Mehani
21
To set warning and critical levels do like this:
22
23 81e19668 Olivier Mehani
  [ssl-certificate-expiry]
24 a4c30808 Olivier Mehani
    env.services ...
25
    env.warning 30:
26
27 47ef2182 Olivier Mehani
Alternatively, if you want to monitor hosts separately, you can create multiple symlinks named as follows.
28
29 81e19668 Olivier Mehani
  ssl-certificate-expiry_HOST_PORT
30 47ef2182 Olivier Mehani
31
For example:
32
33 81e19668 Olivier Mehani
  ssl-certificate-expiry_www.example.net
34
  ssl-certificate-expiry_www.example.org_443
35
  ssl-certificate-expiry_192.0.2.42_636
36
  ssl-certificate-expiry_2001:0DB8::badc:0fee_485
37 3aa213d4 ruliane
  ssl-certificate-expiry_mail.example.net_25_smtp
38 47ef2182 Olivier Mehani
39 59f057f8 Olivier Mehani
=head2 Cron setup
40 a4c30808 Olivier Mehani
41 59f057f8 Olivier Mehani
To avoid having to run the SSL checks during the munin-update, it is possible
42
to run it from cron, and save a cachefile to be read during the update, This is
43
particularly useful when checking a large number of certificates, or when some
44
of the hosts are slow.
45 a4c30808 Olivier Mehani
46 59f057f8 Olivier Mehani
To do so, add a cron job running the plugin with cron as the argument:
47
48 7e995a02 Olivier Mehani
  <minute> * * * <user> /usr/sbin/munin-run/ssl-certificate-expiry cron
49 59f057f8 Olivier Mehani
50
<user> should be the user that has write permission to the MUNIN_PLUGSTATE.
51 7e995a02 Olivier Mehani
<minute> should be a number between 0 and 59 when the check should run every hour.
52
53
If, for any reason, the cron script stops running, the script will revert to
54
uncached updates after the cache file is older than an hour.
55 59f057f8 Olivier Mehani
56
=head1 AUTHORS
57
58
 * Pactrick Domack (ssl_)
59
 * Olivier Mehani (ssl-certificate-expiry)
60 bba98f95 Martin Schobert
 * Martin Schobert (check for intermediate certs)
61
 
62 59f057f8 Olivier Mehani
 * Copyright (C) 2013 Patrick Domack <patrickdk@patrickdk.com>
63
 * Copyright (C) 2017, 2019 Olivier Mehani <shtrom+munin@ssji.net>
64 bba98f95 Martin Schobert
 * Copyright (C) 2020 Martin Schobert <martin@schobert.cc> 
65 a4c30808 Olivier Mehani
66
=head1 LICENSE
67
68
=cut
69
70 e7eb2886 Lars Kruse
# shellcheck disable=SC1090
71 a4c30808 Olivier Mehani
. "${MUNIN_LIBDIR}/plugins/plugin.sh"
72
73 59f057f8 Olivier Mehani
if [ "${MUNIN_DEBUG:-0}" = 1 ]; then
74 a4c30808 Olivier Mehani
    set -x
75
fi
76
77 81e19668 Olivier Mehani
HOSTPORT=${0##*ssl-certificate-expiry_}
78 59f057f8 Olivier Mehani
CACHEFILE="${MUNIN_PLUGSTATE}/$(basename "${0}").cache"
79 47ef2182 Olivier Mehani
80
if [ "${HOSTPORT}" != "${0}" ] \
81 7fed3b97 Lars Kruse
	&& [ -n "${HOSTPORT}" ]; then
82 47ef2182 Olivier Mehani
	services="${HOSTPORT}"
83
fi
84
85 e7eb2886 Lars Kruse
86
# Read data including a certificate from stdin and output the (fractional) number of days left
87
# until the expiry of this certificate. The output is empty if parsing failed.
88
parse_valid_days_from_certificate() {
89 793b75b9 Olivier Mehani
    local input_data
90
    local valid_until_string
91
    local valid_until_epoch
92
    local now_epoch
93
    local input_data
94 e7eb2886 Lars Kruse
    input_data=$(cat)
95 bba98f95 Martin Schobert
96 e7eb2886 Lars Kruse
    if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then
97
        valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \
98
            | grep "^notAfter=" | cut -f 2 -d "=")
99
        if [ -n "$valid_until_string" ]; then
100 292cfb95 Lars Kruse
            # FreeBSD requires special arguments for "date"
101
            if uname | grep -q ^FreeBSD; then
102
                valid_until_epoch=$(date -j -f '%b %e %T %Y %Z' "$valid_until_string" +%s)
103
                now_epoch=$(date -j +%s)
104
            else
105
                valid_until_epoch=$(date --date="$valid_until_string" +%s)
106 e7eb2886 Lars Kruse
                now_epoch=$(date +%s)
107 292cfb95 Lars Kruse
            fi
108
            if [ -n "$valid_until_epoch" ]; then
109 e7eb2886 Lars Kruse
                # calculate the number of days left
110
                echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }'
111
            fi
112
        fi
113
    fi
114
}
115
116
117
print_expire_days() {
118 793b75b9 Olivier Mehani
    local host="$1"
119
    local port="$2"
120 3aa213d4 ruliane
    local starttls="$3"
121 e7eb2886 Lars Kruse
122
    # Wrap IPv6 addresses in square brackets
123
    echo "$host" | grep -q ':' && host="[$host]"
124
125 3aa213d4 ruliane
    local s_client_args=
126
    [ -n "$starttls" ] && s_client_args="-starttls $starttls"
127
128 bba98f95 Martin Schobert
    # We extract and check the server certificate,
129
    # but the end date also depends on intermediate certs. Therefore
130
    # we want to check intermediate certs as well.
131
    #
132
    # The following cryptic lines do:
133
    # - invoke openssl and connect to a port
134
    # - print certs, not only the server cert
135
    # - extract each certificate as a single line
136
    # - pipe each cert to the parse_valid_days_from_certificate
137
    #   function, which basically is 'openssl x509 -enddate'
138
    # - get a list of the parse_valid_days_from_certificate
139
    #   results and sort them
140
    
141 3aa213d4 ruliane
    # shellcheck disable=SC2086
142 52144bc2 Lars Kruse
    echo "" | openssl s_client \
143 bba98f95 Martin Schobert
	-servername "$host" -connect "${host}:${port}" \
144
	-showcerts \
145
	$s_client_args 2>/dev/null | \
146
	awk '{
147
  	  if ($0 == "-----BEGIN CERTIFICATE-----") cert=""
148
  	  else if ($0 == "-----END CERTIFICATE-----") print cert
149
  	  else cert=cert$0
150
	  }' | \
151
	  while read -r CERT; do
152
	      (printf '\n-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n' "$CERT") | \
153
	  	  parse_valid_days_from_certificate
154
          done | sort -n | head -n 1
155
	
156 e7eb2886 Lars Kruse
}
157
158 59f057f8 Olivier Mehani
main() {
159
    for service in $services; do
160
	if echo "$service" | grep -q "_"; then
161
	    host=$(echo "$service" | cut -f 1 -d "_")
162
	    port=$(echo "$service" | cut -f 2 -d "_")
163 3aa213d4 ruliane
	    starttls=$(echo "$service" | cut -f 3 -d "_")
164 59f057f8 Olivier Mehani
	else
165
	    host=$service
166
	    port=443
167 0b4725d6 Olivier Mehani
	    starttls=""
168 59f057f8 Olivier Mehani
	fi
169
	fieldname="$(clean_fieldname "$service")"
170 3aa213d4 ruliane
	valid_days=$(print_expire_days "$host" "$port" "$starttls")
171 59f057f8 Olivier Mehani
	[ -z "$valid_days" ] && valid_days="U"
172
	printf "%s.value %s\\n" "$fieldname" "$valid_days"
173 7e995a02 Olivier Mehani
        echo "${fieldname}.extinfo Last checked: $(date)"
174 59f057f8 Olivier Mehani
    done
175
}
176 e7eb2886 Lars Kruse
177 59f057f8 Olivier Mehani
case ${1:-} in
178 a4c30808 Olivier Mehani
    config)
179
	echo "graph_title SSL Certificates Expiration"
180
	echo 'graph_args --base 1000'
181
	echo 'graph_vlabel days left'
182
	echo 'graph_category security'
183 59f057f8 Olivier Mehani
	echo "graph_info This graph shows the numbers of days before certificate expiry"
184 a4c30808 Olivier Mehani
	for service in $services; do
185
	    fieldname=$(clean_fieldname "$service")
186 47ef2182 Olivier Mehani
	    echo "${fieldname}.label $(echo "${service}" | sed 's/_/:/')"
187 59f057f8 Olivier Mehani
	    print_thresholds "${fieldname}" warning critical
188 a4c30808 Olivier Mehani
	done
189
190
	exit 0
191
	;;
192 59f057f8 Olivier Mehani
    cron)
193 7e995a02 Olivier Mehani
	UPDATE="$(main)"
194
	echo "${UPDATE}" > "${CACHEFILE}"
195
	chmod 0644 "${CACHEFILE}"
196 59f057f8 Olivier Mehani
197
	exit 0
198
	;;
199 a4c30808 Olivier Mehani
esac
200
201 7e995a02 Olivier Mehani
if [ -n "$(find "${CACHEFILE}" -mmin -60 2>/dev/null)" ]; then
202 59f057f8 Olivier Mehani
	cat "${CACHEFILE}"
203
	exit 0
204
fi
205 a4c30808 Olivier Mehani
206 59f057f8 Olivier Mehani
main