Je peux connaître la date d'expiration des certificats SSL à l'aide de cette commande OpenSSL:
openssl x509 -noout -in <filename> -enddate
Mais si les certificats sont dispersés sur différents serveurs Web, comment trouvez-vous les dates d'expiration de tous ces certificats sur tous les serveurs?
Il semble y avoir un moyen de se connecter à un autre hôte, mais je ne sais pas comment obtenir la date d'expiration à l'aide de ceci:
openssl s_client -connect Host:port
J'ai eu le même problème et écrit ceci ... C'est rapide et sale, mais devrait fonctionner. Il enregistre (et affiche à l'écran avec le débogage activé) tous les certificats qui ne sont pas encore valides ou expirent dans les 90 prochains jours. Peut contenir des insectes, mais n'hésitez pas à le ranger.
#!/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
Si vous utilisez OS X, vous pouvez constater que la commande date
ne fonctionne pas correctement. Ceci est dû à différences entre les versions Unix et Linux de cet utilitaire. Le poste lié a des options pour faire ce travail.
Il suffit d’exécuter la commande ci-dessous pour obtenir la date d’expiration:
echo q | openssl s_client -connect google.com.br:443 | openssl x509 -noout -enddate
Vous pouvez utiliser cette commande dans un fichier de commandes pour rassembler ces informations pour d'autres serveurs distants.
Ci-dessous, mon script qui sert de contrôle à Nagios. Il se connecte à un hôte spécifique et vérifie que le certificat est valide dans les limites définies par les options -c/-w. Il peut vérifier que le CN du certificat correspond au nom que vous attendez.
Vous avez besoin de la bibliothèque Python openssl et j'ai effectué tous les tests avec Python 2.7.
Il serait trivial qu'un script Shell appelle cela plusieurs fois. Le script renvoie les valeurs de sortie nagios standard pour les états critic/warning/ok.
Une simple vérification du certificat de Google peut être effectuée comme ceci.
./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 __== "__main__":
main()
Connectez-vous à l'hôte: port, extrayez le certificat avec sed et écrivez-le dans /tmp/Host.port.pem.
Lisez le fichier pem indiqué et évaluez la clé notAfter en tant que variable bash. Ensuite, imprimez le nom du fichier et la date à laquelle il expire dans une langue donnée.
Itérer un fichier d'entrée et exécuter les fonctions ci-dessus.
#!/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
$ 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
Et pour répondre à votre question:
$ openssl s_client -connect www.google.com:443 </dev/null |& \
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | \
openssl x509 -noout -enddate |& \
grep ^notAfter
Voici une version à une ligne de la réponse acceptée, qui ne fournit que le nombre de jours restants:
( 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 )) )
Exemple avec 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
Donne la liste des noms d’hôtes avec le port 443 au format nomhôte: port dans le fichier et donne le nom du fichier.
nom de fichier =/root/kns/certs
date1 = $ (date | cut -d "" -f2,3,6)
currentDate = $ (date -d "$ date1" + "% Y% m% d")
en lecture -r ligne do
dcert = $ (echo | openssl s_client -servername $ ligne -connect $ ligne 2>/dev/null | openssl x509 -noout -dates | grep notAfter | cut -d = -f2)
echo Nom d'hôte: $ line endDate = $ (date -d "$ dcert" + "% Y% m% d")
d1 = $ (date -d "$ endDate" +% s) d2 = $ (date -d "$ currentDate" +% s) echo Nom d'hôte: $ line - Jours restants $ (((d1 - d2)/86400))
echo $ dcert done <"$ filename"