sensu-plugins/sensu-plugins-network-checks

View on GitHub
bin/check-ports.rb

Summary

Maintainability
A
55 mins
Test Coverage
#! /usr/bin/env ruby
# frozen_string_literal: true

#
#   check-ports
#
# DESCRIPTION:
# Connect to a TCP/UDP port on one or more ports, to see if open.   Don't use nmap since it's overkill.
#
# OUTPUT:
#   plain text
#
# PLATFORMS:
#   Linux
#
# DEPENDENCIES:
#   gem: sensu-plugin
#
# USAGE:
#
# Ports are comma separated and support ranges
# ./check-ports.rb -H localhost -p 22,25,8100-8131,3030 -P tcp
#
# NOTES:
# By default, checks for openssh on localhost port 22
#
#
# LICENSE:
#   Released under the same terms as Sensu (the MIT license); see LICENSE
#   for details.

require 'sensu-plugin/check/cli'
require 'socket'
require 'timeout'

#
# Check Banner
#
class CheckPort < Sensu::Plugin::Check::CLI
  option :hosts,
         short: '-H HOSTNAME',
         long: '--hostname HOSTNAME',
         description: 'Hosts to connect to, comma separated',
         default: '0.0.0.0'

  option :ports,
         short: '-p PORTS',
         long: '--ports PORTS',
         description: 'Ports to check, comma separated (22,25,8100-8131,3030)',
         default: '22'

  option :proto,
         short: '-P PROTOCOL',
         long: '--protocol PROTOCOL',
         description: 'Protocol to check: tcp (default) or udp',
         default: 'tcp'

  option :timeout,
         short: '-t SECS',
         long: '--timeout SECS',
         description: 'Connection timeout',
         proc: proc(&:to_i),
         default: 30

  def check_port(port, host)
    Timeout.timeout(config[:timeout]) do
      config[:proto].casecmp('tcp').zero? ? TCPSocket.new(host, port.to_i) : UDPSocket.open.connect(host, port.to_i)
    end
  rescue Errno::ECONNREFUSED
    critical "Connection refused by #{host}:#{port}"
  rescue Timeout::Error
    critical "Connection or read timed out (#{host}:#{port})"
  rescue Errno::EHOSTUNREACH
    critical "Check failed to run: No route to host (#{host}:#{port})"
  rescue EOFError
    critical "Connection closed unexpectedly (#{host}:#{port})"
  end

  def run
    ports = config[:ports].split(',').flat_map do |port|
      # Port range
      if port =~ /^[0-9]+(-[0-9]+)$/
        first_port, last_port = port.split('-')
        (first_port.to_i..last_port.to_i).to_a
      # Single port
      else
        port
      end
    end

    hosts = config[:hosts].split(',')

    okarray = []
    hosts.each do |host|
      ports.each do |port|
        okarray << 'ok' if check_port(port, host)
      end
    end
    if okarray.size == ports.size * hosts.size
      ok "All ports (#{config[:ports]}) are accessible for hosts #{config[:hosts]}"
    else
      warning "port count or pattern #{config[:pattern]} does not match" unless config[:crit_message]
    end
  end
end