Podsjetnik na istek poslužiteljskih certifikata

Poslužiteljski certifikati davno su postali neizostavni dio sigurne svakodnevne komunikacije na internetu. Certifikate na koje imamo pravo (u suradnji s CARNetom) izdaje firma Comodo, a njihovo maksimalno trajanje je tri godine. Taj je period dovoljno dug da većina nas zaboravi pravovremeno provjeriti datum isteka certifikata. Naravno, istekli certifikat aktivira alarm za sve mail klijente i web preglednike, a time i naše korisnike.

Možemo pokušati sebi negdje zapisati podsjetnik, ali ni takav zapis nije otporan na previde. Kako su u svakom certifikatu zapisani datumi početka i kraja valjanosti certifikata, jedno od elegantnijih rješenja jest skripta koja će periodično provjeravati datum isteka te, kad se vrijeme približi, o tome obavijestiti administratora. Jedna takva jednostavna bash skripta je priložena.

#!/bin/bash
TO="administrator@domena.hr"
SERVER=$(</putanja/do/popisa/posluzitelja.txt)
set -- $SERVER
MESSAGE="/tmp/mail_message.txt"
for SERVER in "$@"
do
        openssl_output=$(echo "
        GET / HTTP/1.0
        EOT" \
         | openssl s_client -connect $SERVER:443 2>&1);
        if [[ "$openssl_output" = *"-----BEGIN CERTIFICATE-----"* ]]; then
                cert_expiry_date=$(echo "$openssl_output" \
                 | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
                 | openssl x509 -enddate \
                 | awk -F= ' /notAfter/ { printf("%s\n",$NF); } ');
                seconds_until_expiry=$(echo "$(date --date="$cert_expiry_date" +%s) - $(date +%s)" |bc);
                days_until_expiry=$(echo "$seconds_until_expiry/(60*60*24)" |bc);
                if [[ ($days_until_expiry -lt 45) ]]; then
                        touch $MESSAGE
                        SUBJECT="Blizi se istek certifikata -  $SERVER"
                        echo "Do isteka certifikata na posluzitelju $SERVER je ostalo jos" \
"$days_until_expiry dana" > $MESSAGE
                        mail -s "$SUBJECT" "$TO" < $MESSAGE
                        /bin/rm -f "$MESSAGE"
                fi
        else
            echo "NOT_FOUND"> /dev/null 2>&1;
            exit 1
        fi
done

U varijabli "TO" se definira odredišna mail adresa, a u varijabli "SERVER" se nalazi putanja do tekstualne datoteke s popisom poslužitelja. Pomoću FOR petlje skripta se spaja na priključnu točku 443 svakog od definiranih poslužitelja, traži instalirani certifikat te provjerava njegov datum isteka. Ukoliko je taj datum unutar definiranog perioda od 45 dana od isteka, administrator će dobiti e-mail kao podsjetnik.

Skripta kao parametar prima popis poslužitelja koji se nalaze u tekstualnoj datoteci, a ona je jednostavna, po jedan poslužitelj u svakom retku. Ime poslužitelja može biti FQDN ili skraćeno, lokalno ime:

posluzitelj1.domena.hr
posluzitelj2.domena.hr
posluzitelj3.domena.hr

Broj poslužitelja je proizvoljan, a na ovaj način je dovoljno periodično pokretati skriptu na jednom poslužitelju i provjeravati sve ostale. Ja sam skriptu smjestio u direktorij /etc/cron.weekly, što znači da će se ona izvršavati jednom tjedno. Vremenski period prilagodite svojim potrebama, a moguće je, naravno, i odrediti proizvoljni period izvršavanja skripte naredbom crontab -e.

Kategorije: 
Vote: 
4.4
Vaša ocjena: Nema Average: 4.4 (5 votes)

Komentari

Hvala autoru za korisnu skriptu.

Predložio bih i dva poboljšanja:

(1) Broj dana do isteka nakon kojih se šalje poruka (ovdje 45) bolje bi bilo definirati varijablom koja se postavlja na početku skripte (tako je čitkije nekome tko želi promijeniti taj parametar).

(2) Ako neki od servera u listi nije dostupan ili ne sadrži SSL-certifikat, skripta radi echo u /dev/null (što baš nema smisla) i prekine se daljnje izvođenje (što baš nije poželjno jer nas zanimaju i ostali serveri). Mislim da bi bilo bolje ovako:

        else
            echo "$SERVER not found" >&2
        fi

Usput, na serveru gdje sam to podesio nije postojala naredba bc te je trebalo instalirati paket bc.

Mjesta za poboljšanja, naravno, uvijek ima, ja nisam htio previše komplicirati.

Što se tiče /dev/null, tu redirekciju sam stavio jer cron šalje mailove u slučaju greške prilikom izvršavanja. Prekidanje izvršavanja (exit 1) moj previd. Slažem se, oba vaša prijedloga su bolja.

Moze to i jednostavnije, samo u cron.daily treba staviti:

test `date +'%Y%m%d'` == 20151201 && mail -s "PODSJETNIK: obnoviti certifikat!" root%%Obnoviti certifikat%%

Ovo je iz glave, dakle provjeriti radi li kod vas.

No, pohvalno je da sistemci pisu i dijele svoje skripte....

Nakon više od 5 godina korištenja ove skripte došlo je vrijeme za dodatna poboljšanja.

Naime, postoje web-serveri gdje na istoj IP-adresi postoji više web-sjedišta s različitim domenskim imenima (a stoga i s različitim SSL-certifikatima za svako od tih web-sjedišta).

U takvom slučaju gornja skripta ne može dati datum isteka SSL-certifikata jer će naredba

openssl s_client -connect $SERVER:443

vratiti izlaz u kojem će se vidjeti poruka o grešci i koji neće sadržavati SSL-certifikat.

Rješenje je u tome da se u navedenu naredbu doda još parametar -servername na sljedeći način:

openssl s_client -connect $SERVER:443 -servername $SERVER

 

Skriptu sam još malo preradio tako da popis poslužitelja može sadržavati i broj porta za konekciju s poslužiteljem. Npr.:

host1.domena.hr
host2.domena.hr
host3.domena.hr:8081
host4.domena.hr:5001

 

Slijedi nova verzija skripte (s nešto dodanih komentara za lakše razumijevanje):

#!/bin/bash

# Skripta provjerava da li se približio istek SSL-certifikata na
# poslužiteljima zadanim u pripadnoj listi te šalje e-mail s upozorenjem.

# adresa na koju se šalje e-mail
TO="administrator@domena.hr"

# učitaj popis servera u varijablu SERVER
SERVER=$(</putanja/do/popisa/posluzitelja.txt)

# postavi popis servera u argumente skripte
set -- $SERVER

# privremena datoteka za tijelo e-mail poruke
MESSAGE="/tmp/mail_message.txt"

# broj dana do isteka nakon kojih se šalje poruka
REMINDER_DAYS=30

for SERVER in "$@"
do
  # provjera je li server zadan u formatu "server:port"
  if [[ $SERVER == *:* ]]; then
    PORT=`echo $SERVER | sed -e 's/[^:]*://'`
    SERVER=`echo $SERVER | sed -e 's/:.*//'`
  else
    # port nije zadan pa se pretpostavlja 443
    PORT=443
  fi

  # Spoji se na server i dohvati izlaz koji bi trebao sadržavati SSL-certifikat.
  # Neki serveri zahtijevaju konekciju uz opciju -servername da bi vratili očekivani rezultat.
  openssl_output=$(echo "
  GET / HTTP/1.0
  EOT" | openssl s_client -connect $SERVER:$PORT -servername $SERVER 2>&1)

  # provjera da li izlaz sadrži SSL-certifikat
  if [[ "$openssl_output" = *"-----BEGIN CERTIFICATE-----"* ]]; then
    cert_expiry_date=$(echo "$openssl_output" \
     | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
     | openssl x509 -enddate \
     | awk -F= ' /notAfter/ { printf("%s\n",$NF); } ');
    seconds_until_expiry=$(echo "$(date --date="$cert_expiry_date" +%s) - $(date +%s)" |bc);
    days_until_expiry=$(echo "$seconds_until_expiry/(60*60*24)" |bc);

    # pošalji e-mail ako se bliži istek SSL-certifikata
    if [[ ($days_until_expiry -le $REMINDER_DAYS) ]]; then
      touch $MESSAGE
      /bin/rm -f "$MESSAGE"
      touch $MESSAGE
      SUBJECT="Blizi se istek SSL-certifikata na $SERVER:$PORT"
      echo "Do isteka certifikata na posluzitelju $SERVER:$PORT je ostalo jos" \
      "$days_until_expiry dana" > $MESSAGE
      mail -s "$SUBJECT" "$TO" < $MESSAGE
      /bin/rm -f "$MESSAGE"
    fi
  else
    # ispiši grešku na STDERR koju će cron poslati na e-mail kada izvršava ovu skriptu
    echo "Nisam se uspio spojiti na $SERVER:$PORT ili server nije vratio SSL-certifikat
openssl s_client -connect $SERVER:$PORT -servername $SERVER
$openssl_output" >&2
  fi
done