Verifique a data de expiração do certificado ssl para vários servidores remotos

17

Eu posso descobrir a data de expiração dos certificados SSL usando este comando OpenSSL:

openssl x509 -noout -in <filename> -enddate

Mas se os certificados estiverem espalhados em diferentes servidores da Web, como você encontra as datas de expiração de todos esses certificados em todos os servidores?

Parece haver uma maneira de se conectar a outro host, mas não sei como obter a data de expiração usando:

openssl s_client -connect host:port
    
por user32262 11.07.2013 / 12:17

6 respostas

15

Eu tive o mesmo problema e escrevi isso ... É rápido e sujo, mas deve funcionar. Ele registrará (e imprimirá na tela com depuração) quaisquer certificados que ainda não sejam válidos ou expirem nos próximos 90 dias. Pode conter alguns bugs, mas fique à vontade para arrumar tudo.

#!/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.*//g')
  end_date=$(echo $output | sed 's/.*notAfter=\(.*\)$//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

Se estiver usando o OS X, você verá que o comando date não funciona corretamente. Isso se deve às diferenças na versão Unix e Linux desse utilitário. O post vinculado tem opções para fazer isso funcionar.

    
por 15.07.2013 / 22:32
11

Basta executar o comando abaixo e ele fornecerá a data de expiração:

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

Você pode usar esse comando em um arquivo em lotes para reunir essas informações para mais servidores remotos.

    
por 27.05.2015 / 17:56
6

Abaixo está meu script que como um cheque dentro nagios. Ele se conecta a um host específico, ele verifica se o certificado é válido dentro de um limite definido pelas opções -c / -w. Pode verificar se o CN do certificado corresponde ao nome esperado.

Você precisa da biblioteca openssl do python e fiz todos os testes com o python 2.7.

Seria trivial ter um script de shell chamado várias vezes. O script retorna os valores de saída padrão do nagio para o status crítico / aviso / ok.

Uma simples verificação do certificado do Google pode ser realizada assim.

./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()
    
por 15.07.2013 / 22:46
2

get_pem

Conecte-se ao host: port, extraia o certificado com sed e grave-o em /tmp/host.port.pem.

get_expiration_date

Leia o arquivo pem fornecido e avalie a chave notAfter como uma variável bash. Em seguida, imprima o nome do arquivo e a data em que ele expira em uma determinada localidade.

get_pem_expiration_dates

Iterar algum arquivo de entrada e executar as funções acima.

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/=(.*)/=""/'
        '
        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

exemplo de saída

 $ 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

E para responder à sua pergunta:

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

Aqui está uma versão de uma linha da resposta aceita, que mostra apenas o número restante de dias:

( 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=\(.*\)$//g' )" ) - $(date +%s) ) / 86400 )) )

Exemplo com 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=\(.*\)$//g' )" ) - $(date +%s) ) / 86400 )) )
210
    
por 19.10.2017 / 04:43
0

Forneça uma lista de nomes de host com a porta 443 no formato hostname: port no arquivo e forneça-o como nome de arquivo.

! / bin / bash

nome do arquivo = / root / kns / certs

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

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

enquanto lê a linha -r fazer

dcert = $ (echo | openssl s_client -nome do servidor $ line -connect $ linha 2 > / dev / null | openssl x 509 -noout -dates | grep notAfter | cut -d = -f2)

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

d1 = $ (date -d "$ endDate" +% s) d2 = $ (date -d "$ currentDate" +% s) echo Nome do host: $ line - Dias restantes $ (((d1 - d2) / 86400))

echo $ dcert feito < "$ filename"

    
por 23.06.2017 / 06:52