답변:
나는 같은 문제가 있었고 이것을 썼다. 그것은 빠르고 더럽지 만 작동해야한다. 다음 90 일 동안 유효하지 않거나 만료 된 인증서는 기록하고 디버깅을 사용하여 화면에 인쇄합니다. 몇 가지 버그가 포함되어있을 수 있지만 자유롭게 정리하십시오.
#!/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에서 사용하는 경우 date
명령이 올바르게 작동하지 않을 수 있습니다. 이것은 이 유틸리티의 Unix 및 Linux 버전의 차이 로 인한 것 입니다. 링크 된 게시물에는이 작업을 수행하기위한 옵션이 있습니다.
-servername
과 같은 인수 를 포함해야합니다 .openssl s_client -servername example.com -connect example.com:443
아래 명령을 실행하면 만료 날짜가 제공됩니다.
echo q | openssl s_client -connect google.com.br:443 | openssl x509 -noout -enddate
이 명령을 배치 파일로 사용하여 더 많은 원격 서버에 대한 정보를 수집 할 수 있습니다.
-servername
경우 다음과 같은 인수 를 사용해야합니다 .openssl s_client -servername google.com.br -connect google.com.br:443
아래는 nagios 내에서 확인하는 스크립트입니다. 특정 호스트에 연결하고 -c / -w 옵션으로 설정된 임계 값 내에서 인증서가 유효한지 확인합니다. 인증서의 CN이 예상 이름과 일치하는지 확인할 수 있습니다.
파이썬 openssl 라이브러리가 필요합니다. 파이썬 2.7로 모든 테스트를 수행했습니다.
쉘 스크립트를 여러 번 호출하는 것은 쉽지 않습니다. 스크립트는 위험 / 경고 / 확인 상태에 대한 표준 nagios 종료 값을 반환합니다.
이렇게하면 Google 인증서를 간단하게 확인할 수 있습니다.
./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()
host : port에 연결하고 sed로 인증서를 추출하여 /tmp/host.port.pem에 씁니다.
주어진 pem 파일을 읽고 notAfter 키를 bash 변수로 평가하십시오. 그런 다음 파일 이름과 지정된 로케일에서 만료되는 날짜를 인쇄하십시오.
일부 입력 파일을 반복하고 위의 기능을 실행하십시오.
#!/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
그리고 당신의 질문에 대답하기 위해 :
$ openssl s_client -connect www.google.com:443 </dev/null |& \
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | \
openssl x509 -noout -enddate |& \
grep ^notAfter
-servername
과 같은 인수 를 포함해야합니다 .openssl s_client -servername example.com -connect example.com:443
다음은 허용 된 답변의 한 줄짜리 버전으로 나머지 일 수를 출력합니다.
( 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의 예 :
$ ( 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
( ... )
서브 쉘 구문은 Bash에만 해당 될 수 있습니다. 다른 쉘을 사용하고 있다고 생각하십니까?
파일에서 hostname : port 형식으로 포트 443이있는 호스트 이름 목록을 제공하고 파일 이름으로 지정하십시오.
filename = / root / kns / certs
date1 = $ (날짜 | cut -d ""-f2,3,6)
currentDate = $ (날짜 -d "$ date1"+ "% Y % m % d")
-r 행을 읽는 동안
dcert = $ (echo | openssl s_client-서버 이름 $ line -connect $ line 2> / dev / null | openssl x509 -noout -dates | grep notAfter | cut -d = -f2)
echo 호스트 이름 : $ line endDate = $ (날짜 -d "$ dcert"+ "% Y % m % d")
d1 = $ (date -d "$ endDate"+ % s) d2 = $ (date -d "$ currentDate"+ % s) echo 호스트 이름 : $ line-남은 일수 $ ((((d1-d2) / 86400))
echo $ dcert done < "$ filename"