For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

SSL Certificate Report

Problem this snippet solves:

This is a script will generate a report of all or some of the certificates managed by a BIG-IP. It will output a multitude of information including: subject and issuers properties, serial, public key bit-length, and expiration dates.

How to use this snippet:

Requirements

Installation Steps

  1. Install Ruby, Ruby's OpenSSL library, Ruby Gems, and the Ruby iControl libraries
  2. Copy this code to /usr/local/bin and chmod +x to make the script executable
  3. Run it!
    • ssl-certificate-report.rb -b 192.168.1.245 -u admin -p admin -v

Code :

#!/usr/bin/ruby

require "rubygems"
require "f5-icontrol"
require "getoptlong"

options = GetoptLong.new(
  [ "--bigip-address",  "-b", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--bigip-user",     "-u", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--bigip-pass",     "-p", GetoptLong::REQUIRED_ARGUMENT ],
  [ "--cert-name",      "-n", GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--cert-list",      "-l", GetoptLong::NO_ARGUMENT ],
  [ "--watermark-days", "-d", GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--no-color",       "-c", GetoptLong::NO_ARGUMENT ],
  [ "--verbose",        "-v", GetoptLong::OPTIONAL_ARGUMENT ],
  [ "--help",           "-h", GetoptLong::NO_ARGUMENT ]
)

def usage
  puts $0 + " -b  -u "
  puts "  -b (--bigip-address)   BIG-IP management-accessible address"
  puts "  -u (--bigip-user)      BIG-IP username"
  puts "  -p (--bigip-pass)      BIG-IP password (will prompt if left blank"
  puts "  -n (--cert-name)       name of certificate to display (display all by default)"
  puts "  -l (--cert-list)       list of certificates managed by the BIG-IP"
  puts "  -d (--watermark-days)  certificates expiring inside this number of days will"
  puts "                             be marked as \"expiring soon\", default is 30 days"
  puts "  -c (--no-color)        disable color coding for the shell (useful if piping"
  puts "                             output to less or are using Windows)"
  puts "  -v (--verbose)         show all certificate information (brief by default)"
  puts "  -h (--help)            shows this help/usage dialog"

  exit
end

# initial parameter values

bigip_address = ''
bigip_user = ''
bigip_pass = ''
cert_name = ''
cert_list = false
watermark_days = 30
verbose = false

# the color_code variable must be global to be read within the format_text method

$color_code = true

options.each do |option, arg|
  case option
    when "--bigip-address"
      bigip_address = arg
    when "--bigip-user"
      bigip_user = arg
    when "--bigip-pass"
      bigip_pass = arg
    when "--cert-name"
      cert_name = arg
    when "--cert-list"
      cert_list = true
    when "--watermark-days"
      arg = arg.to_i
      watermark_days = arg
    when "--no-color"
      $color_code = false
    when "--verbose"
      verbose = true
    when "--help"
      usage
  end
end

usage if bigip_address.empty? or bigip_user.empty?

if bigip_pass.empty?
  puts "Please enter the BIG-IPs password..."
  print "Password: "
  system("stty", "-echo")
  bigip_pass = gets.chomp
  system("stty", "echo")
end

bigip = F5::IControl.new(bigip_address, bigip_user, bigip_pass, ["Management.KeyCertificate"]).get_interfaces

def format_text(text, code)
  if $color_code
    "#{code}#{text}\e[00m"
  else
    text
  end
end

def red(text)
  format_text(text, "\e[01;31m")
end

def green(text)
  format_text(text, "\e[01;32m")
end

def yellow(text)
  format_text(text, "\e[01;33m")
end

def bold(text)
  format_text(text, "\e[1m")
end

def underline(text)
  format_text(text, "\e[4m")
end

def bold_underline(text)
  format_text(text, "\e[4;1m")
end

def cert_validity_label(expire_text)
  case expire_text
    when "VTYPE_CERTIFICATE_VALID"
      green("valid")
    when "VTYPE_CERTIFICATE_EXPIRED"
      red("expired")
    when "VTYPE_CERTIFICATE_WILL_EXPIRE"
      yellow("expiring soon")
    when "VTYPE_CERTIFICATE_INVALID"
      red("invalid")
    else
      yellow("unknown")
  end
end

def key_strength_label(key_length)
  case(key_length)
    when 0..1023
      red("low")
    when 1024..2047
      yellow("medium")
    else
      green("strong")
  end
end

def key_type_label(key_type)
  case key_type
    when "KTYPE_RSA_PRIVATE"
      "RSA private key"
    when "KTYPE_RSA_PUBLIC"
      "RSA public key"
    when "KTYPE_DSA_PRIVATE"
      "DSA private key"
    when "KTYPE_DSA_PUBLIC"
      "DSA public key"
    else
      "Unkown"
  end
end

# collect certificate properties

certs = {}

bigip["Management.KeyCertificate"].get_certificate_list("MANAGEMENT_MODE_DEFAULT").each do |cert|
  id = cert['certificate']['cert_info']['id']

  certs[id] = {}

  # general properties

  certs[id]['expires'] = Time.at(cert['certificate']['expiration_date']).strftime("%b %e, %Y")
  certs[id]['version'] = cert['certificate']['version']
  certs[id]['serial'] = cert['certificate']['serial_number']
  certs[id]['serial'] = 'unavailable' if certs[id]['serial'].empty?

  # subject and issuers properties

  ['subject', 'issuer'].each do |x|
    certs[id][x] = {}
    ['common_name', 'organization_name', 'division_name', 'locality_name', 'state_name', 'country_name'].each do |y|

      certs[id][x][y] = cert['certificate'][x][y]
    end
  end

  certs[id]['key'] = {}
  certs[id]['key']['length'] = cert['certificate']['bit_length']
  certs[id]['key']['length_text'] = key_strength_label(cert['certificate']['bit_length'].to_i)
  certs[id]['key']['type'] = key_type_label(cert['certificate']['key_type'])
end

# collect certificate validity information

validity_states = bigip["Management.KeyCertificate"].certificate_check_validity('MANAGEMENT_MODE_DEFAULT', certs.keys, ([watermark_days] * certs.keys.size))

x = 0

certs.each do |id,cert|
  cert['expire_text'] = cert_validity_label(validity_states[x])
  x += 1
end

# display BIG-IP information

puts bold("\nConnected to BIG-IP at #{bigip_address} with user '#{bigip_user}'...")

# if user only wants list, display it and exit

if cert_list
  puts
  puts underline("Available certificates\n")
  puts certs.keys.sort.collect { |id| id = "  " + id }
  puts
  exit
end

unless cert_name.empty?
  if certs.key? cert_name
    certs = { cert_name => certs[cert_name] }
  else
    puts "Error: could not locate a certificate by that name, try '-l' for a list"
    exit
  end
end

puts bold_underline(" " * 80)
puts

certs.keys.sort.each do |id|
  puts underline("General Properties")
  puts "\t" + bold("Name: ") + id
  puts "\t" + bold("Certificate Subject(s): ") + certs[id]['subject']['common_name'] + ", " + certs[id]['subject']['organization_name'] + "\n\n"

  puts underline("Certificate Properties")
  puts "\t" + bold("Expires: \t") + certs[id]['expires'] + " (" + certs[id]['expire_text'] + ")"

  puts "\t" + bold("Version: \t") + certs[id]['version'].to_s
  puts "\t" + bold("Serial: \t") + certs[id]['serial']

  if verbose
    ['subject', 'issuer'].each do |section|
      puts "\t" + bold(section.capitalize) + ":"

      subsections = { 'Common Name' => 'common_name', \
        'Organization' => 'organization_name', \
        'Division' => 'division_name', \
        'Locality' => 'locality_name', \
        'State (Prov)' => 'state_name', \
        'Country' => 'country_name' } \

      subsections.each do |key, subsection|
        puts "\t\t\t" + key + ": \t" + certs[id][section][subsection] unless certs[id][section][subsection].empty?
      end
    end

    puts
  end

  puts underline("Public Key Properties")
  puts "\t" + bold("Key Type: ") + certs[id]['key']['type']
  puts "\t" + bold("Size: ") + certs[id]['key']['length'].to_s + " (" + certs[id]['key']['length_text'] + ")"

  puts
  puts bold_underline(" " * 80)
  puts
end
Published Mar 09, 2015
Version 1.0
No CommentsBe the first to comment