class TLSChecker::CertificateChecker

Attributes

address[R]
hostname[R]
port[R]
starttls[R]

Public Class Methods

new(hostname, address, port, starttls) click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 9
def initialize(hostname, address, port, starttls)
  @hostname = hostname
  @address = address
  @port = port
  @starttls = starttls

  @certificate = nil
  @certificate_failure = nil
  @tls_socket = nil
end

Public Instance Methods

certificate() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 49
def certificate
  @certificate = OpenSSL::X509::Certificate.new(tls_socket.peer_cert) if @certificate.nil?
  @certificate
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ETIMEDOUT, SocketRecvTimeout => e
  @certificate_failure = e.message
  @certificate = false
end
check() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 41
def check
  !!certificate
end
humanized_address() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 61
def humanized_address
  if @address.is_a?(Resolv::IPv6)
    "[#{@address}]"
  else
    @address.to_s
  end
end
service() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 57
def service
  "X.509/#{hostname}/#{humanized_address}:#{port}"
end
to_e() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 22
def to_e
  if certificate
    InternetSecurityEvent::TLSStatus.build(hostname, certificate)
  else
    {
      state:       'critical',
      description: @certificate_failure || "#{hostname} does not have a valid certificate",
    }
  end.merge(
    service:  service,
    af:       af,
    hostname: hostname,
    address:  address.to_s,
    port:     port,
    ttl:      12.hours.to_i,
    tags:     ['tls-checker'],
  )
end
to_s() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 45
def to_s
  description
end

Private Instance Methods

af() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 71
def af
  case @address
  when Resolv::IPv4
    'inet'
  when Resolv::IPv6
    'inet6'
  end
end
imap_tls_socket() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 99
def imap_tls_socket
  socket = LineOrientedSocket.new(@address.to_s, port)
  socket.gets_until_match(/^\* OK/)
  socket.puts('. CAPABILITY')
  socket.gets_until_match(/^\. OK/)
  socket.puts('. STARTTLS')
  socket.gets_until_match(/^\. OK/)

  tls_handshake(socket)
end
ldap_tls_socket() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 110
def ldap_tls_socket
  socket = TCPSocket.new(@address.to_s, port)
  socket.write(['301d02010177188016312e332e362e312e342e312e313436362e3230303337'].pack('H*'))
  expected_res = ['300c02010178070a010004000400'].pack('H*')
  res = socket.read(expected_res.length)

  return nil unless res == expected_res

  tls_handshake(socket)
end
my_hostname() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 143
def my_hostname
  Socket.gethostbyname(Socket.gethostname).first
rescue SocketError
  Socket.gethostname
end
raw_tls_socket() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 93
def raw_tls_socket
  socket = TCPSocket.new(@address.to_s, port)

  tls_handshake(socket)
end
smtp_tls_socket() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 121
def smtp_tls_socket
  socket = LineOrientedSocket.new(@address.to_s, port)
  socket.gets_until_match(/^220 /)
  socket.puts("EHLO #{my_hostname}")
  socket.gets_until_match(/^250 /)
  socket.puts('STARTTLS')
  socket.gets

  tls_handshake(socket)
end
ssl_context() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 149
def ssl_context
  ssl_context = OpenSSL::SSL::SSLContext.new
  # We do not care about trust here, only expiration dates.

  #  ____              _ _                                                 _
  # |  _ \  ___  _ __ ( ) |_    ___ ___  _ __  _   _       _ __   __ _ ___| |_ ___
  # | | | |/ _ \| '_ \|/| __|  / __/ _ \| '_ \| | | |_____| '_ \ / _` / __| __/ _ \
  # | |_| | (_) | | | | | |_  | (_| (_) | |_) | |_| |_____| |_) | (_| \__ \ ||  __/
  # |____/ \___/|_| |_|  \__|  \___\___/| .__/ \__, |     | .__/ \__,_|___/\__\___|
  #                                     |_|    |___/      |_|
  #  _   _     _     _
  # | |_| |__ (_)___| |
  # | __| '_ \| / __| |
  # | |_| | | | \__ \_|
  #  \__|_| |_|_|___(_)
  #
  # YOU SHALL  NOT  "COPY-PASTE"  THE FOLLOWING LINE  IN YOUR CODE.  IF YOU
  # UNDESTAND WHY WE DO TAHT,  YOU KNOW WHY YOU DON'T WANT TO DO THIS.   IF
  # YOU DO NOT UNDERSTAND WHAT IT DOES,  REALIZE THAT YOUR PROBLEM VANISHED
  # WHEN YOU PASTE IT AND SHIP IT, FEL FREE TO GET BACK TO ME WHEN YOU WILL
  # DISCOVER THAT YOU HAVE WAY  MORE PROBLEMS  THAN YOU THOUGH.   I WILL BE
  # PLEASED TO  EXCHANGE MONEY WITH ADVICES AND ASSISTANCE  FOR FIXING YOUR
  # PROBLEMS.
  ssl_context.set_params(tls_options)
  ssl_context
end
tls_handshake(raw_socket) click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 132
def tls_handshake(raw_socket)
  tls_socket = OpenSSL::SSL::SSLSocket.new(raw_socket, ssl_context)
  tls_socket.hostname = hostname
  begin
    tls_socket.connect
  rescue OpenSSL::SSL::SSLError # rubocop:disable Lint/HandleExceptions
    # This may fail for example if a client certificate is required
  end
  tls_socket
end
tls_options() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 176
def tls_options
  {
    verify_mode: OpenSSL::SSL::VERIFY_NONE,
  }
end
tls_socket() click to toggle source
# File lib/tls-checker/certificate_checker.rb, line 80
def tls_socket
  @tls_socket ||= case starttls
                  when :smtp
                    smtp_tls_socket
                  when :imap
                    imap_tls_socket
                  when :ldap
                    ldap_tls_socket
                  else
                    raw_tls_socket
                  end
end