Kiểm tra ngày hết hạn của chứng chỉ ssl cho nhiều máy chủ từ xa


18

Tôi có thể tìm ra ngày hết hạn của chứng chỉ ssl bằng lệnh OpenSSL này:

openssl x509 -noout -in <filename> -enddate

Nhưng nếu các chứng chỉ nằm rải rác trên các máy chủ web khác nhau, làm thế nào để bạn tìm thấy ngày hết hạn của tất cả các chứng chỉ này trên tất cả các máy chủ?

Dường như có một cách để kết nối với máy chủ khác, nhưng tôi không chắc làm thế nào để hết hạn sử dụng bằng cách này:

openssl s_client -connect host:port

Câu trả lời:


15

Tôi có cùng một vấn đề và đã viết điều này ... Nó nhanh và bẩn, nhưng nên hoạt động. Nó sẽ đăng nhập (và in ra màn hình với gỡ lỗi) bất kỳ certs nào chưa hợp lệ hoặc hết hạn trong 90 ngày tới. Có thể chứa một số lỗi, nhưng hãy thoải mái dọn dẹp nó.

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

Nếu sử dụng trên OS X, bạn có thể thấy rằng datelệnh không hoạt động chính xác. Điều này là do sự khác biệt trong phiên bản Unix và Linux của tiện ích này. Các bài đăng được liên kết có các tùy chọn để làm cho công việc này.


Tôi đã sửa đổi / mở rộng tập lệnh của bạn một chút để có thể kiểm tra chứng chỉ máy chủ thư cũng như cung cấp một cái nhìn tổng quan tốt đẹp về tình trạng của tất cả các chứng chỉ. Bạn có thể tìm thấy tập lệnh đã sửa đổi tại: gist.github.com/lkiesow/c9c5d96ecb71822b82cd9d194c581cc8
Lars Kiesow 30/03/2016

1
Nếu máy chủ đang sử dụng SNI, bạn cần bao gồm -servernameđối số, như sau:openssl s_client -servername example.com -connect example.com:443
Flimm

11

Chỉ cần chạy lệnh dưới đây và nó sẽ cung cấp ngày hết hạn:

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

Bạn có thể sử dụng lệnh này thành một tệp bó, để thu thập thông tin này cho các máy chủ từ xa hơn.


2
Cách bạn viết bài này, bạn phải nhấn CTRL-C để kết thúc nó. Bạn có thể khắc phục điều đó bằng: openssl s_client -connect google.com.br:443 </ dev / null 2> & 1 | openssl x509 -noout -enddate Chỉ là một ý nghĩ.
số

1
Nếu máy chủ sử dụng SNI, bạn cần sử dụng -servernameđối số, như thế này:openssl s_client -servername google.com.br -connect google.com.br:443
Flimm

6

Dưới đây là kịch bản của tôi như là một kiểm tra trong nagios. Nó kết nối với một máy chủ cụ thể, nó xác minh rằng chứng chỉ hợp lệ trong một ngưỡng được đặt bởi các tùy chọn -c / -w. Nó có thể kiểm tra xem CN của chứng chỉ có khớp với tên bạn mong đợi không.

Bạn cần thư viện opensthon python và tôi đã thực hiện tất cả các thử nghiệm với python 2.7.

Sẽ là tầm thường khi có một kịch bản shell gọi điều này nhiều lần. Kịch bản trả về các giá trị thoát nagios tiêu chuẩn cho trạng thái quan trọng / cảnh báo / ok.

Việc kiểm tra đơn giản chứng chỉ của Google có thể được thực hiện như thế này.

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

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

check_ssl_cert ve

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

nhận được

Kết nối với máy chủ: cổng, trích xuất chứng chỉ bằng sed và ghi nó vào /tmp/host.port.pem.

get_Exption_date

Đọc tệp pem đã cho và đánh giá khóa notAfter là biến bash. Sau đó in tên tệp và ngày hết hạn trong một miền địa phương nhất định.

get_pem_Exption_dates

Lặp lại một số tệp đầu vào và chạy các chức năng trê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

đầu ra mẫu

 $ 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

Và để trả lời câu hỏi của bạn:

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

Nếu máy chủ đang sử dụng SNI, bạn cần bao gồm -servernameđối số, như sau:openssl s_client -servername example.com -connect example.com:443
Flimm 8/12/2016

1

Đây là phiên bản một câu trả lời được chấp nhận, chỉ xuất ra số ngày còn lại:

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

Ví dụ với 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

tôi nhận được lỗi cú pháp gần mã thông báo bất ngờ `export '
user1130176

@ user1130176 ( ... )cú pháp subshell có thể dành riêng cho Bash; Tôi đoán bạn đang sử dụng một vỏ khác nhau?
Mathieu Rey

0

Đưa ra danh sách tên máy chủ có cổng 443 ở định dạng tên máy chủ: cổng trong tệp và đặt tên đó là tên tệp.

! / thùng / bash

tên tệp = / root / kns / certs

ngày1 = $ (ngày | cắt -d "" -f2,3,6)

currentDate = $ (ngày -d "$ date1" + "% Y% m% d")

trong khi đọc dòng -r làm

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

echo Tên máy chủ: $ line endDate = $ (ngày -d "$ dcert" + "% Y% m% d")

d1 = $ (ngày -d "$ endDate" +% s) d2 = $ (ngày -d "$ currentDate" +% s) echo Tên máy chủ: $ line - Số ngày còn lại $ (((d1 - d2) / 86400))

echo $ dcert xong <"$ tên tệp"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.