Periksa tanggal kedaluwarsa sertifikat ssl untuk beberapa server jarak jauh

18

Saya dapat mengetahui tanggal kedaluwarsa sertifikat ssl menggunakan perintah OpenSSL ini:

openssl x509 -noout -in <filename> -enddate

Tetapi jika sertifikat tersebar di server web yang berbeda, bagaimana Anda menemukan tanggal kedaluwarsa dari semua sertifikat ini di semua server?

Tampaknya ada cara untuk terhubung ke host lain, tapi saya tidak yakin bagaimana cara mendapatkan tanggal kedaluwarsa menggunakan ini:

openssl s_client -connect host:port
pengguna32262
sumber

Jawaban:

15

Saya memiliki masalah yang sama dan menulis ini ... Cepat dan kotor, tetapi harus berhasil. Ini akan mencatat (dan mencetak ke layar dengan debugging aktif) semua sertifikat yang belum valid atau berakhir dalam 90 hari ke depan. Mungkin mengandung beberapa bug, tetapi merasa bebas untuk membereskannya.

#!/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

Jika menggunakan OS X, Anda mungkin menemukan bahwa dateperintah tidak berfungsi dengan benar. Ini karena perbedaan dalam versi Unix dan Linux dari utilitas ini. Posting yang ditautkan memiliki opsi untuk membuat ini berfungsi.

Sirex
sumber
Saya sedikit memodifikasi / memperluas skrip Anda untuk dapat memeriksa sertifikat server surat serta memberikan gambaran umum tentang status semua sertifikat. Anda dapat menemukan skrip yang dimodifikasi di: gist.github.com/lkiesow/c9c5d96ecb71822b82cd9d194c581cc8
Lars Kiesow
1
Jika server menggunakan SNI, Anda harus menyertakan -servernameargumen, seperti ini:openssl s_client -servername example.com -connect example.com:443
Flimm
11

Cukup jalankan perintah di bawah ini dan itu akan memberikan tanggal kedaluwarsa:

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

Anda dapat menggunakan perintah ini ke file batch, untuk mengumpulkan informasi ini untuk server yang lebih jauh.

Vinícius
sumber
2
Cara Anda menulis ini, Anda harus menekan CTRL-C untuk mengakhirinya. Anda dapat memperbaikinya dengan: openssl s_client -connect google.com.br:443 </ dev / null 2> & 1 | openssl x509 -tidak -tanggal -tanggal.
numberwhun
1
Jika server menggunakan SNI, Anda perlu menggunakan -servernameargumen, seperti ini:openssl s_client -servername google.com.br -connect google.com.br:443
Flimm
6

Di bawah ini adalah skrip saya yang sebagai cek dalam nagios. Terhubung ke host tertentu, itu memverifikasi bahwa sertifikat itu valid dalam ambang batas yang ditetapkan oleh opsi -c / -w. Itu dapat memeriksa bahwa CN sertifikat cocok dengan nama yang Anda harapkan.

Anda perlu python openssl library, dan saya melakukan semua pengujian dengan python 2.7.

Akan sepele untuk memiliki panggilan skrip shell ini beberapa kali. Skrip mengembalikan nilai keluar nagios standar untuk status kritis / peringatan / ok.

Pemeriksaan sederhana atas sertifikat Google dapat dilakukan seperti ini.

./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()
Sakit kepala
sumber
2

get_pem

Hubungkan ke host: port, ekstrak sertifikat dengan sed dan tulis ke /tmp/host.port.pem.

get_expiration_date

Baca file pem yang diberikan dan evaluasi kunci notAfter sebagai variabel bash. Kemudian cetak nama file dan tanggal kapan berakhir di lokasi tertentu.

get_pem_expiration_dates

Iterasi beberapa file input dan jalankan fungsi di atas.

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

output sampel

 $ 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

Dan untuk menjawab pertanyaan Anda:

$ openssl s_client -connect www.google.com:443 </dev/null |& \
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | \
openssl x509 -noout -enddate |& \
grep ^notAfter
Ярослав Рахматуллин
sumber
Jika server menggunakan SNI, Anda harus menyertakan -servernameargumen, seperti ini:openssl s_client -servername example.com -connect example.com:443
Flimm
1

Berikut ini adalah versi satu-baris dari jawaban yang diterima, yang hanya menampilkan sisa jumlah hari:

( 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 )) )

Contoh dengan www.github.com:

$ ( 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
Mathieu Rey
sumber
saya mendapatkan kesalahan sintaks dekat token yang tidak terduga `export '
user1130176
@ user1130176 ( ... )sintaks subshell mungkin spesifik untuk Bash; Saya kira Anda menggunakan shell yang berbeda?
Mathieu Rey
0

Berikan daftar nama host dengan port 443 dalam format nama host: port dalam file dan berikan sebagai nama file.

! / bin / bash

nama file = / root / kns / certs

date1 = $ (date | cut -d "" -f2,3,6)

currentDate = $ (date -d "$ date1" + "% Y% m% d")

saat membaca -r line do

dcert = $ (echo | openssl s_client -servername $ line -connect $ line 2> / dev / null | openssl x509 -tidak ada-tanggal | grep notSetelah | cut -d = -f2)

echo Hostname: $ line endDate = $ (date -d "$ dcert" + "% Y% m% d")

d1 = $ (date -d "$ endDate" +% s) d2 = $ (date -d "$ currentDate" +% s) echo Nama Host: $ line - Sisa Sisa $ ((d1 - d2) / 86400))

echo $ dcert selesai <"$ filename"

Karthik
sumber