Birden çok uzak sunucu için SSL sertifikasının son kullanma tarihini kontrol edin


18

Bu OpenSSL komutunu kullanarak ssl sertifikalarının sona erme tarihini öğrenebilirim:

openssl x509 -noout -in <filename> -enddate

Ancak sertifikalar farklı web sunucularına dağılmışsa, tüm bu sertifikaların son kullanma tarihlerini tüm sunucularda nasıl bulursunuz?

Diğer ana bilgisayara bağlanmak için bir yol var gibi görünüyor, ancak bunu kullanarak son kullanma tarihini nasıl alacağınızdan emin değilim:

openssl s_client -connect host:port

Yanıtlar:


15

Aynı sorunu yaşadım ve yazdım ... Hızlı ve kirli, ama işe yaramalı. Henüz geçerli olmayan veya önümüzdeki 90 gün içinde süresi dolan tüm sertifikaları günlüğe kaydeder (ve hata ayıklama ile ekrana yazdırır). Bazı hatalar içerebilir, ancak toparlamaktan çekinmeyin.

#!/bin/sh

DEBUG=false
warning_days=90 # Number of days to warn about soon-to-expire certs
certs_to_check='serverA.test.co.uk:443
serverB.test.co.uk:8140
serverC.test.co.uk:443'

