Skip to content

Instantly share code, notes, and snippets.

@xiejiangzhi
Last active November 27, 2018 09:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xiejiangzhi/bf43d02bcb210a057a6dc47ace9932b5 to your computer and use it in GitHub Desktop.
Save xiejiangzhi/bf43d02bcb210a057a6dc47ace9932b5 to your computer and use it in GitHub Desktop.
a ruby https(tls/ssl) cert checker
#! /usr/bin/env ruby
# Usage:
# tls_cert_checker xjz.pw aliyun.com yourdomain.com
# tls_cert_checker /path/to/your_hosts
# hosts_file: split hosts with new line "\n", ignore empty line
require 'socket'
require 'openssl'
hosts = if ARGV.size == 1 && File.exists?(ARGV.first)
File.readlines(ARGV.first).map(&:strip).select { |host| host != '' }
else
ARGV
end
$timeout = 5
def get_cert(host)
tcp_client = Socket.tcp(host, 443, connect_timeout: $timeout)
ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client)
ssl_client.hostname = host
begin
ssl_client.connect_nonblock
rescue IO::WaitReadable
retry if IO.select([ssl_client], nil, nil, $timeout)
rescue IO::WaitWritable
retry if IO.select(nil, [ssl_client], nil, $timeout)
end
[ssl_client.peer_cert, ssl_client.peer_cert_chain].tap do
ssl_client.close
tcp_client.close
end
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ETIMEDOUT
nil
end
$format = '%-13s %-40s %-20s %s (%s)'
$cert_store = OpenSSL::X509::Store.new
$cert_store.set_default_paths
def show_cert(host, cert, cert_chain)
dns = cert.extensions.find { |e| e.value =~ /^DNS/ }.value.split(',').map { |d| d.split(':').last }
issuer = cert.issuer.to_a.find { |name, data, type| name == 'O' }[1]
expired_at = cert.not_after
valid_days = '%3s days' % ((cert.not_after - Time.now) / (3600.0 * 24)).floor
status_str = if expired_at <= Time.now
'[expired]'
elsif expired_at <= Time.now + 15 * 24 * 3600
'[< 15 day]'
elsif expired_at <= Time.now + 30 * 24 * 3600
'[< 30 day]'
else
'[ok]'
end
status_str = '[not-match]' if dns.all? { |d| !(Regexp.new('\A' + d.gsub('*', '.+') + '\z') =~ host) }
status_str = '[err-chain]' unless $cert_store.verify(cert, cert_chain)
times = cert.not_after.strftime("%F")
puts $format % [status_str, host, issuer, times, valid_days]
end
hosts.each do |host|
begin
cert, cert_chain = get_cert(host)
if cert
show_cert(host, cert, cert_chain)
else
puts $format % ['[failed]', host, nil, nil, nil]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment