📜 ⬆️ ⬇️

DIY SSL-certificate monitoring script for the lazy

I continue the epic of homemade bicycles.

A little bit of history. At work urgently needed a script to monitor SSL certificates of our web servers. Opinions were divided, I offered to get used to the role of an attacker and scan all the company's subnets, opponents - make a list and monitor it.

Since the admins are often lazy and, sometimes, do not document their work, and also like to do something and forget (about the existence of the server), I decided that my method is better (and more universal) and started writing the script.
')
So, what you need:

The script scans the specified subnets for the presence of an open 443rd port and, using openssl, verifies the certificate. Then it displays the certificates, which expire in the next month. It also checks the reverse zones in the day and, if it does not find (writing in the reverse zone), happily informs about it. The results are stored in separate files (“good certificates”, expiring / expired, connection error, ip-addresses without reverse zone) and in one common file.

Script under the cut.


 #! / bin / bash
 # Certificate monitoring script
 # monitors for expired ssl-certificates and missing reverse zones.
 # depends on:
 # 2010 lolipop.habrahabr.ru
 # dependencies, without them the script refuses to run
 DEPS = "host bc openssl nmap awk"

 # where to send stderr, by default in devnull :)
 DN = / dev / null
 # verbose output to the screen (duplicates the logs on the screen), turned off by default, turn off after
 # script settings
 VERBOSE = YES
 # openssl tcp-connect timeout
 TIMEOUT = 0.5
 # maximum timeout is calculated as 0.5 * maximum of TRIES.  In this case
 # in 2 seconds the connection should be established
 TRIES = "1 2 3 4"

 # name of files with logs, everything is clear by name
 LOGGOOD = log_good
 LOGFAIL = log_fail
 LOGNOREV = log_noreverse
 LOGEXPIRED = log_expired
 # internal variable
 NODESNAME = nodes
 # common file name
 REPORTNAME = report
 # list of exceptions, these ip not scan
 EXCLUDELIST = "192.168.6.36 | 192.168.6.48"

 # subnets that scan
 SUBNETS = "192.168.6.0/24 123.45.67.0/24 1.2.3.0/24"

 # roughly: (
 export LANG = C
 EXPIRY = '2678393' # ~ 1 month
 DATETODAY = `date +% s`
 RUNDATE = `date`

 # checking for installed dependencies
 for DEP in $ DEPS;  do
     which $ DEP> $ DN 2> $ DN
     if ["$?"  == "1"];  then echo "Binary $ DEP is missing. Install it!";  exit;  fi
 done

 # cleaning
 rm -f $ NODESNAME
 rm -f tmp. *
 rm -f $ LOGGOOD
 rm -f $ LOGFAIL
 rm -f $ LOGNOREV
 rm -f $ LOGEXPIRED

 # scan the list of subnets
 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

 # main loop
 for i in `cat $ NODESNAME`;  do
 # check on the reverse zone, if there is a ".", we believe that this is a valid domain name
     HOSTNAME = `host $ i |  head -1 |  awk '{print $ 5}' |  grep "\." `
     if ["$ HOSTNAME" == ""];  then
         HOSTNAME = "NO-REVERSE-ZONE"
         echo $ i >> $ LOGNOREV
     fi
 # connect to ip
     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 &
 # get the process id
     OPENSSLPID = $!
 # we wait :)
     sleep 0.3
 # we check whether the process is completed and, if not, we nail it on timeout
     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 the attempt fails, mark it in a separate file
     if ["$ RESULT" == ""];  then
         if ["$ VERBOSE" == "YES"];  then echo $ i "" $ HOSTNAME "NO_DATA";  fi
         echo $ i "" $ HOSTNAME "NO_DATA" >> $ LOGFAIL
 # otherwise, check if the certificate has expired or not.
     else
         GETDATE = `echo $ i" "$ HOSTNAME" "$ RESULT |  awk '{print $ 3, $ 4, $ 5, $ 6}' `
         DATECERT = `date +% s -d" $ GETDATE "`
         DATERESULT = `echo $ DATECERT - $ DATETODAY |  bc`
 # has expired or will expire soon
         if [$ EXPIRY -gt $ DATERESULT];  then
             BOOL = "NOT OK !!!"
             echo $ i "" $ HOSTNAME "" $ RESULT $ BOOL >> $ LOGEXPIRED
         else
 # everything is good :)
             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

 # script always displays a list of expired / expiring certificates, regardless of $ 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

Source: https://habr.com/ru/post/92001/


All Articles