-
Notifications
You must be signed in to change notification settings - Fork 4
/
SSLexpiryPredictions.sh
executable file
·324 lines (280 loc) · 8.58 KB
/
SSLexpiryPredictions.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/bin/bash
##############################################
#
# PURPOSE: The script to predict expiring SSL certificates.
#
# AUTHOR: 'Abhishek.Tamrakar'
#
# VERSION: 1.0.0
#
# EMAIL: abhishek.tamrakar08@gmail.com
#
# GENERATED: on 2018-05-20
#
# LICENSE: Copyright (C) 2018 Abhishek Tamrakar
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################
#your Variables go here
SCRIPT=${0##/}
WRITEFORMAT=table
CONFIG=0
DIR=0
LOGLEVEL=info
SSLCMD=$(which openssl)
JQ=$(which jq)
EXT=crt
HEADER="query;commonname;issuer;serial;timetoexpire"
SSLCERTIFICATEMETRICNAME=ssl_certificate_time_to_expire
SSLCERTIFICATESCANNED=ssl_certificates_scanned_total
SSLCERTIFICATEEXPIRED=ssl_certificates_expired_total
SCANNED=0
EXPIRED=0
OUTFILE=''
# functions here
usage()
{
cat <<EOF
USAGE: $SCRIPT -[cdewolh]"
DESCRIPTION: This script predicts and prints the expiring SSL certificates based on the end date.
OPTIONS:
-c| sets the value for configuration file which has server:port or host:port details.
-d| sets the value of directory containing the certificate files in crt or pem format.
-e| sets the value of certificate extention [crt, pem], default: crt
-w| sets the value for output format of the script [table, csv, json, prometheus], default: table
-o| write output to a file.
-l| sets the log level [info, debug, error, warn], default: info
-h| prints this help and exit.
EOF
exit 1
}
error()
{
>&2 printf '\n%s: %6s\n' "ERROR" "$@"
exit 1
}
warn()
{
>&2 printf '\n%s: %6s\n\n' "WARN" "$@"
}
log()
{
local LEVEL=$LOGLEVEL
local SEVERITY=$1
local MESSAGE=$2
if [[ "${LEVEL}" = "${SEVERITY}" ]]; then
# check if loglevel is same as severity or one of the valid log levels.
case $SEVERITY in
info|debug ) printf '\n%s: %6s\n' "${SEVERITY}" "$MESSAGE";;
* ) error "invalid log level $LOGLEVEL";;
esac
fi
}
printCSV()
{
local ARGS=$@
i=0
if [[ ${#ARGS} -ne 0 ]]; then
#statements
printf '%s\n' $HEADER | awk -F";" 'BEGIN{OFS=","};{print $1,$2,$3,$4,$5}'
printf '%s\n' ${ARGS} | \
sed 's/|/ /g;s/\;/,/g' | \
sort -t',' -g -k5
fi
}
printTable()
{
local ARGS=$@
local LINEBREAK="------------------------------------------------------------------------------------------"
i=0
if [[ ${#ARGS} -ne 0 ]]; then
#statements
printf '%s\n' $LINEBREAK
printf '%70s\n' "List of expiring SSL certificates"
printf '%s\n' $LINEBREAK
printf '%s\n%s\n' ${HEADER^^} ${ARGS} | \
sed 's/|/ /g' | \
sort -t';' -g -k5 | \
column -s';' -t | \
awk '{printf "%s\n", $0}'
printf '%s\n' $LINEBREAK
fi
}
printJSON()
{
local ARGS=$@
local VALUE=''
if [[ ${#ARGS} -ne 0 ]]; then
count=1
printf '%s' "{ \"items\": [ "
for VALUE in ${ARGS}; do
VALUE=(${VALUE//;/ })
printf '%s' "{ \"${VALUE[0]}\": { \"commonname\": \"${VALUE[1]//|/ }\", \"issuer\": \"${VALUE[2]//|/ }\", \"serial\": \"${VALUE[3]}\", \"days\": ${VALUE[4]} } }, "
done| sed -r 's/(.*), /\1/'
printf '%s' " ] }"
fi
}
printPrometheus()
{
local ARGS="$1"
local SCANNEDCERTS=$2
local EXPIREDCERTS=$3
local VALUE=''
printf '%s\n' "# HELP $SSLCERTIFICATEMETRICNAME ssl certificate expiration time in days"
printf '%s\n' "# TYPE $SSLCERTIFICATEMETRICNAME GAUGE"
for VALUE in ${ARGS}; do
VALUE=(${VALUE//;/ })
# ignore putting filename in prometheus metrics
printf '%s %.2f\n' "$SSLCERTIFICATEMETRICNAME{commonname=\"${VALUE[1]//|/ }\",issuer=\"${VALUE[2]//|/ }\",serial=\"${VALUE[3]}\"}" ${VALUE[4]}
done
printf '%s\n' "# HELP $SSLCERTIFICATESCANNED total ssl certificates scanned"
printf '%s\n' "# TYPE $SSLCERTIFICATESCANNED COUNTER"
printf '%s %d\n' "$SSLCERTIFICATESCANNED" $SCANNEDCERTS
printf '%s\n' "# HELP $SSLCERTIFICATEEXPIRED total ssl certificates expired"
printf '%s\n' "# TYPE $SSLCERTIFICATEEXPIRED COUNTER"
printf '%s %d\n' "$SSLCERTIFICATEEXPIRED" $EXPIREDCERTS
}
printOutput()
{
local ARGS=$@
case $WRITEFORMAT in
table) printTable "${ARGS}";;
csv) printCSV "${ARGS}";;
json) if [[ "x${JQ}" = "x" ]]; then
warn "to pretty print json, install jq"
printJSON "${ARGS}"
else
printJSON "${ARGS}"|${JQ}
fi;;
prometheus) EXT=prom; printPrometheus "${ARGS}" $SCANNED $EXPIRED;;
*) error "$WRITEFORMAT - invalid or unsupported format."
esac
}
calcEndDate()
{
if [[ x$SSLCMD = x ]]; then
#statements
error "$SSLCMD command not found!"
fi
# when cert dir is given
if [[ $DIR -eq 1 ]]; then
#statements
for CERTDIR in ${TARGETDIR[@]}
do
ISCERTSEXISTS=$(ls -A $CERTDIR| egrep "*.$EXT$")
if [[ -z ${ISCERTSEXISTS} ]]; then
#statements
warn "no certificate files at $CERTDIR with extention $EXT"
fi
for FILE in $(find $CERTDIR/ -maxdepth 2 -type f -iname "*.${EXT}")
do
log debug "Scanning certificate ${FILE}"
SSLINFO=($(openssl x509 -in $FILE -noout -subject -issuer -serial | \
sed -r 's/\s=\s/=/g;s/(.*),\s(.*)/\2/g;s/\s/|/g' | \
awk -F= '{print $NF}'))
log debug "processed certificate information - ${SSLINFO[*]}"
EXPDATE=$($SSLCMD x509 -in $FILE -noout -enddate)
log debug "Certificate ${FILE} expires on ${EXPDATE}"
EXPEPOCH=$(date -d "${EXPDATE##*=}" +%s)
CERTIFICATENAME=${FILE##*/}
getExpiry $EXPEPOCH ${CERTIFICATENAME%%.*} "${SSLINFO[*]}"
done
done
elif [[ $CONFIG -eq 1 ]]; then
#statements
while read LINE
do
log debug "Scanning certificate for ${LINE}"
if echo "$LINE" | \
egrep -q '^[a-zA-Z0-9.]+:[0-9]+|^[a-zA-Z0-9]+_.*:[0-9]+';
then
SSLINFO=($(echo | openssl s_client -connect $LINE 2>/dev/null | \
openssl x509 -noout -subject -issuer -serial 2>/dev/null | \
sed -r 's/\s=\s/=/g;s/(.*),\s(.*)/\2/g;s/\s/|/g' | \
awk -F= '{print $NF}'))
log debug "processed certificate information - ${SSLINFO[*]}"
EXPDATE=$(echo | \
openssl s_client -connect $LINE 2>/dev/null | \
openssl x509 -noout -enddate 2>/dev/null);
if [[ $EXPDATE = '' ]]; then
#statements
warn "[error:0906D06C] Cannot fetch certificates for $LINE"
else
log debug "Certificate ${LINE} expires on ${EXPDATE}"
EXPEPOCH=$(date -d "${EXPDATE##*=}" +%s);
CERTIFICATENAME=${LINE%%:*};
getExpiry $EXPEPOCH ${CERTIFICATENAME} "${SSLINFO[*]}"
fi
else
warn "[format error] $LINE is not in required format!"
fi
done < $CONFIGFILE
fi
}
getExpiry()
{
local EXPDATE=$1
local CERTNAME=$2
local DATA=($3)
TODAY=$(date +%s)
TIMETOEXPIRE=$(( ($EXPDATE - $TODAY)/(60*60*24) ))
log debug "${CERTNAME}.${EXT} will expire in ${TIMETOEXPIRE} days"
((SCANNED+=1))
if [[ ${TIMETOEXPIRE} -le 0 ]]; then
((EXPIRED+=1))
fi
log debug "extracted ssl information - ${DATA[*]}"
EXPCERTS=( ${EXPCERTS[@]} "${CERTNAME};${DATA[0]};${DATA[1]};${DATA[2]};$TIMETOEXPIRE" )
log debug "Expiring certificates - ${EXPCERTS[*]}"
}
# your script goes here
while getopts ":c:d:w:o:e:l:h" OPTIONS
do
case $OPTIONS in
c ) CONFIG=1
CONFIGFILE="$OPTARG"
if [[ ! -e $CONFIGFILE ]] || [[ ! -s $CONFIGFILE ]]; then
#statements
error "$CONFIGFILE does not exist or empty!"
fi;;
e ) EXT="$OPTARG"
case $EXT in
crt|pem|cert )
log info "Extention check complete."
;;
* )
error "invalid certificate extention $EXT!"
;;
esac;;
d ) DIR=1
TARGETDIR="${OPTARG//,/ }"
[[ ${#TARGETDIR[@]} -eq 0 ]] && error "$TARGETDIR empty variable!";;
w ) WRITEFORMAT="$OPTARG";;
o ) OUTFILE="$OPTARG";;
l ) LOGLEVEL="$OPTARG";;
h ) usage;;
\? ) usage;;
: ) error "Argument required !!! see '-h' for help";;
esac
done
shift $(($OPTIND - 1))
#
calcEndDate
#finally print the list
if [[ ${OUTFILE} = "" ]]; then
printOutput ${EXPCERTS[@]}
else
printOutput ${EXPCERTS[@]} > ${OUTFILE}
# handle permissions for the files paret directory
chmod -R 755 ${OUTFILE%/*}
fi