Pull to refresh

DIY SSL-certificate monitoring script для ленивых

Reading time4 min
Views4.1K
Продолжаю эпопею самодельных велосипедов.

Чуть-чуть истории. На работе срочно понадобился скрипт для мониторинга 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
Tags:
Hubs:
+23
Comments18

Articles