for CERT in $certs_to_check
do
  $DEBUG && echo "Checking cert: [$CERT]"

  output=$(echo | openssl s_client -connect ${CERT} 2>/dev/null |\
  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' |\
  openssl x509 -noout -subject -dates 2>/dev/null) 

  if [ "$?" -ne 0 ]; then
    $DEBUG && echo "Error connecting to host for cert [$CERT]"
    logger -p local6.warn "Error connecting to host for cert [$CERT]"
    continue
  fi

  start_date=$(echo $output | sed 's/.*notBefore=\(.*\).*not.*/\1/g')
  end_date=$(echo $output | sed 's/.*notAfter=\(.*\)$/\1/g')

  start_epoch=$(date +%s -d "$start_date")
  end_epoch=$(date +%s -d "$end_date")

  epoch_now=$(date +%s)

  if [ "$start_epoch" -gt "$epoch_now" ]; then
    $DEBUG && echo "Certificate for [$CERT] is not yet valid"
    logger -p local6.warn "Certificate for $CERT is not yet valid"
  fi

  seconds_to_expire=$(($end_epoch - $epoch_now))
  days_to_expire=$(($seconds_to_expire / 86400))

  $DEBUG && echo "Days to expiry: ($days_to_expire)"

  warning_seconds=$((86400 * $warning_days))

  if [ "$seconds_to_expire" -lt "$warning_seconds" ]; then
    $DEBUG && echo "Cert [$CERT] is soon to expire ($seconds_to_expire seconds)"
    logger -p local6.warn "cert [$CERT] is soon to expire ($seconds_to_expire seconds)"
  fi
done

OS X üzerinde kullanıyorsanız, datekomutun düzgün çalışmadığını görebilirsiniz. Bunun nedeni , bu yardımcı programın Unix ve Linux sürümündeki farklılıklardır . Bağlantılı yayın, bu işi yapmak için seçeneklere sahiptir.


Komut dosyanızı, posta sunucusu sertifikalarını da kontrol edebilmek ve tüm sertifikaların durumu hakkında güzel bir genel bakış vermek için biraz değiştirdim / genişlettim. Değiştirilmiş komut dosyasını şu adreste
Lars Kiesow

1
Sunucu SNI kullanıyorsa, -servernameargümanı şu şekilde eklemeniz gerekir :openssl s_client -servername example.com -connect example.com:443
Flimm

11

Aşağıdaki komutu çalıştırmanız yeterlidir ve son kullanma tarihini verir:

echo q | openssl s_client -connect google.com.br:443 | openssl x509 -noout -enddate

Daha fazla uzak sunucu için bu bilgileri toplamak amacıyla bu komutu bir toplu iş dosyasında kullanabilirsiniz.


2
Bunu yazdığınız şekilde, onu bitirmek için CTRL-C'ye basmanız gerekir. Bunu şu şekilde düzeltebilirsiniz: openssl s_client -connect google.com.br:443 </ dev / null 2> & 1 | openssl x509 -noout -enddate Sadece bir düşünce.
16:16

1
Sunucu SNI kullanıyorsa, -servernameargümanı şu şekilde kullanmanız gerekir :openssl s_client -servername google.com.br -connect google.com.br:443
Flimm

6

Aşağıda benim senaryom nagios içinde bir çek olarak. Belirli bir ana bilgisayara bağlanır, sertifikanın -c / -w seçenekleri tarafından belirlenen bir eşik içinde geçerli olduğunu doğrular. Sertifikanın CN'sinin beklediğiniz adla eşleşip eşleşmediğini kontrol edebilir.

Python openssl kütüphanesine ihtiyacınız var ve tüm testleri python 2.7 ile yaptım.

Bir kabuk betiğinin bunu birkaç kez çağırması önemsiz olacaktır. Komut dosyası, kritik / uyarı / ok durumu için standart nagios çıkış değerlerini döndürür.

Google sertifikasının basit bir kontrolü bu şekilde yapılabilir.

./check_ssl_certificate -H www.google.com -p 443 -n www.google.com

Expire OK[108d] - CN OK - cn:www.google.com

check_ssl_certificate

#!/usr/bin/python

"""
Usage: check_ssl_certificate -H <host> -p <port> [-m <method>] 
                      [-c <days>] [-w <days>]
  -h show the help
  -H <HOST>    host/ip to check
  -p <port>    port number
  -m <method>  (SSLv2|SSLv3|SSLv23|TLSv1) defaults to SSLv23
  -c <days>    day threshold for critical
  -w <days>    day threshold for warning
  -n name      Check CN value is valid
"""

import getopt,sys
import __main__
from OpenSSL import SSL
import socket
import datetime

# On debian Based systems requires python-openssl

def get_options():
  "get options"

  options={'host':'',
           'port':'',
           'method':'SSLv23',
           'critical':5,
           'warning':15,
           'cn':''}

  try:
    opts, args = getopt.getopt(sys.argv[1:], "hH:p:m:c:w:n:", ['help', "host", 'port', 'method'])
  except getopt.GetoptError as err:
    # print help information and exit:
    print str(err) # will print something like "option -a not recognized"
    usage()
    sys.exit(2)
  for o, a in opts:
    if o in ("-h", "--help"):
      print __main__.__doc__
      sys.exit()
    elif o in ("-H", "--host"):
      options['host'] = a
      pass
    elif o in ("-p", "--port"):
      options['port'] = a
    elif o in ("-m", "--method"):
      options['method'] = a
    elif o == '-c':
      options['critical'] = int(a)
    elif o == '-w':
      options['warning'] = int(a)
    elif o == '-n':
      options['cn'] = a
    else:
      assert False, "unhandled option"

  if (''==options['host'] or 
      ''==options['port']):
    print __main__.__doc__
    sys.exit()

  if options['critical'] >= options['warning']:
    print "Critical must be smaller then warning"
    print __main__.__doc__
    sys.exit()

  return options

def main():
  options = get_options()

  # Initialize context
  if options['method']=='SSLv3':
    ctx = SSL.Context(SSL.SSLv3_METHOD)
  elif options['method']=='SSLv2':
    ctx = SSL.Context(SSL.SSLv2_METHOD)
  elif options['method']=='SSLv23':
    ctx = SSL.Context(SSL.SSLv23_METHOD)
  else:
    ctx = SSL.Context(SSL.TLSv1_METHOD)

  # Set up client
  sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
  sock.connect((options['host'], int(options['port'])))
  # Send an EOF
  try:
    sock.send("\x04")
    sock.shutdown()
    peer_cert=sock.get_peer_certificate()
    sock.close()
  except SSL.Error,e:
    print e

  exit_status=0
  exit_message=[]

  cur_date = datetime.datetime.utcnow()
  cert_nbefore = datetime.datetime.strptime(peer_cert.get_notBefore(),'%Y%m%d%H%M%SZ')
  cert_nafter = datetime.datetime.strptime(peer_cert.get_notAfter(),'%Y%m%d%H%M%SZ')

  expire_days = int((cert_nafter - cur_date).days)

  if cert_nbefore > cur_date:
    if exit_status < 2: 
      exit_status = 2
    exit_message.append('C: cert is not valid')
  elif expire_days < 0:
    if exit_status < 2: 
      exit_status = 2
    exit_message.append('Expire critical (expired)')
  elif options['critical'] > expire_days:
    if exit_status < 2: 
      exit_status = 2
    exit_message.append('Expire critical')
  elif options['warning'] > expire_days:
    if exit_status < 1: 
      exit_status = 1
    exit_message.append('Expire warning')
  else:
    exit_message.append('Expire OK')

  exit_message.append('['+str(expire_days)+'d]')

  for part in peer_cert.get_subject().get_components():
    if part[0]=='CN':
      cert_cn=part[1]

  if options['cn']!='' and options['cn'].lower()!=cert_cn.lower():
    if exit_status < 2:
      exit_status = 2
    exit_message.append(' - CN mismatch')
  else:
    exit_message.append(' - CN OK')

  exit_message.append(' - cn:'+cert_cn)

  print ''.join(exit_message)
  sys.exit(exit_status)

if __name__ == "__main__":
  main()

2

get_pem

Host: port'a bağlanın, sertifikayı sed ile çıkarın ve /tmp/host.port.pem adresine yazın.

get_expiration_date

Verilen pem dosyasını okuyun ve notAfter anahtarını bash değişkeni olarak değerlendirin. Ardından, belirli bir yerel ayarda dosya adını ve son kullanma tarihini yazdırın.

get_pem_expiration_dates

Bazı girdi dosyalarını yineleyin ve yukarıdaki işlevleri çalıştırın.

check.pems.sh

#!/bin/bash
get_pem () {
    openssl s_client -connect $1:$2 < /dev/null |& \
    sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/w'/tmp/$1.$2.pem
}
get_expiration_date () {
    local pemfile=$1 notAfter
    if [ -s $pemfile ]; then
        eval `
          openssl x509 -noout -enddate -in /tmp/$pemfile |
          sed -E 's/=(.*)/="\1"/'
        `
        printf "%40s: " $pemfile
        LC_ALL=ru_RU.utf-8 date -d "$notAfter" +%c
    else
        printf "%40s: %s\n" $pemfile '???'
    fi
}

get_pem_expiration_dates () {
    local pemfile server port
    while read host; do
        pemfile=${host/ /.}.pem
        server=${host% *}
        port=${host#* }
        if [ ! -f /tmp/$pemfile ]; then get_pem $server $port; fi
        if [   -f /tmp/$pemfile ]; then get_expiration_date $pemfile; fi
    done < ${1:-input.txt}
}

if [ -f "$1" ]; then
    get_pem_expiration_dates "$1" ; fi

örnek çıktı

 $ sh check.pems.sh input.txt
             www.google.com.443.pem: Пн. 30 дек. 2013 01:00:00
              superuser.com.443.pem: Чт. 13 марта 2014 13:00:00
               slashdot.org.443.pem: Сб. 24 мая 2014 00:49:50
          movielens.umn.edu.443.pem: ???
 $ cat input.txt
 www.google.com 443
 superuser.com 443
 slashdot.org 443
 movielens.umn.edu 443

Ve sorunuzu cevaplamak için:

$ openssl s_client -connect www.google.com:443 </dev/null |& \
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | \
openssl x509 -noout -enddate |& \
grep ^notAfter

Sunucu SNI kullanıyorsa, -servernameargümanı şu şekilde eklemeniz gerekir :openssl s_client -servername example.com -connect example.com:443
Flimm

1

Kabul edilen yanıtın, kalan gün sayısını gösteren tek satırlı bir sürümü:

( export DOMAIN=example.com; echo $(( ( $(date +%s -d "$( echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | sed 's/.*notAfter=\(.*\)$/\1/g' )" ) - $(date +%s) ) / 86400 )) )

Www.github.com ile örnek:

$ ( export DOMAIN=www.github.com; echo $(( ( $(date +%s -d "$( echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | sed 's/.*notAfter=\(.*\)$/\1/g' )" ) - $(date +%s) ) / 86400 )) )
210

beklenmedik belirteci `` dışa aktarma '' yakınında sözdizimi hatası alıyorum
user1130176

@ user1130176, ( ... )alt kabuk sözdizimi Bash'a özgü olabilir; Sanırım farklı bir kabuk kullanıyorsunuz?
Mathieu Rey

0

443 numaralı bağlantı noktasına sahip ana bilgisayar adlarının listesini hostname: bağlantı noktası biçiminde verin ve dosya adı olarak verin.

! / Bin / bash

filename = / root / kns / certs

tarih1 = $ (tarih | kes -d "" -f2,3,6)

currentDate = $ (tarih -d "$ tarih1" + "% Y% m% d")

-r satırı okurken

dcert = $ (echo | openssl s_client -servername $ line -connect $ line 2> / dev / null | openssl x509 -noout -dates | grep notAfter | cut -d = -f2)

echo Ana Makine Adı: $ line endDate = $ (tarih -d "$ dcert" + "% Y% m% d")

d1 = $ (tarih -d "$ endDate" +% s) d2 = $ (tarih -d "$ currentDate" +% s) echo Hostname: $ line - Kalan Gün $ (((d1 - d2) / 86400))

echo $ dcert yapıldı <"$ dosyaadı"

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.