Продолжаю эпопею самодельных велосипедов.
Чуть-чуть истории. На работе срочно понадобился скрипт для мониторинга SSL-сертификатов наших веб-серверов. Мнения разделились, я предлагал вжиться в роль злоумышленника и просканировать все подсети компании, оппоненты — составить список и мониторить его.
Так как админчики зачастую ленивые и, бывает, не документируют свою работу, а так же любят что-то сделать и забыть(про существование сервера), я решил что мой способ лучше(и универсальней) и приступил к написанию скрипта.
Итак, что понадобится:
Скрипт сканирует заданные подсети на наличие открытого 443-го порта и, с помощью openssl, проверяет сертификат. Потом выводит сертификаты, которые истекают в ближайший месяц. Так же проверяет в днсе обратные зоны и, если не находит (запись в обратной зоне), радостно об этом сообщает. Результаты складываются в отдельные файлы(«хорошие сертификаты», истекающие/просроченные, ошибка соединения, айпи-адреса без обратной зоны) и в один общий файл.
Скрипт под катом.
Чуть-чуть истории. На работе срочно понадобился скрипт для мониторинга SSL-сертификатов наших веб-серверов. Мнения разделились, я предлагал вжиться в роль злоумышленника и просканировать все подсети компании, оппоненты — составить список и мониторить его.
Так как админчики зачастую ленивые и, бывает, не документируют свою работу, а так же любят что-то сделать и забыть(про существование сервера), я решил что мой способ лучше(и универсальней) и приступил к написанию скрипта.
Итак, что понадобится:
- OpenSSL
- Nmap
- Bash
- bc
- awk
Скрипт сканирует заданные подсети на наличие открытого 443-го порта и, с помощью openssl, проверяет сертификат. Потом выводит сертификаты, которые истекают в ближайший месяц. Так же проверяет в днсе обратные зоны и, если не находит (запись в обратной зоне), радостно об этом сообщает. Результаты складываются в отдельные файлы(«хорошие сертификаты», истекающие/просроченные, ошибка соединения, айпи-адреса без обратной зоны) и в один общий файл.
Скрипт под катом.
#!/bin/bash
# Certificate monitoring script
# monitors for expired ssl-certificates and missing reverse dns zones.
# depends on:
# 2010 lolipop.habrahabr.ru
# зависимости, без них скрипт запускаться отказывается
DEPS="host bc openssl nmap awk"
# куда отправлять stderr, по умолчанию в девнулл :)
DN=/dev/null
# подробный вывод на экран (дублирует логи на экране), выключен по-умолчанию, выключить после
# настройки скрипта
VERBOSE=YES
# таймаут ожидания tcp-коннекта openssl
TIMEOUT=0.5
# максимальный таймаут вычисляется как 0.5 * максимум из TRIES. В данном случае
# за 2 секунды соединение должно установиться
TRIES="1 2 3 4"
# название файлов с логами, по названию всё ясно
LOGGOOD=log_good
LOGFAIL=log_fail
LOGNOREV=log_noreverse
LOGEXPIRED=log_expired
# внутренняя переменная
NODESNAME=nodes
# имя общего файла
REPORTNAME=report
# список исключений, эти айпи не сканировать
EXCLUDELIST="192.168.6.36|192.168.6.48"
# подсети, которые сканировать
SUBNETS="192.168.6.0/24 123.45.67.0/24 1.2.3.0/24"
# грубо :(
export LANG=C
EXPIRY='2678393' # ~ 1 month
DATETODAY=`date +%s`
RUNDATE=`date`
# проверка на установленные зависимости
for DEP in $DEPS; do
which $DEP > $DN 2> $DN
if [ "$?" == "1" ]; then echo "Binary $DEP is missing. Install it!"; exit; fi
done
# очистка
rm -f $NODESNAME
rm -f tmp.*
rm -f $LOGGOOD
rm -f $LOGFAIL
rm -f $LOGNOREV
rm -f $LOGEXPIRED
# сканирование списка подсетей
for NET in $SUBNETS; do
if [ "$VERBOSE" == "YES" ]; then echo "Scanning $NET"; fi
nmap -v $NET -PN -n -p 443 | grep "Discovered" | awk '{print $6}' | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | grep -v -E "$EXCLUDELIST" >> $NODESNAME
done
# главный цикл
for i in `cat $NODESNAME`; do
# проверка на обратную зону, если есть ".", считаем что это валидное доменное имя
HOSTNAME=`host $i | head -1 | awk '{print $5}' | grep "\."`
if [ "$HOSTNAME" == "" ]; then
HOSTNAME="NO-REVERSE-ZONE"
echo $i >> $LOGNOREV
fi
# подключение к айпи
echo | openssl s_client -connect $i:443 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -noout -dates 2>/dev/null | awk '/After/' | cut --delimiter="=" -s -f2 > tmp.$i &
# получаем пид процесса
OPENSSLPID=$!
# ждём :)
sleep 0.3
# проверяем, выполнился ли процесс и, если нет, прибиваем его по таймауту
for j in $TRIES; do
SIZE=`du tmp.$i | awk '{print $1}'`
if [ "$SIZE" == "0" ]; then
T=$j
sleep $TIMEOUT
else
kill -9 $OPENSSLPID > $DN 2> $DN
break
fi
done
RESULT=`cat tmp.$i`
rm -f tmp.$i
# если попытка не удалась, отмечаем это в отдельном файле
if [ "$RESULT" == "" ]; then
if [ "$VERBOSE" == "YES" ]; then echo $i" "$HOSTNAME" NO_DATA"; fi
echo $i" "$HOSTNAME" NO_DATA" >> $LOGFAIL
# в противном случае, проверяем, просрочился ли сертификат или нет.
else
GETDATE=`echo $i" "$HOSTNAME" "$RESULT | awk '{print $3,$4,$5,$6}' `
DATECERT=`date +%s -d "$GETDATE"`
DATERESULT=`echo $DATECERT - $DATETODAY | bc`
# истек или скоро истечет
if [ $EXPIRY -gt $DATERESULT ]; then
BOOL="NOT OK!!!"
echo $i" "$HOSTNAME" "$RESULT $BOOL >> $LOGEXPIRED
else
# всё в порядке :)
BOOL="OK"
fi
if [ "$VERBOSE" == "YES" ]; then echo $i" "$HOSTNAME" "$RESULT $BOOL; fi
if [ "$BOOL" == "OK" ]; then
echo $i" "$HOSTNAME" "$RESULT $BOOL>> $LOGGOOD
fi
fi
done
# скрипт всегда выводит список истекших/истекающих сертификатов, вне зависмости от $VERBOSE
cat $LOGEXPIRED 2>$DN
# Report module
echo REPORT FOR SSL SCAN >> $REPORTNAME
echo $RUNDATE >> $REPORTNAME
echo SUBNETS: $SUBNETS >> $REPORTNAME
echo >> $REPORTNAME
echo ===================================================== >> $REPORTNAME
echo EXPIRED CERTIFICATES: >> $REPORTNAME
cat $LOGEXPIRED >> $REPORTNAME 2>$DN
echo >> $REPORTNAME
echo ===================================================== >> $REPORTNAME
echo GOOD CERTIFICATES: >> $REPORTNAME
cat $LOGGOOD >> $REPORTNAME 2>$DN
echo >> $REPORTNAME
echo ===================================================== >> $REPORTNAME
echo FAILED TO CONNECT: >> $REPORTNAME
cat $LOGFAIL >> $REPORTNAME 2>$DN
echo >> $REPORTNAME
echo ===================================================== >> $REPORTNAME
echo NO REVERSE DNS ZONE: >> $REPORTNAME
cat $LOGNOREV >> $REPORTNAME 2>$DN
echo >> $REPORTNAME
