├── test ├── fixtures │ ├── check-mtu-1500 │ └── check-mtu-9000 ├── spec_helper.rb ├── plugin_stub.rb ├── metrics-sockstat_spec.rb ├── check-netfilter-conntrack_spec.rb ├── check-ports_spec.rb └── check-mtu_spec.rb ├── CONTRIBUTING.md ├── lib ├── sensu-plugins-network-checks.rb └── sensu-plugins-network-checks │ └── version.rb ├── Gemfile ├── .gitignore ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .rubocop.yml ├── LICENSE ├── Rakefile ├── .travis.yml ├── bin ├── metrics-netif.rb ├── check-netfilter-conntrack.rb ├── check-multicast-groups.rb ├── metrics-sockstat.rb ├── check-whois-domain-expiration.rb ├── check-socat.rb ├── check-ports-nmap.rb ├── check-mtu.rb ├── check-rbl.rb ├── check-ports.rb ├── metrics-interface.rb ├── metrics-ping.rb ├── check-ping.rb ├── check-netstat-tcp.rb ├── metrics-net.rb ├── metrics-netstat-tcp.rb ├── check-whois-domain-expiration-multi.rb ├── check-jsonwhois-domain-expiration.rb ├── check-banner.rb └── check-ports-bind.rb ├── .bonsai.yml ├── sensu-plugins-network-checks.gemspec ├── README.md └── CHANGELOG.md /test/fixtures/check-mtu-1500: -------------------------------------------------------------------------------- 1 | 1500 2 | -------------------------------------------------------------------------------- /test/fixtures/check-mtu-9000: -------------------------------------------------------------------------------- 1 | 9000 2 | -------------------------------------------------------------------------------- /test/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | [Development Documentation](http://sensu-plugins.io/docs/developer_guidelines.html) 2 | -------------------------------------------------------------------------------- /lib/sensu-plugins-network-checks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'sensu-plugins-network-checks/version' 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in sensu-plugins-network-checks.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | .vagrant/* 16 | .DS_Store 17 | .idea/* 18 | *.gem 19 | -------------------------------------------------------------------------------- /lib/sensu-plugins-network-checks/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SensuPluginsNetworkChecks 4 | module Version 5 | MAJOR = 5 6 | MINOR = 0 7 | PATCH = 0 8 | 9 | VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.') 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/plugin_stub.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |c| 4 | # XXX: Sensu plugins run in the context of an at_exit handler. This prevents 5 | # XXX: code-under-test from being run at the end of the rspec suite. 6 | c.before(:each) do 7 | Sensu::Plugin::CLI.class_eval do 8 | # PluginStub 9 | class PluginStub 10 | def run; end 11 | 12 | def ok(*); end 13 | 14 | def warning(*); end 15 | 16 | def critical(*); end 17 | 18 | def unknown(*); end 19 | end 20 | class_variable_set(:@@autorun, PluginStub) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Pull Request Checklist 2 | 3 | **Is this in reference to an existing issue?** 4 | 5 | #### General 6 | 7 | - [ ] Update Changelog following the conventions laid out [here](https://github.com/sensu-plugins/community/blob/master/HOW_WE_CHANGELOG.md) 8 | 9 | - [ ] Update README with any necessary configuration snippets 10 | 11 | - [ ] Binstubs are created if needed 12 | 13 | - [ ] RuboCop passes 14 | 15 | - [ ] Existing tests pass 16 | 17 | #### New Plugins 18 | 19 | - [ ] Tests 20 | 21 | - [ ] Add the plugin to the README 22 | 23 | - [ ] Does it have a complete header as outlined [here](http://sensu-plugins.io/docs/developer_guidelines.html#coding-style) 24 | 25 | #### Purpose 26 | 27 | #### Known Compatibility Issues 28 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | 2 | MethodLength: 3 | Max: 200 4 | 5 | LineLength: 6 | Max: 160 7 | 8 | AbcSize: 9 | Max: 100 10 | 11 | FileName: 12 | Enabled: false 13 | 14 | PerceivedComplexity: 15 | Enabled: false 16 | 17 | CyclomaticComplexity: 18 | Enabled: false 19 | 20 | ClassLength: 21 | Enabled: false 22 | 23 | IfUnlessModifier: 24 | Enabled: false 25 | 26 | RegexpLiteral: 27 | Enabled: false 28 | 29 | Style/Documentation: 30 | Enabled: false 31 | 32 | Style/Next: 33 | Enabled: false 34 | 35 | Style/MultilineTernaryOperator: 36 | Enabled: false 37 | 38 | # safe navigation was introduced in ruby 2.3 39 | Style/SafeNavigation: 40 | Enabled: false 41 | 42 | # match?() was added in ruby 2.4 43 | #Performance/RegexpMatch: 44 | # Enabled: false 45 | 46 | 47 | # TODO: figure out which to use `Date` or `Time` 48 | Style/DateTime: 49 | Enabled: false 50 | 51 | 52 | # testing can be slow 53 | Metrics/BlockLength: 54 | Enabled: true 55 | Exclude: 56 | - 'test/**/*.rb' 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Sensu-Plugins 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'github/markup' 5 | require 'redcarpet' 6 | require 'rspec/core/rake_task' 7 | require 'rubocop/rake_task' 8 | require 'yard' 9 | require 'yard/rake/yardoc_task' 10 | 11 | desc 'Don\'t run Rubocop for unsupported versions' 12 | args = %i[spec make_bin_executable yard rubocop check_binstubs] 13 | 14 | YARD::Rake::YardocTask.new do |t| 15 | OTHER_PATHS = %w[].freeze 16 | t.files = ['lib/**/*.rb', 'bin/**/*.rb', OTHER_PATHS] 17 | t.options = %w[--markup-provider=redcarpet --markup=markdown --main=README.md --files CHANGELOG.md] 18 | end 19 | 20 | RuboCop::RakeTask.new 21 | 22 | RSpec::Core::RakeTask.new(:spec) do |r| 23 | r.pattern = FileList['**/**/*_spec.rb'] 24 | end 25 | 26 | desc 'Make all plugins executable' 27 | task :make_bin_executable do 28 | `chmod -R +x bin/*` 29 | end 30 | 31 | desc 'Test for binstubs' 32 | task :check_binstubs do 33 | bin_list = Gem::Specification.load('sensu-plugins-network-checks.gemspec').executables 34 | bin_list.each do |b| 35 | `which #{b}` 36 | unless $CHILD_STATUS.success? 37 | puts "#{b} was not a binstub" 38 | exit 39 | end 40 | end 41 | end 42 | 43 | task default: args 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | services: 3 | - docker 4 | cache: 5 | - bundler 6 | before_install: 7 | - gem install bundler 8 | install: 9 | - bundle install 10 | rvm: 11 | - 2.3.0 12 | - 2.4.1 13 | notifications: 14 | email: 15 | recipients: 16 | - sensu-plugin@sensu-plugins.io 17 | on_success: change 18 | on_failure: always 19 | script: 20 | - bundle exec rake default 21 | - gem build sensu-plugins-network-checks.gemspec 22 | - gem install sensu-plugins-network-checks-*.gem 23 | before_deploy: 24 | - bash -c "[ ! -d bonsai/ ] && git clone https://github.com/sensu/sensu-go-bonsai-asset.git bonsai || echo 'bonsai/ exists, skipping git clone'" 25 | deploy: 26 | - provider: rubygems 27 | api_key: 28 | secure: l79xZquLcaZ1AqR58Vahu2PhIwz4UHpVN3vJZYOOO2phYC5qi1Q4UAQsg1Yw3sgY/3wrjtTPGo26MbGDs86oZXE9Gq8L9paIyRDGhTWp/Y5+q7vUx7gBGu/r1kVco2UTwXAUr/cWA+WbiqYYlTiTIxBLJlsbE2mPGOnW2H97cCc= 29 | gem: sensu-plugins-network-checks 30 | on: 31 | tags: true 32 | all_branches: true 33 | rvm: 2.3.0 34 | rvm: 2.4.1 35 | repo: sensu-plugins/sensu-plugins-network-checks 36 | - provider: script 37 | script: bonsai/ruby-runtime/travis-build-ruby-plugin-assets.sh sensu-plugins-network-checks 38 | skip_cleanup: true 39 | on: 40 | tags: true 41 | all_branches: true 42 | rvm: 2.4.1 43 | -------------------------------------------------------------------------------- /test/metrics-sockstat_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # metrics-sockstat_spec 6 | # 7 | # DESCRIPTION: 8 | # Tests for metrics-sockstat.rb 9 | # 10 | # OUTPUT: 11 | # 12 | # PLATFORMS: 13 | # 14 | # DEPENDENCIES: 15 | # 16 | # USAGE: 17 | # bundle install 18 | # rake spec 19 | # 20 | # NOTES: 21 | # 22 | # LICENSE: 23 | # Copyright 2015 Contegix, LLC. 24 | # Released under the same terms as Sensu (the MIT license); see LICENSE for details. 25 | # 26 | require_relative './spec_helper.rb' 27 | require_relative '../bin/metrics-sockstat.rb' 28 | 29 | RSpec.configure do |c| 30 | c.before { allow($stdout).to receive(:puts) } 31 | c.before { allow($stderr).to receive(:puts) } 32 | end 33 | 34 | describe MetricsSockstat, 'run' do 35 | it 'should successfully output socket metrics for the total number of sockets and any other types that are present' do 36 | sockstat = MetricsSockstat.new 37 | allow(sockstat).to receive(:read_sockstat).and_return("sockets: used 10\nFOO: bar 5 baz 4") 38 | allow(sockstat).to receive(:output) 39 | allow(sockstat).to receive(:ok) 40 | expect(sockstat).to receive(:output).with(match('total_used 10')) 41 | expect(sockstat).to receive(:output).with(match('FOO.bar 5')) 42 | expect(sockstat).to receive(:output).with(match('FOO.baz 4')) 43 | sockstat.run 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /bin/metrics-netif.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # netif-metrics 6 | # 7 | # DESCRIPTION: 8 | # Network interface throughput 9 | # 10 | # OUTPUT: 11 | # metric data 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # #YELLOW 21 | # 22 | # NOTES: 23 | # 24 | # LICENSE: 25 | # Copyright 2014 Sonian, Inc. and contributors. 26 | # Released under the same terms as Sensu (the MIT license); see LICENSE 27 | # for details. 28 | # 29 | 30 | require 'sensu-plugin/metric/cli' 31 | require 'socket' 32 | 33 | # 34 | # Netif Metrics 35 | # 36 | class NetIFMetrics < Sensu::Plugin::Metric::CLI::Graphite 37 | option :scheme, 38 | description: 'Metric naming scheme, text to prepend to .$parent.$child', 39 | long: '--scheme SCHEME', 40 | default: Socket.gethostname.to_s 41 | 42 | option :interval, 43 | description: 'Interval to collect metrics over', 44 | long: '--interval INTERVAL', 45 | default: 1 46 | 47 | option :average_key, 48 | description: 'This key is used to `grep` for a key that corresponds to average. useful for different locales', 49 | long: '--average-key AVERAGE_KEY', 50 | default: 'Average' 51 | 52 | def run 53 | sar = `sar -n DEV #{config[:interval]} 1 | grep #{config[:average_key]} | grep -v IFACE` 54 | if sar.nil? || sar.empty? 55 | unknown 'sar is not installed or in $PATH' 56 | end 57 | sar.each_line do |line| 58 | stats = line.split(/\s+/) 59 | unless stats.empty? 60 | stats.shift 61 | nic = stats.shift 62 | output "#{config[:scheme]}.#{nic}.rx_kB_per_sec", stats[2].to_f if stats[3] 63 | output "#{config[:scheme]}.#{nic}.tx_kB_per_sec", stats[3].to_f if stats[3] 64 | end 65 | end 66 | 67 | ok 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/check-netfilter-conntrack_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-netfilter-conntrack_spec 6 | # 7 | # DESCRIPTION: 8 | # rspec tests for netfilter-conntrack-mtu 9 | # 10 | # OUTPUT: 11 | # RSpec testing output: passes and failures info 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # rspec 18 | # 19 | # USAGE: 20 | # For Rspec Testing 21 | # 22 | # NOTES: 23 | # For Rspec Testing 24 | # 25 | # LICENSE: 26 | # Copyright 2018 Jan Kunzmann 27 | # Released under the same terms as Sensu (the MIT license); see LICENSE 28 | # for details. 29 | # 30 | 31 | require_relative '../bin/check-netfilter-conntrack' 32 | require_relative './spec_helper.rb' 33 | 34 | describe CheckNetfilterConntrack do 35 | let(:checker) { described_class.new } 36 | let(:checker_no_file) { described_class.new } 37 | let(:exit_code) { nil } 38 | 39 | before(:each) do 40 | def checker.ok(*_args) 41 | exit 0 42 | end 43 | 44 | def checker.warning(*_args) 45 | exit 1 46 | end 47 | 48 | def checker.critical(*_args) 49 | exit 2 50 | end 51 | end 52 | 53 | [ 54 | [100, 0, 0, 'ok'], 55 | [100, 79, 0, 'ok'], 56 | [100, 80, 1, 'warn'], 57 | [100, 89, 1, 'warn'], 58 | [100, 90, 2, 'crit'], 59 | [100, 100, 2, 'crit'] 60 | ].each do |testdata| 61 | it "returns #{testdata[3]} for default thresholds" do 62 | begin 63 | allow(checker).to receive(:nf_conntrack_max).and_return testdata[0] 64 | allow(checker).to receive(:nf_conntrack_count).and_return testdata[1] 65 | checker.run 66 | rescue SystemExit => e 67 | exit_code = e.status 68 | end 69 | expect(exit_code).to eq testdata[2] 70 | end 71 | end 72 | 73 | it 'returns warning if conntract sysctl files not found' do 74 | begin 75 | allow(checker).to receive(:nf_conntrack_max).and_raise Errno::ENOENT 76 | checker.run 77 | rescue SystemExit => e 78 | exit_code = e.status 79 | end 80 | expect(exit_code).to eq 1 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /.bonsai.yml: -------------------------------------------------------------------------------- 1 | --- 2 | description: "#{repo}" 3 | builds: 4 | - platform: "alpine" 5 | arch: "amd64" 6 | asset_filename: "#{repo}_#{version}_alpine_linux_amd64.tar.gz" 7 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 8 | filter: 9 | - "entity.system.os == 'linux'" 10 | - "entity.system.arch == 'amd64'" 11 | - "entity.system.platform == 'alpine'" 12 | - "entity.system.platform_version.split('.')[0] == '3'" 13 | - platform: "alpine3.8" 14 | arch: "amd64" 15 | asset_filename: "#{repo}_#{version}_alpine3.8_linux_amd64.tar.gz" 16 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 17 | filter: 18 | - "entity.system.os == 'linux'" 19 | - "entity.system.arch == 'amd64'" 20 | - "entity.system.platform == 'alpine'" 21 | - platform: "centos6" 22 | arch: "amd64" 23 | asset_filename: "#{repo}_#{version}_centos6_linux_amd64.tar.gz" 24 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 25 | filter: 26 | - "entity.system.os == 'linux'" 27 | - "entity.system.arch == 'amd64'" 28 | - "entity.system.platform_family == 'rhel'" 29 | - "entity.system.platform_version.split('.')[0] == '6'" 30 | - platform: "centos7" 31 | arch: "amd64" 32 | asset_filename: "#{repo}_#{version}_centos7_linux_amd64.tar.gz" 33 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 34 | filter: 35 | - "entity.system.os == 'linux'" 36 | - "entity.system.arch == 'amd64'" 37 | - "entity.system.platform_family == 'rhel'" 38 | - "entity.system.platform_version.split('.')[0] == '7'" 39 | - platform: "debian" 40 | arch: "amd64" 41 | asset_filename: "#{repo}_#{version}_debian_linux_amd64.tar.gz" 42 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 43 | filter: 44 | - "entity.system.os == 'linux'" 45 | - "entity.system.arch == 'amd64'" 46 | - "entity.system.platform_family == 'debian'" 47 | - platform: "debian9" 48 | arch: "amd64" 49 | asset_filename: "#{repo}_#{version}_debian9_linux_amd64.tar.gz" 50 | sha_filename: "#{repo}_#{version}_sha512-checksums.txt" 51 | filter: 52 | - "entity.system.os == 'linux'" 53 | - "entity.system.arch == 'amd64'" 54 | -------------------------------------------------------------------------------- /bin/check-netfilter-conntrack.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-netfilter-conntrack 6 | # 7 | # DESCRIPTION: 8 | # Check netfilter connection tracking table condition 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # $ ./check-netfilter-conntrack.rb --warning 60 --critical 90 21 | # 22 | # NOTES: 23 | # - If you need to check the conntrack table of a specific linux 24 | # network namespace (e.g in a docker context), run this check as 25 | # `nsenter --net= check-netfilter-conntrack.rb` to use the 26 | # network namespace which ``'s descriptor indicates. 27 | # 28 | # LICENSE: 29 | # Released under the same terms as Sensu (the MIT license); see LICENSE 30 | # for details. 31 | # 32 | 33 | require 'sensu-plugin/check/cli' 34 | 35 | # 36 | # Check Netfilter connection tracking table condition 37 | # 38 | class CheckNetfilterConntrack < Sensu::Plugin::Check::CLI 39 | option :warning, 40 | description: 'Warn if conntrack table is filled more than PERC%', 41 | short: '-w PERC', 42 | long: '--warning PERC', 43 | default: 80, 44 | proc: proc(&:to_i) 45 | 46 | option :critical, 47 | description: 'Critical if conntrack table is filled more than PERC%', 48 | short: '-c PERC', 49 | long: '--critical PERC', 50 | default: 90, 51 | proc: proc(&:to_i) 52 | 53 | def nf_conntrack_max 54 | File.read('/proc/sys/net/netfilter/nf_conntrack_max').to_i 55 | end 56 | 57 | def nf_conntrack_count 58 | File.read('/proc/sys/net/netfilter/nf_conntrack_count').to_i 59 | end 60 | 61 | def run 62 | max = nf_conntrack_max 63 | count = nf_conntrack_count 64 | percentage = (count / max.to_f) * 100 65 | 66 | message "Table is at #{percentage.round(1)}% (#{count}/#{max})" 67 | 68 | critical if percentage >= config[:critical] 69 | warning if percentage >= config[:warning] 70 | ok 71 | rescue StandardError 72 | warning "Can't read conntrack information." 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /bin/check-multicast-groups.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: false 3 | 4 | # 5 | # check-multicast-groups 6 | # 7 | # DESCRIPTION: 8 | # This plugin checks if specific multicast groups are configured 9 | # on specific interfaces. The netstat command is required. 10 | # 11 | # The configurations can be put in the default sensu config directory 12 | # and/or out of the sensu directory, as a JSON file. If the config file 13 | # is not in the sensu directry, -c PATH option must be given. 14 | # 15 | # OUTPUT: 16 | # plain text 17 | # 18 | # PLATFORMS: 19 | # Linux 20 | # 21 | # DEPENDENCIES: 22 | # gem: sensu-plugin 23 | # 24 | # USAGE: 25 | # example commands 26 | # 27 | # NOTES: 28 | # Does it behave differently on specific platforms, specific use cases, etc 29 | # 30 | # LICENSE: 31 | # Copyright 2014 Mitsutoshi Aoe 32 | # Released under the same terms as Sensu (the MIT license); see LICENSE 33 | # for details. 34 | # 35 | 36 | require 'json' 37 | require 'sensu-plugin/check/cli' 38 | require 'sensu-plugin/utils' 39 | require 'set' 40 | 41 | # 42 | # Check Multicast Groups 43 | # 44 | class CheckMulticastGroups < Sensu::Plugin::Check::CLI 45 | include Sensu::Plugin::Utils 46 | 47 | option :config, 48 | short: '-c PATH', 49 | long: '--config PATH', 50 | required: true, 51 | description: 'Path to a config file' 52 | 53 | def run 54 | targets = load_config(config[:config])['check-multicast-groups'] || [] 55 | critical 'No target muticast groups are specified.' if targets.empty? 56 | 57 | iface_pat = /[a-zA-Z0-9\.]+/ 58 | refcount_pat = /\d+/ 59 | group_pat = /[a-f0-9\.:]+/ # assumes that -n is given 60 | pattern = /(#{iface_pat})\s+#{refcount_pat}\s+(#{group_pat})/ 61 | 62 | actual = Set.new(`netstat -ng`.scan(pattern)) 63 | expected = Set.new(targets) 64 | 65 | diff = expected.difference(actual) 66 | unless diff.empty? 67 | diff_output = diff.map { |iface, addr| "#{iface}\t#{addr}" }.join("\n") 68 | critical "#{diff.size} missing multicast group(s):\n#{diff_output}" 69 | end 70 | ok 71 | rescue StandardError => e 72 | critical "Failed to check multicast groups: #{e}" 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /bin/metrics-sockstat.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # metrics-sockstat 6 | # 7 | # DESCRIPTION: 8 | # This metric check parses /proc/net/sockstat and outputs all fields as metrics 9 | # 10 | # OUTPUT: 11 | # graphite metric data 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # Specify [-s|--scheme] SCHEME to change the text appended to the metric paths. 21 | # 22 | # NOTES: 23 | # It outputs the value in the first line ("sockets used") as SCHEME.total_used. 24 | # All other fields are output as SCHEME.type.field, i.e., SCHEME.TCP.inuse, SCHEME.UDP.mem 25 | # 26 | # LICENSE: 27 | # Copyright 2015 Contegix, LLC. 28 | # Released under the same terms as Sensu (the MIT license); see LICENSE for details. 29 | # 30 | require 'sensu-plugin/metric/cli' 31 | 32 | # MetricsSockstat 33 | class MetricsSockstat < Sensu::Plugin::Metric::CLI::Graphite 34 | option :scheme, 35 | description: 'Metric naming scheme, text to prepend to $protocol.$field', 36 | long: '--scheme SCHEME', 37 | short: '-s SCHEME', 38 | default: "#{Socket.gethostname}.network.sockets" 39 | 40 | def output_metric(name, value) 41 | output "#{@config[:scheme]}.#{name} #{value} #{@timestamp}" 42 | end 43 | 44 | def socket_metrics(fields) 45 | name = 'total_used' 46 | value = fields[2] 47 | output_metric(name, value) 48 | end 49 | 50 | def generic_metrics(fields) 51 | proto = fields[0].sub(':', '') 52 | fields[1..-1].join(' ').scan(/([A-Za-z]+) (\d+)/).each do |tuple| 53 | output_metric("#{proto}.#{tuple[0]}", tuple[1]) 54 | end 55 | end 56 | 57 | def read_sockstat 58 | IO.read('/proc/net/sockstat') 59 | rescue StandardError => e 60 | unknown "Failed to read /proc/net/sockstat: #{e}" 61 | end 62 | 63 | def run 64 | sockstat = read_sockstat 65 | @config = config 66 | @timestamp = Time.now.to_i 67 | sockstat.split("\n").each do |line| 68 | fields = line.split 69 | if fields[0] == 'sockets:' 70 | socket_metrics(fields) 71 | elsif fields.length > 1 72 | generic_metrics(fields) 73 | end 74 | end 75 | ok 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /bin/check-whois-domain-expiration.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: false 3 | 4 | # 5 | # check-whois-domain-expiration 6 | # 7 | # DESCRIPTION: 8 | # This plugin checks domain expiration dates using the 'whois' gem. 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Tested on Mac OS X 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # gem: whois 19 | # 20 | # USAGE: 21 | # $ ./check-whois-domain-expiration.rb -d mijit.com 22 | # WhoisDomainExpirationCheck OK: mijit.com expires on 02-07-2016 (325 days away) 23 | # 24 | # LICENSE: 25 | # Copyright 2015 michael j talarczyk and contributors. 26 | # Released under the same terms as Sensu (the MIT license); see LICENSE 27 | # for details. 28 | 29 | require 'sensu-plugin/check/cli' 30 | require 'whois' 31 | require 'whois-parser' 32 | 33 | # 34 | # Check Whois domain expiration 35 | # 36 | class WhoisDomainExpirationCheck < Sensu::Plugin::Check::CLI 37 | option :domain, 38 | short: '-d DOMAIN', 39 | long: '--domain DOMAIN', 40 | required: true, 41 | description: 'Domain to check' 42 | 43 | option :warning, 44 | short: '-w DAYS', 45 | long: '--warn DAYS', 46 | default: 30, 47 | description: 'Warn if fewer than DAYS away' 48 | 49 | option :critical, 50 | short: '-c DAYS', 51 | long: '--critical DAYS', 52 | default: 7, 53 | description: 'Critical if fewer than DAYS away' 54 | 55 | option :help, 56 | short: '-h', 57 | long: '--help', 58 | description: 'Show this message', 59 | on: :tail, 60 | boolean: true, 61 | show_options: true, 62 | exit: 0 63 | 64 | def run 65 | whois = Whois.whois(config[:domain]) 66 | 67 | # TODO: figure out which to use `Date` or `Time` 68 | expires_on = DateTime.parse(whois.parser.expires_on.to_s) 69 | num_days = (expires_on - DateTime.now).to_i 70 | 71 | message "#{config[:domain]} expires on #{expires_on.strftime('%m-%d-%Y')} (#{num_days} days away)" 72 | 73 | if num_days <= config[:critical].to_i 74 | critical 75 | elsif num_days <= config[:warning].to_i 76 | warning 77 | else 78 | ok 79 | end 80 | rescue StandardError 81 | unknown "#{config[:domain]} can't be checked" 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /bin/check-socat.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: false 3 | 4 | # 5 | # check-socat 6 | # 7 | # DESCRIPTION: 8 | # Run socat to inspect sockets 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # coreutils package for the timeout command 19 | # socat package 20 | # 21 | # USAGE: 22 | # 23 | # Check if socat can receive particular a UDP multicast packet in 10 seconds: 24 | # check-socat.rb -t 10s -i UDP4-RECVFROM:,ip-add-membership=: -o /dev/null 25 | # 26 | # Check if a UDP multicast packet contains an expected pattern: 27 | # check-socat.rb -t 10s -i UDP4-RECVFROM:,ip-add-membership=: -o - --pipe "grep PATTERN" 28 | # 29 | # LICENSE: 30 | # Released under the same terms as Sensu (the MIT license); see LICENSE 31 | # for details. 32 | 33 | require 'open3' 34 | require 'sensu-plugin/check/cli' 35 | 36 | class SocatCheck < Sensu::Plugin::Check::CLI 37 | option :timeout, 38 | description: 'Timeout in seconds', 39 | short: '-t DURATION', 40 | long: '--timeout DURATION', 41 | default: '5s' 42 | 43 | option :input, 44 | description: 'Input stream', 45 | short: '-i INPUT', 46 | long: '--input INPUT', 47 | required: true 48 | 49 | option :output, 50 | description: 'Output stream', 51 | short: '-o OUTPUT', 52 | long: '--output OUTPUT', 53 | required: true 54 | 55 | option :pipe, 56 | description: 'Pipe socat output into this command', 57 | short: '-p COMMAND', 58 | long: '--pipe COMMAND' 59 | 60 | def run 61 | pipe = config[:pipe].nil? ? '' : "| #{config[:pipe]}" 62 | timeout = config[:timeout] 63 | stdout, stderr, status = Open3.capture3( 64 | %(bash -c "set -o pipefail; timeout #{timeout} socat #{config[:input]} #{config[:output]} #{pipe}") 65 | ) 66 | if status.success? 67 | ok stdout 68 | else 69 | case status.exitstatus 70 | when 124 71 | critical "socat timed out\n#{stderr}" 72 | when 125 73 | critical "timeout failed\n#{stderr}" 74 | when 126 75 | critical "socat cannot be invoked\n#{stderr}" 76 | when 127 77 | critical "socat cannot be found\n#{stderr}" 78 | when 137 79 | critical "socat is sent the KILL signal\n#{stderr}" 80 | else 81 | critical stderr 82 | end 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /bin/check-ports-nmap.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-ports-nmap 6 | # 7 | # DESCRIPTION: 8 | # Fetch port status using nmap. This check is good for catching bad network ACLs 9 | # or service down events for network resources. 10 | # 11 | # OUTPUT: 12 | # plain text 13 | # 14 | # PLATFORMS: 15 | # Linux, Windows, BSD, Solaris, etc 16 | # 17 | # DEPENDENCIES: 18 | # gem: sensu-plugin 19 | # nmap package 20 | # 21 | # USAGE: 22 | # $ ./check-ports-nmap.rb --host some_server --ports 5671,5672 --level crit 23 | # 24 | # NOTES: 25 | # #YELLOW 26 | # Look at rewriting this using the namp library to not depend on external tools 27 | # 28 | # LICENSE: 29 | # Copyright 2013 GoDaddy.com, LLC 30 | # Released under the same terms as Sensu (the MIT license); see LICENSE 31 | # for details. 32 | # 33 | 34 | require 'open3' 35 | require 'sensu-plugin/check/cli' 36 | require 'json' 37 | 38 | # CheckPorts 39 | class CheckPorts < Sensu::Plugin::Check::CLI 40 | option :host, 41 | description: 'Resolving name or IP address of target host', 42 | short: '-h HOST', 43 | long: '--host HOST', 44 | default: 'localhost' 45 | 46 | option :ports, 47 | description: 'TCP port(s) you wish to get status for', 48 | short: '-t PORT,PORT...', 49 | long: '--ports PORT,PORT...' 50 | 51 | option :level, 52 | description: 'Alert level crit(critical) or warn(warning)', 53 | short: '-l crit|warn', 54 | long: '--level crit|warn', 55 | default: 'WARN' 56 | 57 | def run 58 | stdout, stderr = Open3.capture3( 59 | ENV, 60 | "nmap -P0 -p #{config[:ports]} #{config[:host]}" 61 | ) 62 | 63 | case stderr 64 | when /Failed to resolve/ 65 | critical 'cannot resolve the target hostname' 66 | end 67 | 68 | port_checks = {} 69 | check_pass = true 70 | 71 | stdout.split("\n").each do |line| 72 | line.scan(/(\d+).tcp\s+(\w+)\s+(\w+)/).each do |status| 73 | port_checks[status[1]] ||= [] 74 | port_checks[status[1]].push status[0] 75 | check_pass = false unless status[1]['open'] 76 | end 77 | end 78 | 79 | result = port_checks.map { |state, ports| "#{state}:#{ports.join(',')}" }.join(' ') 80 | 81 | if check_pass 82 | ok result 83 | elsif config[:level].casecmp('WARN').zero? 84 | warning result 85 | elsif config[:level].casecmp('CRIT').zero? 86 | critical result 87 | else 88 | unknown "Unknown alert level #{config[:level]}" 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /bin/check-mtu.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-mtu.rb 6 | # 7 | # DESCRIPTION: 8 | # Check MTU of a network interface 9 | # In many setups, MTUs are tuned. MTU mismatches cause issues. Having a check for MTU settings helps catch these mistakes. 10 | # Also, some instances in Amazon EC2 have default MTU size of 9,000 bytes. It is undesirable in some environments. This check can catch undesired setups. 11 | # 12 | # OUTPUT: 13 | # OK, Warning, Critical message 14 | # 15 | # PLATFORMS: 16 | # Linux 17 | # 18 | # DEPENDENCIES: 19 | # gem: sensu-plugin 20 | # 21 | # USAGE: 22 | # This will throw an error if interface eth0 does not have MTU of 1,500 bytes 23 | # check-mtu.rb --interface eth0 --mtu 1500 24 | # This will throw a warning if interface eth0 does not have MTU of 1,500 bytes 25 | # check-mtu.rb --interface eth0 --mtu 1500 --warn 26 | # This will throw an error if interface eth1 does not have MTU 9,000 bytes 27 | # check-mtu.rb --interface eth1 --mtu 9000 28 | # This will throw a waring if interface eth1 does not have MTU 9,000 bytes 29 | # check-mtu.rb --interface eth1 --mtu 9000 --warn 30 | # 31 | # NOTES: 32 | # No special notes. This should be fairly straight forward. 33 | # 34 | # LICENSE: 35 | # Robin 36 | # Released under the same terms as Sensu (the MIT license); see LICENSE 37 | # for details. 38 | # 39 | 40 | require 'sensu-plugin/check/cli' 41 | 42 | # 43 | # Check MTU 44 | # 45 | class CheckMTU < Sensu::Plugin::Check::CLI 46 | option :interface, 47 | short: '-i INTERFACE', 48 | long: '--interface INTERFACE', 49 | description: 'Specify the interface', 50 | default: 'eth0' 51 | 52 | option :mtu, 53 | short: '-m MTU', 54 | long: '--mtu MTU', 55 | description: 'Optionally specify desired MTU size', 56 | proc: proc(&:to_i), 57 | default: 1500 58 | 59 | option :warn, 60 | short: '-w', 61 | long: '--warn', 62 | boolean: true, 63 | Description: 'Specify the level of criticality to warning (instead of critical) if MTU size does not match', 64 | default: false 65 | 66 | def locate_mtu_file 67 | "/sys/class/net/#{config[:interface]}/mtu" 68 | end 69 | 70 | def run 71 | required_mtu = config[:mtu] 72 | 73 | mtu_file = locate_mtu_file 74 | 75 | error_handling = 'critical' 76 | error_handling = 'warning' if config[:warn] 77 | 78 | file_read_issue_error_message = "#{mtu_file} does not exist or is not readble" 79 | send(error_handling, file_read_issue_error_message) unless File.file?(mtu_file) 80 | 81 | mtu = IO.read(mtu_file).to_i 82 | mtu_mismatch_error_message = "Required MTU is #{required_mtu} and we found #{mtu}" 83 | send(error_handling, mtu_mismatch_error_message) unless mtu == required_mtu 84 | 85 | ok_message = "#{mtu} matches #{required_mtu}" 86 | ok ok_message 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /sensu-plugins-network-checks.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | 6 | require 'date' 7 | 8 | require_relative 'lib/sensu-plugins-network-checks' 9 | 10 | Gem::Specification.new do |s| # rubocop:disable Metrics/BlockLength 11 | s.authors = ['Sensu-Plugins and contributors'] 12 | s.date = Date.today.to_s 13 | s.description = 'This plugin provides native network instrumentation 14 | for monitoring and metrics collection, including: 15 | hardware, TCP response, RBLs, whois, port status, and more' 16 | s.email = '' 17 | s.executables = Dir.glob('bin/**/*.rb').map { |file| File.basename(file) } 18 | s.files = Dir.glob('{bin,lib}/**/*') + %w[LICENSE README.md CHANGELOG.md] 19 | s.homepage = 'https://github.com/sensu-plugins/sensu-plugins-network-checks' 20 | s.license = 'MIT' 21 | s.metadata = { 'maintainer' => 'sensu-plugin', 22 | 'development_status' => 'active', 23 | 'production_status' => 'unstable - testing recommended', 24 | 'release_draft' => 'false', 25 | 'release_prerelease' => 'false' } 26 | s.name = 'sensu-plugins-network-checks' 27 | s.platform = Gem::Platform::RUBY 28 | s.post_install_message = 'You can use the embedded Ruby by setting EMBEDDED_RUBY=true in /etc/default/sensu' 29 | s.require_paths = ['lib'] 30 | s.required_ruby_version = '>= 2.3' 31 | s.summary = 'Sensu plugins for checking network hardware, connections, and data' 32 | s.test_files = s.files.grep(%r{^(test|spec|features)/}) 33 | s.version = SensuPluginsNetworkChecks::Version::VER_STRING 34 | 35 | s.add_runtime_dependency 'sensu-plugin', '~> 4.0' 36 | 37 | s.add_runtime_dependency 'activesupport', '~> 4.2' 38 | s.add_runtime_dependency 'dnsbl-client', '1.0.4' 39 | s.add_runtime_dependency 'net-ping', '2.0.6' 40 | s.add_runtime_dependency 'whois', '>= 4.0' 41 | s.add_runtime_dependency 'whois-parser', '~> 1.2' 42 | 43 | s.add_development_dependency 'bundler', '~> 2.1' 44 | s.add_development_dependency 'github-markup', '~> 4.0' 45 | s.add_development_dependency 'pry', '~> 0.10' 46 | s.add_development_dependency 'rake', '~> 13.0' 47 | s.add_development_dependency 'rdoc', '~> 6.2.0' 48 | s.add_development_dependency 'redcarpet', '~> 3.2' 49 | s.add_development_dependency 'rspec', '~> 3.1' 50 | s.add_development_dependency 'rubocop', '~> 0.79.0' 51 | s.add_development_dependency 'yard', '~> 0.9.11' 52 | end 53 | -------------------------------------------------------------------------------- /bin/check-rbl.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-rbl 6 | # 7 | # DESCRIPTION: 8 | # Checks if a ip is blacklisted in the common dns blacklists. You can 9 | # add a list 10 | # 11 | # OUTPUT: 12 | # plain text 13 | # 14 | # PLATFORMS: 15 | # Linux 16 | # 17 | # DEPENDENCIES: 18 | # gem: sensu-plugin 19 | # gem: dnsbl-client 20 | # 21 | # USAGE: 22 | # 23 | # NOTES: 24 | # A list of DNS blacklists to not be checked can be passed with -I as a 25 | # comma-separated list. 26 | # 27 | # LICENSE: 28 | # Copyright 2012 Sarguru Nathan 29 | # Released under the same terms as Sensu (the MIT license); see LICENSE 30 | # for details. 31 | # 32 | 33 | require 'sensu-plugin/check/cli' 34 | require 'dnsbl/client' 35 | require 'set' 36 | 37 | # 38 | # Rbl Check 39 | # 40 | class RblCheck < Sensu::Plugin::Check::CLI 41 | option :ip, 42 | short: '-i IPADDRESS', 43 | long: '--ip IPADDRESS', 44 | description: 'IP of the server to check' 45 | 46 | option :ignored_bls, 47 | short: '-I BLACKLISTNAME', 48 | long: '--ignored_bls BLACKLISTNAME', 49 | description: 'Comma Separated String of ignored blacklists from default list', 50 | default: 'null' 51 | 52 | option :critical_bls, 53 | short: '-C BLACKLISTNAME', 54 | long: '--critical_bls BLACKLISTNAME', 55 | description: 'Comma Separated String of critical blacklists from default list', 56 | default: 'null' 57 | 58 | def run 59 | c = DNSBL::Client.new 60 | 61 | if config[:ip] 62 | ip_add = config[:ip] 63 | else 64 | critical 'plugin failed. Required Argument -i (ip address of the client)' 65 | end 66 | 67 | if config[:ignored_bls] 68 | ignored_bls = config[:ignored_bls] 69 | ignored_bls_set = ignored_bls.split(',').to_set 70 | end 71 | 72 | if config[:critical_bls] 73 | critical_bls = config[:critical_bls] 74 | critical_bls_set = critical_bls.split(',').to_set 75 | end 76 | 77 | dnsbl_ret = c.lookup(ip_add.to_s) 78 | msg_string = '' 79 | criticality = 0 80 | 81 | # #YELLOW 82 | dnsbl_ret.each do |dnsbl_result| 83 | if dnsbl_result.meaning =~ /spam/i || dnsbl_result.meaning =~ /blacklist/i 84 | unless ignored_bls_set.member?(dnsbl_result.dnsbl) 85 | msg_string = "#{msg_string} #{dnsbl_result.dnsbl}" 86 | end 87 | 88 | criticality += 1 if critical_bls_set.member?(dnsbl_result.dnsbl) 89 | end 90 | end 91 | 92 | # YELLOW 93 | unless msg_string.empty? # rubocop:disable UnlessElse 94 | if criticality.positive? 95 | critical "#{ip_add} Blacklisted in#{msg_string}" 96 | else 97 | warning "#{ip_add} Blacklisted in#{msg_string}" 98 | end 99 | else 100 | msg_txt = "All is well. #{ip_add} has good reputation." 101 | ok msg_txt.to_s 102 | end 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /bin/check-ports.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-ports 6 | # 7 | # DESCRIPTION: 8 | # Connect to a TCP/UDP port on one or more ports, to see if open. Don't use nmap since it's overkill. 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # 21 | # Ports are comma separated and support ranges 22 | # ./check-ports.rb -H localhost -p 22,25,8100-8131,3030 -P tcp 23 | # 24 | # NOTES: 25 | # By default, checks for openssh on localhost port 22 26 | # 27 | # 28 | # LICENSE: 29 | # Released under the same terms as Sensu (the MIT license); see LICENSE 30 | # for details. 31 | 32 | require 'sensu-plugin/check/cli' 33 | require 'socket' 34 | require 'timeout' 35 | 36 | # 37 | # Check Banner 38 | # 39 | class CheckPort < Sensu::Plugin::Check::CLI 40 | option :hosts, 41 | short: '-H HOSTNAME', 42 | long: '--hostname HOSTNAME', 43 | description: 'Hosts to connect to, comma separated', 44 | default: '0.0.0.0' 45 | 46 | option :ports, 47 | short: '-p PORTS', 48 | long: '--ports PORTS', 49 | description: 'Ports to check, comma separated (22,25,8100-8131,3030)', 50 | default: '22' 51 | 52 | option :proto, 53 | short: '-P PROTOCOL', 54 | long: '--protocol PROTOCOL', 55 | description: 'Protocol to check: tcp (default) or udp', 56 | default: 'tcp' 57 | 58 | option :timeout, 59 | short: '-t SECS', 60 | long: '--timeout SECS', 61 | description: 'Connection timeout', 62 | proc: proc(&:to_i), 63 | default: 30 64 | 65 | def check_port(port, host) 66 | Timeout.timeout(config[:timeout]) do 67 | config[:proto].casecmp('tcp').zero? ? TCPSocket.new(host, port.to_i) : UDPSocket.open.connect(host, port.to_i) 68 | end 69 | rescue Errno::ECONNREFUSED 70 | critical "Connection refused by #{host}:#{port}" 71 | rescue Timeout::Error 72 | critical "Connection or read timed out (#{host}:#{port})" 73 | rescue Errno::EHOSTUNREACH 74 | critical "Check failed to run: No route to host (#{host}:#{port})" 75 | rescue EOFError 76 | critical "Connection closed unexpectedly (#{host}:#{port})" 77 | end 78 | 79 | def run 80 | ports = config[:ports].split(',').flat_map do |port| 81 | # Port range 82 | if port =~ /^[0-9]+(-[0-9]+)$/ 83 | first_port, last_port = port.split('-') 84 | (first_port.to_i..last_port.to_i).to_a 85 | # Single port 86 | else 87 | port 88 | end 89 | end 90 | 91 | hosts = config[:hosts].split(',') 92 | 93 | okarray = [] 94 | hosts.each do |host| 95 | ports.each do |port| 96 | okarray << 'ok' if check_port(port, host) 97 | end 98 | end 99 | if okarray.size == ports.size * hosts.size 100 | ok "All ports (#{config[:ports]}) are accessible for hosts #{config[:hosts]}" 101 | else 102 | warning "port count or pattern #{config[:pattern]} does not match" unless config[:crit_message] 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /bin/metrics-interface.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # interface-metrics 6 | # 7 | # DESCRIPTION: 8 | # 9 | # OUTPUT: 10 | # metric data 11 | # 12 | # PLATFORMS: 13 | # Linux 14 | # 15 | # DEPENDENCIES: 16 | # gem: sensu-plugin 17 | # 18 | # USAGE: 19 | # 20 | # NOTES: 21 | # 22 | # LICENSE: 23 | # Copyright 2012 Sonian, Inc 24 | # Released under the same terms as Sensu (the MIT license); see LICENSE 25 | # for details. 26 | # 27 | 28 | require 'sensu-plugin/metric/cli' 29 | require 'socket' 30 | 31 | # 32 | # Interface Graphite 33 | # 34 | class InterfaceGraphite < Sensu::Plugin::Metric::CLI::Graphite 35 | option :scheme, 36 | description: 'Metric naming scheme, text to prepend to metric', 37 | short: '-s SCHEME', 38 | long: '--scheme SCHEME', 39 | default: "#{Socket.gethostname}.interface" 40 | 41 | option :excludeinterfaceregex, 42 | description: 'Regex matching interfaces to exclude', 43 | short: '-X INTERFACE', 44 | long: '--exclude-interface-regex' 45 | 46 | option :includeinterfaceregex, 47 | description: 'Regex matching interfaces to include', 48 | short: '-I INTERFACE', 49 | long: '--include-interface-regex' 50 | 51 | option :excludeinterface, 52 | description: 'List of interfaces to exclude', 53 | short: '-x INTERFACE[,INTERFACE]', 54 | long: '--exclude-interface', 55 | proc: proc { |a| a.split(',') } 56 | 57 | option :includeinterface, 58 | description: 'List of interfaces to include', 59 | short: '-i INTERFACE[,INTERFACE]', 60 | long: '--include-interface', 61 | proc: proc { |a| a.split(',') } 62 | 63 | def run 64 | # Metrics borrowed from hoardd: https://github.com/coredump/hoardd 65 | 66 | metrics = %w[rxBytes 67 | rxPackets 68 | rxErrors 69 | rxDrops 70 | rxFifo 71 | rxFrame 72 | rxCompressed 73 | rxMulticast 74 | txBytes 75 | txPackets 76 | txErrors 77 | txDrops 78 | txFifo 79 | txColls 80 | txCarrier 81 | txCompressed] 82 | 83 | File.open('/proc/net/dev', 'r').each_line do |line| 84 | interface, stats_string = line.scan(/^\s*([^:]+):\s*(.*)$/).first 85 | next if config[:excludeinterfaceregex] && (interface =~ /#{config[:excludeinterfaceregex]}/) 86 | next if config[:includeinterfaceregex] && (interface !~ /#{config[:includeinterfaceregex]}/) 87 | next if config[:excludeinterface] && config[:excludeinterface].find { |x| line.match(x) } 88 | next if config[:includeinterface] && !(config[:includeinterface].find { |x| line.match(x) }) 89 | next unless interface 90 | 91 | if interface.is_a?(String) 92 | interface = interface.tr('.', '_') 93 | end 94 | 95 | stats = stats_string.split(/\s+/) 96 | next if stats == ['0'].cycle.take(stats.size) 97 | 98 | metrics.size.times { |i| output "#{config[:scheme]}.#{interface}.#{metrics[i]}", stats[i] } 99 | end 100 | 101 | ok 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /bin/metrics-ping.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # metrics-ping 6 | # 7 | # DESCRIPTION: 8 | # This plugin pings a host and outputs ping statistics 9 | # 10 | # OUTPUT: 11 | # .packets_transmitted 5 1437137076 12 | # .packets_received 5 1437137076 13 | # .packet_loss 0 1437137076 14 | # .time 3996 1437137076 15 | # .min 0.016 1437137076 16 | # .max 0.017 1437137076 17 | # .avg 0.019 1437137076 18 | # .mdev 0.004 1437137076 19 | # 20 | # PLATFORMS: 21 | # Linux 22 | # 23 | # DEPENDENCIES: 24 | # gem: sensu-plugin 25 | # gem: open3 26 | # 27 | # WARNING: 28 | # This plugins requires `ping` binary from `iputils-ping` package. 29 | # `ping` binary from `inetutils-ping` package produces incompatible 30 | # output. For more info see: 31 | # https://github.com/sensu-plugins/sensu-plugins-network-checks/issues/26 32 | # 33 | # USAGE: 34 | # ./metric-ping --host --count \ 35 | # --timeout --scheme 36 | # 37 | # NOTES: 38 | # 39 | # LICENSE: 40 | # Copyright 2015 Rob Wilson 41 | # Released under the same terms as Sensu (the MIT license); see LICENSE 42 | # for details. 43 | # 44 | 45 | require 'sensu-plugin/metric/cli' 46 | require 'socket' 47 | require 'open3' 48 | 49 | class PingMetrics < Sensu::Plugin::Metric::CLI::Graphite 50 | option :scheme, 51 | description: 'Metric naming scheme, text to prepend to metric', 52 | short: '-s SCHEME', 53 | long: '--scheme SCHEME', 54 | default: "#{Socket.gethostname}.ping" 55 | 56 | option :host, 57 | description: 'Host to ping', 58 | short: '-h HOST', 59 | long: '--host HOST', 60 | default: 'localhost' 61 | 62 | option :count, 63 | description: 'Ping count', 64 | short: '-c COUNT', 65 | long: '--count COUNT', 66 | default: 5 67 | 68 | option :timeout, 69 | description: 'Timeout', 70 | short: '-t TIMEOUT', 71 | long: '--timeout TIMEOUT', 72 | default: 5 73 | 74 | OVERVIEW_METRICS = %i[packets_transmitted packets_received packet_loss time].freeze 75 | STATISTIC_METRICS = %i[min avg max mdev].freeze 76 | FLOAT = '(\d+\.\d+)' 77 | 78 | def overview 79 | @ping.split("\n")[-2].scan(/^(\d+) packets transmitted, (\d+) received, (\d+)% packet loss, time (\d+)ms/)[0] 80 | end 81 | 82 | def statistics 83 | @ping.split("\n")[-1].scan(/^rtt min\/avg\/max\/mdev = #{FLOAT}\/#{FLOAT}\/#{FLOAT}\/#{FLOAT} ms/)[0] 84 | end 85 | 86 | def results 87 | Hash[OVERVIEW_METRICS.zip(overview)].merge Hash[STATISTIC_METRICS.zip(statistics)] 88 | end 89 | 90 | def timestamp 91 | @timestamp ||= Time.now.to_i 92 | end 93 | 94 | def write_output 95 | results.each { |metric, value| output "#{config[:scheme]}.#{metric} #{value} #{timestamp}" } 96 | end 97 | 98 | def ping 99 | @ping, @status = Open3.capture2e("ping -W#{config[:timeout]} -c#{config[:count]} #{config[:host]}") 100 | end 101 | 102 | def validate 103 | critical "ping error: unable to ping #{config[:host]}" unless @status.success? 104 | end 105 | 106 | def run 107 | ping 108 | validate 109 | write_output 110 | ok 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /bin/check-ping.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-ping 6 | # 7 | # DESCRIPTION: 8 | # This is a simple Ping check script for Sensu. 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # gem: net-ping 19 | # 20 | # USAGE: 21 | # check-ping -h host -T timeout [--report] 22 | # 23 | # NOTES: 24 | # 25 | # LICENSE: 26 | # Deepak Mohan Dass 27 | # Released under the same terms as Sensu (the MIT license); see LICENSE 28 | # for details. 29 | # 30 | 31 | require 'sensu-plugin/check/cli' 32 | require 'net/ping' 33 | 34 | # 35 | # Check Ping 36 | # 37 | class CheckPING < Sensu::Plugin::Check::CLI 38 | option :host, 39 | short: '-h host', 40 | default: 'localhost' 41 | 42 | option :ipv6, 43 | short: '-6', 44 | long: '--ipv6', 45 | description: 'Ping IPv6 address', 46 | default: false 47 | 48 | option :timeout, 49 | short: '-T timeout', 50 | proc: proc(&:to_i), 51 | default: 5 52 | 53 | option :count, 54 | short: '-c count', 55 | description: 'The number of ping requests', 56 | proc: proc(&:to_i), 57 | default: 1 58 | 59 | option :interval, 60 | short: '-i interval', 61 | description: 'The number of seconds to wait between ping requests', 62 | proc: proc(&:to_f), 63 | default: 1 64 | 65 | option :warn_ratio, 66 | short: '-W ratio', 67 | description: 'Warn if successful ratio is under this value', 68 | proc: proc(&:to_f), 69 | default: 0.5 70 | 71 | option :critical_ratio, 72 | short: '-C ratio', 73 | description: 'Critical if successful ratio is under this value', 74 | proc: proc(&:to_f), 75 | default: 0.2 76 | 77 | option :report, 78 | short: '-r', 79 | long: '--report', 80 | description: 'Attach MTR report if ping is failed', 81 | default: false 82 | 83 | def run 84 | result = [] 85 | pt = Net::Ping::External.new(config[:host], nil, config[:timeout]) 86 | 87 | config[:count].times do |i| 88 | sleep(config[:interval]) unless i.zero? 89 | result[i] = config[:ipv6] ? pt.ping6 : pt.ping 90 | end 91 | 92 | successful_count = result.count(true) 93 | total_count = config[:count] 94 | success_ratio = successful_count / total_count.to_f 95 | 96 | if success_ratio > config[:warn_ratio] 97 | success_message = "ICMP ping successful for host: #{config[:host]}" 98 | ok success_message 99 | else 100 | failure_message = "ICMP ping unsuccessful for host: #{config[:host]} (successful: #{successful_count}/#{total_count})" 101 | 102 | if config[:report] 103 | mtr = `mtr --help` 104 | if mtr == 1 105 | unknown 'mtr is not available in $PATH' 106 | end 107 | report = `mtr --curses --report-cycles=1 --report --no-dns #{config[:host]}` 108 | failure_message = failure_message + "\n" + report 109 | end 110 | 111 | if success_ratio <= config[:critical_ratio] 112 | critical failure_message 113 | else 114 | warning failure_message 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /test/check-ports_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-ports_spec 6 | # 7 | # DESCRIPTION: 8 | # rspec tests for check-ports 9 | # 10 | # OUTPUT: 11 | # RSpec testing output: passes and failures info 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # rspec 18 | # 19 | # USAGE: 20 | # For Rspec Testing 21 | # 22 | # NOTES: 23 | # For Rspec Testing 24 | # 25 | # LICENSE: 26 | # Copyright 2015 Robin 27 | # Released under the same terms as Sensu (the MIT license); see LICENSE 28 | # for details. 29 | # 30 | 31 | require_relative '../bin/check-ports' 32 | require_relative './spec_helper.rb' 33 | 34 | describe CheckPort do 35 | let(:checker_tcp) { described_class.new } 36 | let(:checker_udp) { described_class.new } 37 | 38 | ## Simulate the system when you connect tcp 39 | before(:each) do 40 | # Default config 41 | checker_tcp.config[:port] = 80 42 | checker_tcp.config[:host] = 'localhost' 43 | checker_tcp.config[:timeout] = 2 44 | def checker_tcp.ok(*_args) 45 | exit 0 46 | end 47 | 48 | def checker_tcp.warning(*_args) 49 | exit 1 50 | end 51 | 52 | def checker_tcp.critical(*_args) 53 | exit 2 54 | end 55 | end 56 | 57 | it 'returns ok by default with local http service' do 58 | begin 59 | allow(TCPSocket).to receive(:new).and_return(true) 60 | checker_tcp.run 61 | rescue SystemExit => e 62 | exit_code = e.status 63 | end 64 | expect(exit_code).to eq 0 65 | end 66 | 67 | it 'returns ok by default with both ssh and local http service' do 68 | checker_tcp.config[:host] = 22, 80 69 | begin 70 | allow(TCPSocket).to receive(:new).and_return(true) 71 | checker_tcp.run 72 | rescue SystemExit => e 73 | exit_code = e.status 74 | end 75 | expect(exit_code).to eq 0 76 | end 77 | 78 | it 'returns critical because of connection refused' do 79 | begin 80 | allow(TCPSocket).to receive(:new) { raise Errno::ECONNREFUSED } 81 | checker_tcp.run 82 | rescue SystemExit => e 83 | exit_code = e.status 84 | end 85 | expect(exit_code).to eq 2 86 | end 87 | 88 | it 'returns critical because of timeout' do 89 | begin 90 | allow(TCPSocket).to receive(:new) { raise Timeout::Error } 91 | checker_tcp.run 92 | rescue SystemExit => e 93 | exit_code = e.status 94 | end 95 | expect(exit_code).to eq 2 96 | end 97 | 98 | it 'returns critical because of no route to host' do 99 | begin 100 | allow(TCPSocket).to receive(:new) { raise Errno::EHOSTUNREACH } 101 | checker_tcp.run 102 | rescue SystemExit => e 103 | exit_code = e.status 104 | end 105 | expect(exit_code).to eq 2 106 | end 107 | 108 | it 'returns critical because of conn closed' do 109 | begin 110 | allow(TCPSocket).to receive(:new) { raise EOFError } 111 | checker_tcp.run 112 | rescue SystemExit => e 113 | exit_code = e.status 114 | end 115 | expect(exit_code).to eq 2 116 | end 117 | 118 | ## Simulate the system when you connect using udp 119 | before(:each) do 120 | # Default config 121 | checker_udp.config[:port] = 123 122 | checker_udp.config[:host] = 'localhost' 123 | checker_udp.config[:timeout] = 2 124 | checker_udp.config[:proto] = 'udp' 125 | def checker_udp.ok(*_args) 126 | exit 0 127 | end 128 | 129 | def checker_udp.warning(*_args) 130 | exit 1 131 | end 132 | 133 | def checker_udp.critical(*_args) 134 | exit 2 135 | end 136 | end 137 | 138 | it 'returns ok by default with local http service' do 139 | begin 140 | allow(UDPSocket.open).to receive(:connect).and_return(true) 141 | checker_udp.run 142 | rescue SystemExit => e 143 | exit_code = e.status 144 | end 145 | expect(exit_code).to eq 0 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /bin/check-netstat-tcp.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-netstat-tcp 6 | # 7 | # DESCRIPTION: 8 | # Alert based on thresholds of discrete TCP socket states reported by netstat 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # $ ./check-netstat-tcp.rb --states ESTABLISHED,CLOSE_WAIT --warning 10,3 --critical 100,30 21 | # 22 | # NOTES: 23 | # - Thanks to metric-netstat-tcp.rb! 24 | # https://github.com/sensu/sensu-community-plugins 25 | # - Code for parsing Linux /proc/net/tcp from Anthony Goddard's ruby-netstat: 26 | # https://github.com/agoddard/ruby-netstat 27 | # 28 | # LICENSE: 29 | # Released under the same terms as Sensu (the MIT license); see LICENSE 30 | # for details. 31 | # 32 | 33 | require 'sensu-plugin/check/cli' 34 | require 'socket' 35 | 36 | TCP_STATES = { 37 | '00' => 'UNKNOWN', # Bad state ... Impossible to achieve ... 38 | 'FF' => 'UNKNOWN', # Bad state ... Impossible to achieve ... 39 | '01' => 'ESTABLISHED', 40 | '02' => 'SYN_SENT', 41 | '03' => 'SYN_RECV', 42 | '04' => 'FIN_WAIT1', 43 | '05' => 'FIN_WAIT2', 44 | '06' => 'TIME_WAIT', 45 | '07' => 'CLOSE', 46 | '08' => 'CLOSE_WAIT', 47 | '09' => 'LAST_ACK', 48 | '0A' => 'LISTEN', 49 | '0B' => 'CLOSING' 50 | }.freeze 51 | 52 | # 53 | # Check Netstat TCP 54 | # 55 | class CheckNetstatTCP < Sensu::Plugin::Check::CLI 56 | option :states, 57 | description: 'Comma delimited list of states to check', 58 | short: '-s STATES', 59 | long: '--states STATES', 60 | default: ['ESTABLISHED'], 61 | proc: proc { |a| a.split(',') } 62 | 63 | option :critical, 64 | description: "Comma delimited list of state values to set critical at (order follows 'states')", 65 | short: '-c CRITICAL', 66 | long: '--critical CRITICAL', 67 | default: [1000], 68 | proc: proc { |a| a.split(',').map(&:to_i) } 69 | 70 | option :warning, 71 | description: "Comma delimited list of state values to set warning at (order follows 'states')", 72 | short: '-w WARNING', 73 | long: '--warning WARNING', 74 | default: [500], 75 | proc: proc { |a| a.split(',').map(&:to_i) } 76 | 77 | option :port, 78 | description: 'Port you wish to check values on (default: all)', 79 | short: '-p PORT', 80 | long: '--port PORT', 81 | proc: proc(&:to_i) 82 | 83 | def netstat(protocols = ['tcp']) 84 | state_counts = Hash.new(0) 85 | TCP_STATES.each_pair { |_hex, name| state_counts[name] = 0 } 86 | 87 | protocols.select { |p| File.exist?('/proc/net/' + p) }.each do |protocol| 88 | File.open('/proc/net/' + protocol).each do |line| 89 | line.strip! 90 | if m = line.match(/^\s*\d+:\s+(.{8}|.{32}):(.{4})\s+(.{8}|.{32}):(.{4})\s+(.{2})/) # rubocop:disable AssignmentInCondition 91 | connection_state = m[5] 92 | connection_port = m[2].to_i(16) 93 | connection_state = TCP_STATES[connection_state] 94 | next unless config[:states].include?(connection_state) 95 | 96 | if config[:port] && config[:port] == connection_port 97 | state_counts[connection_state] += 1 98 | elsif !config[:port] 99 | state_counts[connection_state] += 1 100 | end 101 | end 102 | end 103 | end 104 | state_counts 105 | end 106 | 107 | def run 108 | state_counts = netstat(%w[tcp tcp6]) 109 | is_critical = false 110 | is_warning = false 111 | message = '' 112 | 113 | config[:states].each_index do |i| 114 | if state_counts[config[:states][i]] >= config[:critical][i] 115 | is_critical = true 116 | message += " CRITICAL:#{config[:states][i]}=#{state_counts[config[:states][i]]}" 117 | elsif state_counts[config[:states][i]] >= config[:warning][i] 118 | is_warning = true 119 | message += " WARNING:#{config[:states][i]}=#{state_counts[config[:states][i]]}" 120 | else 121 | message += " OK:#{config[:states][i]}=#{state_counts[config[:states][i]]}" 122 | end 123 | end 124 | 125 | critical message if is_critical 126 | warning message if is_warning 127 | ok message 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /bin/metrics-net.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # metrics-net 6 | # 7 | # DESCRIPTION: 8 | # Simple plugin that fetchs metrics from all interfaces 9 | # on the box using the /sys/class interface. 10 | # 11 | # Use the data with graphite's `nonNegativeDerivative()` function 12 | # to construct per-second graphs for your hosts. 13 | # 14 | # Loopback iface (`lo`) is ignored. 15 | # 16 | # Compat 17 | # ------ 18 | # 19 | # This plugin uses the `/sys/class/net//statistics/{rx,tx}_*` 20 | # files to fetch stats. On older linux boxes without /sys, this same 21 | # info can be fetched from /proc/net/dev but additional parsing 22 | # will be required. 23 | # 24 | # OUTPUT: 25 | # metric data 26 | # 27 | # PLATFORMS: 28 | # Linux 29 | # 30 | # DEPENDENCIES: 31 | # gem: sensu-plugin 32 | # 33 | # USAGE: 34 | # $ ./metrics-packets.rb --scheme servers.web01 35 | # servers.web01.eth0.tx_packets 982965 1351112745 36 | # servers.web01.eth0.rx_packets 1180186 1351112745 37 | # servers.web01.eth1.tx_packets 273936669 1351112745 38 | # servers.web01.eth1.rx_packets 563787422 1351112745 39 | # 40 | # NOTES: 41 | # Does it behave differently on specific platforms, specific use cases, etc. 42 | # Devices can be specifically included or ignored using -i or -I options: 43 | # e.g. metrics-net.rb -i veth,dummy 44 | # 45 | # LICENSE: 46 | # Copyright 2012 Joe Miller 47 | # Released under the same terms as Sensu (the MIT license); see LICENSE 48 | # for details. 49 | # 50 | 51 | require 'sensu-plugin/metric/cli' 52 | require 'socket' 53 | 54 | # 55 | # Linux Packet Metrics 56 | # 57 | class LinuxPacketMetrics < Sensu::Plugin::Metric::CLI::Graphite 58 | option :scheme, 59 | description: 'Metric naming scheme, text to prepend to metric', 60 | short: '-s SCHEME', 61 | long: '--scheme SCHEME', 62 | default: "#{Socket.gethostname}.net" 63 | 64 | option :ignore_device, 65 | description: 'Ignore devices matching pattern(s)', 66 | short: '-i DEV[,DEV]', 67 | long: '--ignore-device', 68 | proc: proc { |a| a.split(',') } 69 | 70 | option :include_device, 71 | description: 'Include only devices matching pattern(s)', 72 | short: '-I DEV[,DEV]', 73 | long: '--include-device', 74 | proc: proc { |a| a.split(',') } 75 | 76 | option :only_up, 77 | description: 'Include only devices whose interface status is up', 78 | short: '-u', 79 | long: '--only-up' 80 | 81 | def run 82 | timestamp = Time.now.to_i 83 | 84 | Dir.glob('/sys/class/net/*').each do |iface_path| 85 | next if File.file?(iface_path) 86 | 87 | iface = File.basename(iface_path) 88 | next if iface == 'lo' 89 | 90 | next if config[:ignore_device] && config[:ignore_device].find { |x| iface.match(x) } 91 | next if config[:include_device] && !config[:include_device].find { |x| iface.match(x) } 92 | next if config[:only_up] && File.open(iface_path + '/operstate').read.strip != 'up' 93 | 94 | tx_pkts = File.open(iface_path + '/statistics/tx_packets').read.strip 95 | rx_pkts = File.open(iface_path + '/statistics/rx_packets').read.strip 96 | tx_bytes = File.open(iface_path + '/statistics/tx_bytes').read.strip 97 | rx_bytes = File.open(iface_path + '/statistics/rx_bytes').read.strip 98 | tx_errors = File.open(iface_path + '/statistics/tx_errors').read.strip 99 | rx_errors = File.open(iface_path + '/statistics/rx_errors').read.strip 100 | 101 | begin 102 | if_speed = File.open(iface_path + '/speed').read.strip 103 | rescue StandardError 104 | if_speed = 0 105 | end 106 | 107 | output "#{config[:scheme]}.#{iface}.tx_packets", tx_pkts, timestamp 108 | output "#{config[:scheme]}.#{iface}.rx_packets", rx_pkts, timestamp 109 | output "#{config[:scheme]}.#{iface}.tx_bytes", tx_bytes, timestamp 110 | output "#{config[:scheme]}.#{iface}.rx_bytes", rx_bytes, timestamp 111 | output "#{config[:scheme]}.#{iface}.tx_errors", tx_errors, timestamp 112 | output "#{config[:scheme]}.#{iface}.rx_errors", rx_errors, timestamp 113 | output "#{config[:scheme]}.#{iface}.if_speed", if_speed, timestamp 114 | end 115 | ok 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /bin/metrics-netstat-tcp.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # metrics-netstat-tcp 6 | # 7 | # DESCRIPTION: 8 | # Fetch metrics on TCP socket states from netstat. This is particularly useful 9 | # on high-traffic web or proxy servers with large numbers of short-lived TCP 10 | # connections coming and going. 11 | # 12 | # OUTPUT: 13 | # metric data 14 | # 15 | # PLATFORMS: 16 | # Linux 17 | # 18 | # DEPENDENCIES: 19 | # gem: sensu-plugin 20 | # 21 | # USAGE: 22 | # $ ./metrics-netstat-tcp.rb --scheme servers.hostname 23 | # servers.hostname.UNKNOWN 0 1350496466 24 | # servers.hostname.ESTABLISHED 235 1350496466 25 | # servers.hostname.SYN_SENT 0 1350496466 26 | # servers.hostname.SYN_RECV 1 1350496466 27 | # servers.hostname.FIN_WAIT1 0 1350496466 28 | # servers.hostname.FIN_WAIT2 53 1350496466 29 | # servers.hostname.TIME_WAIT 10640 1350496466 30 | # servers.hostname.CLOSE 0 1350496466 31 | # servers.hostname.CLOSE_WAIT 7 1350496466 32 | # servers.hostname.LAST_ACK 1 1350496466 33 | # servers.hostname.LISTEN 16 1350496466 34 | # servers.hostname.CLOSING 0 1350496466 35 | # 36 | # NOTES: 37 | # - Code for parsing Linux /proc/net/tcp from Anthony Goddard's ruby-netstat: 38 | # https://github.com/agoddard/ruby-netstat 39 | # 40 | # LICENSE: 41 | # Copyright 2012 Joe Miller 42 | # Released under the same terms as Sensu (the MIT license); see LICENSE 43 | # for details. 44 | # 45 | 46 | require 'sensu-plugin/metric/cli' 47 | require 'socket' 48 | 49 | TCP_STATES = { 50 | '00' => 'UNKNOWN', # Bad state ... Impossible to achieve ... 51 | 'FF' => 'UNKNOWN', # Bad state ... Impossible to achieve ... 52 | '01' => 'ESTABLISHED', 53 | '02' => 'SYN_SENT', 54 | '03' => 'SYN_RECV', 55 | '04' => 'FIN_WAIT1', 56 | '05' => 'FIN_WAIT2', 57 | '06' => 'TIME_WAIT', 58 | '07' => 'CLOSE', 59 | '08' => 'CLOSE_WAIT', 60 | '09' => 'LAST_ACK', 61 | '0A' => 'LISTEN', 62 | '0B' => 'CLOSING' 63 | }.freeze 64 | 65 | # 66 | # Netstat TCP Metrics 67 | # 68 | class NetstatTCPMetrics < Sensu::Plugin::Metric::CLI::Graphite 69 | option :scheme, 70 | description: 'Metric naming scheme, text to prepend to metric', 71 | short: '-s SCHEME', 72 | long: '--scheme SCHEME', 73 | default: "#{Socket.gethostname}.tcp" 74 | 75 | option :port, 76 | description: 'Port you wish to get metrics for', 77 | short: '-p PORT', 78 | long: '--port PORT', 79 | proc: proc(&:to_i) 80 | 81 | option :type, 82 | description: 'Specify the type of the port to get metrics for: Local (default) or remote', 83 | short: '-t local|remote', 84 | long: '--type local|remote', 85 | default: 'local' 86 | 87 | option :disabletcp6, 88 | description: 'Disable tcp6 check', 89 | short: '-d', 90 | long: '--disabletcp6', 91 | boolean: true 92 | 93 | def netstat(protocol, pattern, state_counts) 94 | File.open('/proc/net/' + protocol).each do |line| 95 | line.strip! 96 | if m = line.match(pattern) # rubocop:disable AssignmentInCondition 97 | connection_state = m[5] 98 | if config[:type] == 'local' 99 | connection_port = m[2].to_i(16) 100 | elsif config[:type] == 'remote' 101 | connection_port = m[4].to_i(16) 102 | else 103 | unknown "Unknown type level #{config[:type]}. Available values are: local, remote." 104 | end 105 | connection_state = TCP_STATES[connection_state] 106 | if config[:port] && config[:port] == connection_port 107 | state_counts[connection_state] += 1 108 | elsif !config[:port] 109 | state_counts[connection_state] += 1 110 | end 111 | end 112 | end 113 | state_counts 114 | end 115 | 116 | def run 117 | timestamp = Time.now.to_i 118 | state_counts = Hash.new(0) 119 | TCP_STATES.each_pair { |_hex, name| state_counts[name] = 0 } 120 | 121 | tcp4_pattern = /^\s*\d+:\s+(.{8}):(.{4})\s+(.{8}):(.{4})\s+(.{2})/ 122 | state_counts = netstat('tcp', tcp4_pattern, state_counts) 123 | 124 | unless config[:disabletcp6] 125 | tcp6_pattern = /^\s*\d+:\s+(.{32}):(.{4})\s+(.{32}):(.{4})\s+(.{2})/ 126 | state_counts = netstat('tcp6', tcp6_pattern, state_counts) 127 | end 128 | 129 | state_counts.each do |state, count| 130 | graphite_name = config[:port] ? "#{config[:scheme]}.#{config[:port]}.#{config[:type]}.#{state}" : 131 | "#{config[:scheme]}.#{state}" 132 | output graphite_name.to_s, count, timestamp 133 | end 134 | ok 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /bin/check-whois-domain-expiration-multi.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: false 3 | 4 | # 5 | # check-whois-domain-expiration-multi 6 | # 7 | # DESCRIPTION: 8 | # This plugin checks domain expiration dates using the 'whois' gem. 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # *nix systems 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # gem: whois 19 | # 20 | # USAGE: 21 | # ./check-whois-domain-expiration-multi.rb -d therealtimsmith.com,mightyoakspreschool.com 22 | # WhoisDomainExpirationCheck WARNING: mightyoakspreschool.com: 30 days 23 | # 24 | # LICENSE: 25 | # Copyright 2015 Tim Smith (tim@cozy.co) - Cozy Services Ltd. 26 | # Based on check-whois-domain-expiration, Copyright 2015 michael j talarczyk 27 | # and contributors. 28 | # Released under the same terms as Sensu (the MIT license); see LICENSE 29 | # for details. 30 | 31 | require 'sensu-plugin/check/cli' 32 | require 'whois' 33 | require 'whois-parser' 34 | 35 | # 36 | # Check Whois domain expiration 37 | # 38 | class WhoisDomainExpirationCheck < Sensu::Plugin::Check::CLI 39 | option :domain, 40 | short: '-d DOMAINS', 41 | long: '--domains DOMAIN', 42 | required: true, 43 | description: 'Domain(s) to check. Separate by commas for 2+' 44 | 45 | option :warning, 46 | short: '-w DAYS', 47 | long: '--warn DAYS', 48 | default: 30, 49 | proc: proc(&:to_i), 50 | description: 'Warn if fewer than DAYS away' 51 | 52 | option :critical, 53 | short: '-c DAYS', 54 | long: '--critical DAYS', 55 | proc: proc(&:to_i), 56 | default: 7, 57 | description: 'Critical if fewer than DAYS away' 58 | 59 | option :'ignore-errors', 60 | short: '-i', 61 | long: '--ignore-errors', 62 | boolean: true, 63 | default: false, 64 | description: 'Ignore connection or parsing errors' 65 | 66 | option :'report-errors', 67 | short: '-r LEVEL', 68 | long: '--report-errors LEVEL', 69 | proc: proc(&:to_sym), 70 | in: %i[unknown warning critical], 71 | default: :unknown, 72 | description: 'Level for reporting connection or parsing errors' 73 | 74 | option :timeout, 75 | short: '-t SECONDS', 76 | long: '--timeout SECONDS', 77 | proc: proc(&:to_i), 78 | default: 10, 79 | description: 'Timeout for whois lookup' 80 | 81 | option :help, 82 | short: '-h', 83 | long: '--help', 84 | description: 'Show this message', 85 | on: :tail, 86 | boolean: true, 87 | show_options: true, 88 | exit: 0 89 | 90 | # split the provided domain list and perform whois lookups on each 91 | # return a hash with domains grouped by their status level 92 | def expiration_results 93 | domains = config[:domain].split(',') 94 | warning_days = config[:warning].to_i 95 | critical_days = config[:critical].to_i 96 | max_retries = 4 97 | 98 | results = { 99 | critical: {}, 100 | warning: {}, 101 | ok: {}, 102 | unknown: {} 103 | } 104 | whois = Whois::Client.new(timeout: config[:timeout]) 105 | 106 | domains.each do |domain| 107 | begin 108 | tries ||= 0 109 | whois_result = whois.lookup(domain).parser 110 | rescue Timeout::Error, Errno::ECONNRESET, Whois::ConnectionError 111 | tries += 1 112 | if tries < max_retries 113 | retry 114 | else 115 | results[:unknown][domain] = 'Connection error' unless config[:'ignore-errors'] 116 | next 117 | end 118 | end 119 | 120 | begin 121 | expires_on = DateTime.parse(whois_result.expires_on.to_s) 122 | domain_result = (expires_on - DateTime.now).to_i 123 | if domain_result <= critical_days 124 | results[:critical][domain] = domain_result 125 | elsif domain_result <= warning_days 126 | results[:warning][domain] = domain_result 127 | else 128 | results[:ok][domain] = domain_result 129 | end 130 | rescue StandardError 131 | results[:unknown][domain] = 'Parsing error' unless config[:'ignore-errors'] 132 | end 133 | end 134 | results 135 | end 136 | 137 | def run 138 | results = expiration_results 139 | 140 | warn_results = results[:critical].merge(results[:warning]).map { |u, v| "#{u} (#{v} days left)" } 141 | unknown_results = results[:unknown].map { |u, v| "#{u} (#{v})" } 142 | message warn_results.concat(unknown_results).join(', ') 143 | 144 | if !results[:critical].empty? || (!results[:unknown].empty? && config[:'report-errors'] == :critical) 145 | critical 146 | elsif !results[:warning].empty? || (!results[:unknown].empty? && config[:'report-errors'] == :warning) 147 | warning 148 | elsif !results[:unknown].empty? 149 | unknown 150 | else 151 | ok 'No domains expire in the near term' 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /test/check-mtu_spec.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-mtu_spec 6 | # 7 | # DESCRIPTION: 8 | # rspect tests for check-mtu 9 | # 10 | # OUTPUT: 11 | # RSpec testing output: passes and failures info 12 | # 13 | # PLATFORMS: 14 | # Linux 15 | # 16 | # DEPENDENCIES: 17 | # rspec 18 | # 19 | # USAGE: 20 | # For Rspec Testing 21 | # 22 | # NOTES: 23 | # For Rspec Testing 24 | # 25 | # LICENSE: 26 | # Copyright 2015 Robin 27 | # Released under the same terms as Sensu (the MIT license); see LICENSE 28 | # for details. 29 | # 30 | 31 | require_relative '../bin/check-mtu' 32 | require_relative './spec_helper.rb' 33 | 34 | describe CheckMTU do 35 | let(:checker) { described_class.new } 36 | let(:checker_9000) { described_class.new } 37 | let(:checker_no_file) { described_class.new } 38 | let(:exit_code) { nil } 39 | 40 | ## Simulate the system MTU to be 1500 41 | before(:each) do 42 | def checker.locate_mtu_file 43 | 'test/fixtures/check-mtu-1500' 44 | end 45 | 46 | def checker.ok(*_args) 47 | exit 0 48 | end 49 | 50 | def checker.warning(*_args) 51 | exit 1 52 | end 53 | 54 | def checker.critical(*_args) 55 | exit 2 56 | end 57 | end 58 | 59 | it 'returns ok by default with 1500 MTU ' do 60 | begin 61 | checker.run 62 | rescue SystemExit => e 63 | exit_code = e.status 64 | end 65 | expect(exit_code).to eq 0 66 | end 67 | 68 | it 'returns ok by default with 1500 MTU (with warn setting)' do 69 | checker.config[:warn] = true 70 | begin 71 | checker.run 72 | rescue SystemExit => e 73 | exit_code = e.status 74 | end 75 | expect(exit_code).to eq 0 76 | end 77 | 78 | it 'returns critical if we ask it to check for 9000 while it has 1500 MTU interface' do 79 | checker.config[:mtu] = 9000 80 | begin 81 | checker.run 82 | rescue SystemExit => e 83 | exit_code = e.status 84 | end 85 | expect(exit_code).to eq 2 86 | end 87 | 88 | it 'returns critical if we ask it to check for 9000 while it has 1500 MTU interface (with warn setting)' do 89 | checker.config[:mtu] = 9000 90 | checker.config[:warn] = true 91 | begin 92 | checker.run 93 | rescue SystemExit => e 94 | exit_code = e.status 95 | end 96 | expect(exit_code).to eq 1 97 | end 98 | 99 | ## Simulate system MTU to be 9000 100 | before(:each) do 101 | def checker_9000.locate_mtu_file 102 | 'test/fixtures/check-mtu-9000' 103 | end 104 | 105 | def checker_9000.ok(*_args) 106 | exit 0 107 | end 108 | 109 | def checker_9000.warning(*_args) 110 | exit 1 111 | end 112 | 113 | def checker_9000.critical(*_args) 114 | exit 2 115 | end 116 | end 117 | 118 | it 'returns critical if we ask it to check for 1500 MTU while we have 9000 MTU interface' do 119 | begin 120 | checker_9000.run 121 | rescue SystemExit => e 122 | exit_code = e.status 123 | end 124 | expect(exit_code).to eq 2 125 | end 126 | 127 | it 'returns warning if we ask it to check for 1500 MTU while we have 9000 MTU interface (with warn setting on)' do 128 | checker_9000.config[:warn] = true 129 | begin 130 | checker_9000.run 131 | rescue SystemExit => e 132 | exit_code = e.status 133 | end 134 | expect(exit_code).to eq 1 135 | end 136 | 137 | it 'returns ok if we ask it to check for 9000 MTU while we have 9000 MTU interface' do 138 | checker_9000.config[:mtu] = 9000 139 | begin 140 | checker_9000.run 141 | rescue SystemExit => e 142 | exit_code = e.status 143 | end 144 | expect(exit_code).to eq 0 145 | end 146 | 147 | it 'returns ok if we ask it to check for 9000 MTU while we have 9000 MTU interface (with warn setting on)' do 148 | checker_9000.config[:warn] = true 149 | checker_9000.config[:mtu] = 9000 150 | begin 151 | checker_9000.run 152 | rescue SystemExit => e 153 | exit_code = e.status 154 | end 155 | expect(exit_code).to eq 0 156 | end 157 | 158 | ## This should never happen. This simulates a situation (which may not happen) whereby a system's MTU info cannot be read 159 | before(:each) do 160 | def checker_no_file.locate_mtu_file 161 | 'no_existing_file' 162 | end 163 | 164 | def checker_no_file.ok(*_args) 165 | exit 0 166 | end 167 | 168 | def checker_no_file.warning(*_args) 169 | exit 1 170 | end 171 | 172 | def checker_no_file.critical(*_args) 173 | exit 2 174 | end 175 | end 176 | 177 | it 'returns critical' do 178 | begin 179 | checker_no_file.run 180 | rescue SystemExit => e 181 | exit_code = e.status 182 | end 183 | expect(exit_code).to eq 2 184 | end 185 | 186 | it 'returns warning (with warn setting is on)' do 187 | checker_no_file.config[:warn] = true 188 | begin 189 | checker_no_file.run 190 | rescue SystemExit => e 191 | exit_code = e.status 192 | end 193 | expect(exit_code).to eq 1 194 | end 195 | end 196 | -------------------------------------------------------------------------------- /bin/check-jsonwhois-domain-expiration.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: false 3 | 4 | # 5 | # check-jsonwhois-domain-expiration 6 | # 7 | # DESCRIPTION: 8 | # This plugin checks domain expiration dates using the https://jsonwhois.com API 9 | # 10 | # OUTPUT: 11 | # plain text 12 | # 13 | # PLATFORMS: 14 | # Any 15 | # 16 | # DEPENDENCIES: 17 | # gem: sensu-plugin 18 | # 19 | # USAGE: 20 | # ./check-jsonwhois-domain-expiration.rb -a [YOUR API KEY] -d foo.com,bar.com 21 | # JSONWhoisDomainExpirationCheck WARNING: foo.com: 30 days 22 | # 23 | # LICENSE: 24 | # Copyright 2015 Matt Greensmith (mgreensmith@cozy.co) - Cozy Services Ltd. 25 | # Based on check-whois-domain-expiration-multi, Copyright 2015 Tim Smith (tim@cozy.co) - Cozy Services Ltd. 26 | # Based on check-whois-domain-expiration, Copyright 2015 michael j talarczyk 27 | # and contributors. 28 | # Released under the same terms as Sensu (the MIT license); see LICENSE 29 | # for details. 30 | 31 | require 'sensu-plugin/check/cli' 32 | 33 | require 'net/http' 34 | require 'json' 35 | require 'date' 36 | 37 | # 38 | # Check Whois domain expiration 39 | # 40 | class JSONWhoisDomainExpirationCheck < Sensu::Plugin::Check::CLI 41 | option :domain, 42 | short: '-d DOMAINS', 43 | long: '--domains DOMAIN', 44 | required: true, 45 | description: 'Comma-separated list of domains to check.' 46 | 47 | option :apikey, 48 | short: '-a APIKEY', 49 | long: '--apikey APIKEY', 50 | required: true, 51 | description: 'API key for jsonwhois.com' 52 | 53 | option :warning, 54 | short: '-w DAYS', 55 | long: '--warn DAYS', 56 | default: 30, 57 | description: 'Warn if a domain expires in fewer than DAYS days' 58 | 59 | option :critical, 60 | short: '-c DAYS', 61 | long: '--critical DAYS', 62 | default: 7, 63 | description: 'Critical if a domain expires in fewer than DAYS days' 64 | 65 | option :'ignore-errors', 66 | short: '-i', 67 | long: '--ignore-errors', 68 | boolean: true, 69 | default: false, 70 | description: 'Ignore connection or parsing errors' 71 | 72 | option :'report-errors', 73 | short: '-r LEVEL', 74 | long: '--report-errors LEVEL', 75 | proc: proc(&:to_sym), 76 | in: %i[unknown warning critical], 77 | default: :unknown, 78 | description: 'Level for reporting connection or parsing errors' 79 | 80 | option :help, 81 | short: '-h', 82 | long: '--help', 83 | description: 'Show this message', 84 | on: :tail, 85 | boolean: true, 86 | show_options: true, 87 | exit: 0 88 | 89 | # Split the provided domain list and perform whois lookups on each 90 | # 91 | # @return [Hash] a hash of severity_level -> domain -> days until expiry 92 | def expiration_results 93 | domains = config[:domain].split(',') 94 | warning_days = config[:warning].to_i 95 | critical_days = config[:critical].to_i 96 | results = { 97 | critical: {}, 98 | warning: {}, 99 | ok: {}, 100 | unknown: {} 101 | } 102 | 103 | domains.each do |domain| 104 | begin 105 | expires_on = get_domain_expiration(domain) 106 | domain_result = (expires_on - DateTime.now).to_i 107 | if domain_result <= critical_days 108 | results[:critical][domain] = domain_result 109 | elsif domain_result <= warning_days 110 | results[:warning][domain] = domain_result 111 | else 112 | results[:ok][domain] = domain_result 113 | end 114 | rescue StandardError 115 | results[:unknown][domain] = 'Connection or parsing error' unless config[:'ignore-errors'] 116 | end 117 | end 118 | results 119 | end 120 | 121 | # Fetch whois data from the JSONWhois API and return the "expires_on" date 122 | # 123 | # @param domain [String] the domain to fetch from JSONWhois 124 | # @return [DateTime] the expiration date for the domain 125 | def get_domain_expiration(domain) 126 | uri = URI('https://jsonwhois.com/api/v1/whois') 127 | uri.query = URI.encode_www_form(domain: domain) 128 | req = Net::HTTP::Get.new(uri) 129 | req['Authorization'] = "Token token=#{config[:apikey]}" 130 | 131 | res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| 132 | http.request(req) 133 | end 134 | 135 | json = JSON.parse(res.body) 136 | DateTime.parse(json['expires_on']) 137 | end 138 | 139 | def run 140 | results = expiration_results 141 | 142 | warn_results = results[:critical].merge(results[:warning]).map { |u, v| "#{u} (#{v} days left)" } 143 | unknown_results = results[:unknown].map { |u, v| "#{u} (#{v})" } 144 | message warn_results.concat(unknown_results).join(', ') 145 | 146 | if !results[:critical].empty? || (!results[:unknown].empty? && config[:'report-errors'] == :critical) 147 | critical 148 | elsif !results[:warning].empty? || (!results[:unknown].empty? && config[:'report-errors'] == :warning) 149 | warning 150 | elsif !results[:unknown].empty? 151 | unknown 152 | else 153 | ok 'No domains expire in the near term' 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /bin/check-banner.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-banner 6 | # 7 | # DESCRIPTION: 8 | # Connect to a TCP port on one or more hosts, read till a char (EOF, EOL, etc) 9 | # and test output against a pattern. 10 | # 11 | # Useful for SSH, ZooKeeper, etc. 12 | # 13 | # OUTPUT: 14 | # plain text 15 | # 16 | # PLATFORMS: 17 | # Linux 18 | # 19 | # DEPENDENCIES: 20 | # gem: sensu-plugin 21 | # 22 | # USAGE: 23 | # 24 | # To check there is only one zk leader 25 | # ./check-banner.rb -h 'zk01,z02,zk03' -p 2181 -q leader -c 1 -w mntr -r EOF -O "ZK has one leader" 26 | # -C "ZK has zero or more than one leader" 27 | # 28 | # NOTES: 29 | # By default, checks for openssh on localhost port 22 and reads till EOL 30 | # 31 | # 32 | # LICENSE: 33 | # Copyright 2012 Sonian, Inc 34 | # Modified by Raghu Udiyar 35 | # Released under the same terms as Sensu (the MIT license); see LICENSE 36 | # for details. 37 | 38 | require 'openssl' 39 | require 'sensu-plugin/check/cli' 40 | require 'socket' 41 | require 'timeout' 42 | 43 | # 44 | # Check Banner 45 | # 46 | class CheckBanner < Sensu::Plugin::Check::CLI 47 | option :hosts, 48 | short: '-H HOSTNAME(S)', 49 | long: '--hostnames HOSTNAME(S)', 50 | description: 'Host(s) to connect to, comma seperated', 51 | default: 'localhost' 52 | 53 | option :port, 54 | short: '-p PORT', 55 | long: '--port PORT', 56 | proc: proc(&:to_i), 57 | default: 22 58 | 59 | option :write, 60 | short: '-w STRING', 61 | long: '--write STRING', 62 | description: 'write STRING to the socket' 63 | 64 | option :exclude_newline, 65 | short: '-e', 66 | long: '--exclude_newline', 67 | description: 'Exclude newline character at end of write STRING', 68 | boolean: true, 69 | default: false 70 | 71 | option :pattern, 72 | short: '-q PAT', 73 | long: '--pattern PAT', 74 | description: 'Pattern to search for' 75 | 76 | option :timeout, 77 | short: '-t SECS', 78 | long: '--timeout SECS', 79 | description: 'Connection timeout', 80 | proc: proc(&:to_i), 81 | default: 30 82 | 83 | option :count_match, 84 | short: '-c NUMBER', 85 | long: '--count NUMBER', 86 | description: 'Number of successful matches, default(1)', 87 | proc: proc(&:to_i), 88 | default: 1 89 | 90 | option :read_till, 91 | short: '-r CHAR', 92 | long: '--readtill CHAR', 93 | description: 'Read till CHAR is reached', 94 | default: "\n" 95 | 96 | option :ok_message, 97 | short: '-O MESSAGE', 98 | long: '--okmessage MESSAGE', 99 | description: 'Custom ok message to send' 100 | 101 | option :crit_message, 102 | short: '-C MESSAGE', 103 | long: '--critmessage MESSAGE', 104 | description: 'Custom critical message to send' 105 | 106 | option :ssl, 107 | short: '-S', 108 | long: '--ssl', 109 | description: 'Enable SSL socket for secure connection' 110 | 111 | def acquire_banner(host) 112 | Timeout.timeout(config[:timeout]) do 113 | sock = TCPSocket.new(host, config[:port]) 114 | 115 | if config[:ssl] 116 | ssl_context = OpenSSL::SSL::SSLContext.new 117 | sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context) 118 | sock.connect 119 | end 120 | 121 | if config[:write] 122 | sock.write config[:write] 123 | sock.write "\n" unless config[:exclude_newline] 124 | end 125 | if config[:read_till] == 'EOF' 126 | sock.gets(nil) 127 | else 128 | sock.gets(config[:read_till]) 129 | end 130 | end 131 | rescue Errno::ECONNREFUSED 132 | critical "Connection refused by #{host}:#{config[:port]}" 133 | rescue Timeout::Error 134 | critical 'Connection or read timed out' 135 | rescue Errno::EHOSTUNREACH 136 | critical 'Check failed to run: No route to host' 137 | rescue EOFError 138 | critical 'Connection closed unexpectedly' 139 | end 140 | 141 | def acquire_no_banner(host) 142 | Timeout.timeout(config[:timeout]) do 143 | TCPSocket.new(host, config[:port]) 144 | end 145 | rescue Errno::ECONNREFUSED 146 | critical "Connection refused by #{host}:#{config[:port]}" 147 | rescue Timeout::Error 148 | critical 'Connection or read timed out' 149 | rescue Errno::EHOSTUNREACH 150 | critical 'Check failed to run: No route to host' 151 | rescue EOFError 152 | critical 'Connection closed unexpectedly' 153 | end 154 | 155 | def run 156 | hosts = config[:hosts].split(',') 157 | okarray = [] 158 | hosts.each do |host| 159 | case config[:pattern] 160 | when nil 161 | banner = acquire_no_banner host 162 | okarray << 'ok' if banner 163 | else 164 | banner = acquire_banner host 165 | okarray << 'ok' if banner =~ /#{config[:pattern]}/ 166 | end 167 | if okarray.size == config[:count_match] && !config[:pattern].nil? 168 | ok "pattern #{config[:pattern]} matched" unless config[:ok_message] 169 | ok config[:ok_message] 170 | elsif okarray.size == config[:count_match] && config[:pattern].nil? 171 | ok "port #{config[:port]} open" unless config[:ok_message] 172 | ok config[:ok_message] 173 | else 174 | critical "port count or pattern #{config[:pattern]} does not match" unless config[:crit_message] 175 | critical config[:crit_message] 176 | end 177 | end 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /bin/check-ports-bind.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # check-ports-bind 6 | # 7 | # DESCRIPTION: 8 | # Connect to a TCP/UDP address:port, to check whether open or closed. 9 | # Don't use nmap since it's overkill. 10 | # Test UDP ports as well: Experimental 11 | # 12 | # OUTPUT: 13 | # plain text 14 | # 15 | # PLATFORMS: 16 | # Linux 17 | # 18 | # DEPENDENCIES: 19 | # gem: sensu-plugin 20 | # 21 | # USAGE: 22 | # 23 | # Ports are comma separated and support ranges 24 | # ./check-ports.rb -p 127.0.0.1:22,46.20.205.10 --hard --warn 25 | # ./check-ports.rb -p 127.0.0.1:22,46.20.205.10:80 26 | # ./check-ports.rb -p 127.0.0.1:22,127.0.0.1:1812/udp,46.20.205.10:389/both 27 | # If you mention a port without the bind address then the default address is : 0.0.0.0 28 | # 29 | # NOTES: 30 | # By default, checks for openssh on localhost port 22 (TCP) 31 | # 32 | # 33 | # LICENSE: 34 | # Adpated from check-ports.rb 35 | # Magic Online - www.magic.fr - hanynowsky@gmail.com 36 | # September 2016 37 | # Released under the same terms as Sensu (the MIT license); see LICENSE 38 | # for details. 39 | 40 | require 'sensu-plugin/check/cli' 41 | require 'socket' 42 | require 'timeout' 43 | 44 | # 45 | # Check Ports by bound address 46 | # 47 | class CheckPort < Sensu::Plugin::Check::CLI 48 | option( 49 | :hard, 50 | short: '-d', 51 | long: '--hard', 52 | description: 'Check given ports on both, TCP & UDP, if no explicit protocol is set', 53 | boolean: true, 54 | default: false 55 | ) 56 | 57 | option( 58 | :host, 59 | short: '-H HOSTNAME', 60 | long: '--hostname HOSTNAME', 61 | description: 'default Host address bound to port', 62 | default: '0.0.0.0' 63 | ) 64 | 65 | option( 66 | :portbinds, 67 | short: '-p PORTS', 68 | long: '--portbinds PORTS', 69 | description: 'different address:port/protocol to check, comma separated (0.0.0.0:22,localhost:25/tcp,127.0.0.0.1:8100-8131/udp,192.168.0.12:3030/both)', 70 | default: '0.0.0.0:22' 71 | ) 72 | 73 | option( 74 | :timeout, 75 | short: '-t SECS', 76 | long: '--timeout SECS', 77 | description: 'Connection timeout', 78 | proc: proc(&:to_i), 79 | default: 10 80 | ) 81 | 82 | option( 83 | :warn, 84 | description: 'Alert level. warn(warning) instead of critical', 85 | short: '-w', 86 | long: '--warn', 87 | default: false, 88 | boolean: true 89 | ) 90 | 91 | option( 92 | :debug, 93 | description: 'Print debug info', 94 | short: '-D', 95 | long: '--debug', 96 | default: false, 97 | boolean: true 98 | ) 99 | 100 | # Severity switcher 101 | def severity(warn, text) 102 | if warn 103 | warning(text.to_s) 104 | else 105 | critical(text.to_s) 106 | end 107 | end 108 | 109 | # Check valid port number 110 | def valid_port?(port) 111 | return false unless port =~ /^[0-9]+$/ 112 | 113 | (0..65_535).include?(port.to_i) 114 | end 115 | 116 | # Check valid port range 117 | def valid_port_range?(port) 118 | return false unless port =~ /^[0-9]+-[0-9]+$/ 119 | 120 | port_start, port_end = port.split('-', 2) 121 | 122 | valid_port?(port_start) && valid_port?(port_end) && port_start.to_i <= port_end.to_i 123 | end 124 | 125 | # Ports to check 126 | def portbinds 127 | default_protocol = config[:hard] ? 'both' : 'tcp' 128 | binds = [] 129 | 130 | config[:portbinds].split(',').each do |portbind| 131 | portbind = "#{config[:host]}:#{portbind}" unless portbind.include?(':') 132 | portbind = "#{portbind}/#{default_protocol}" unless portbind.include?('/') 133 | 134 | protocol = portbind.split('/')[1] || default_protocol 135 | address_port = portbind.split('/')[0] 136 | address = address_port.split(':')[0] 137 | port = address_port.split(':')[1] 138 | 139 | if valid_port_range?(port) 140 | # Port range 141 | 142 | first_port, last_port = port.split('-', 2) 143 | (first_port.to_i..last_port.to_i).each do |p| 144 | binds += portbindings(address, p, protocol) 145 | end 146 | elsif valid_port?(port) 147 | # Single port 148 | 149 | binds += portbindings(address, port, protocol) 150 | else 151 | critical("Invalid port or port range: #{port}") 152 | end 153 | end 154 | 155 | binds 156 | end 157 | 158 | def portbindings(address, port, protocol) 159 | if protocol == 'both' 160 | [ 161 | { address: address, port: port, protocol: 'tcp' }, 162 | { address: address, port: port, protocol: 'udp' } 163 | ] 164 | else 165 | [{ address: address, port: port, protocol: protocol }] 166 | end 167 | end 168 | 169 | # Portbind hash to string 170 | def portbind_to_s(portbind) 171 | "#{portbind[:address]}:#{portbind[:port]}/#{portbind[:protocol]}" 172 | end 173 | 174 | # Check TCP port 175 | def check_tcp_port(portbind, okays) 176 | Timeout.timeout(config[:timeout]) do 177 | connection = TCPSocket.new(portbind[:address], portbind[:port]) 178 | p connection if config[:debug] 179 | okays.push(portbind_to_s(portbind)) 180 | end 181 | end 182 | 183 | # Check UDP port 184 | def check_udp_port(portbind, okays) 185 | Timeout.timeout(config[:timeout]) do 186 | s = UDPSocket.new 187 | s.connect(portbind[:address], portbind[:port]) 188 | s.close 189 | okays.push(portbind_to_s(portbind)) 190 | end 191 | end 192 | 193 | # Check address:port/protocol 194 | def check_port(portbind, okays) 195 | case portbind[:protocol].downcase 196 | when 'tcp' 197 | check_tcp_port(portbind, okays) 198 | when 'udp' 199 | check_udp_port(portbind, okays) 200 | else 201 | severity(config[:warn], "Unsupported protocol #{portbind_to_s(portbind)}") 202 | end 203 | rescue Errno::ECONNREFUSED 204 | severity(config[:warn], "Connection refused by #{portbind_to_s(portbind)}") 205 | rescue Timeout::Error 206 | severity(config[:warn], "Connection or read timed out (#{portbind_to_s(portbind)})") 207 | rescue Errno::EHOSTUNREACH 208 | severity(config[:warn], "Check failed to run: No route to host (#{portbind_to_s(portbind)})") 209 | rescue EOFError 210 | severity(config[:warn], "Connection closed unexpectedly (#{portbind_to_s(portbind)})") 211 | end 212 | 213 | def run 214 | ports = portbinds 215 | okays = [] 216 | 217 | ports.each do |portbind| 218 | check_port(portbind, okays) 219 | end 220 | 221 | if okays.size == ports.size 222 | ok "All ports (#{config[:portbinds]}) are reachable: #{okays.join(', ')}" 223 | else 224 | severity(config[:warn], 'port count or pattern does not match') 225 | end 226 | end 227 | end 228 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Sensu Bonsai Asset](https://img.shields.io/badge/Bonsai-Download%20Me-brightgreen.svg?colorB=89C967&logo=sensu)](https://bonsai.sensu.io/assets/sensu-plugins/sensu-plugins-network-checks) 2 | [![Build Status](https://travis-ci.org/sensu-plugins/sensu-plugins-network-checks.svg?branch=master)](https://travis-ci.org/sensu-plugins/sensu-plugins-network-checks) 3 | [![Gem Version](https://badge.fury.io/rb/sensu-plugins-network-checks.svg)](http://badge.fury.io/rb/sensu-plugins-network-checks) 4 | [![Code Climate](https://codeclimate.com/github/sensu-plugins/sensu-plugins-network-checks/badges/gpa.svg)](https://codeclimate.com/github/sensu-plugins/sensu-plugins-network-checks) 5 | [![Test Coverage](https://codeclimate.com/github/sensu-plugins/sensu-plugins-network-checks/badges/coverage.svg)](https://codeclimate.com/github/sensu-plugins/sensu-plugins-network-checks) 6 | [![Dependency Status](https://gemnasium.com/sensu-plugins/sensu-plugins-network-checks.svg)](https://gemnasium.com/sensu-plugins/sensu-plugins-network-checks) 7 | 8 | ## Sensu Plugins Network Checks Plugin 9 | 10 | - [Overview](#overview) 11 | - [Files](#files) 12 | - [Usage examples](#usage-examples) 13 | - [Configuration](#configuration) 14 | - [Sensu Go](#sensu-go) 15 | - [Asset registration](#asset-registration) 16 | - [Asset definition](#asset-definition) 17 | - [Check definition](#check-definition) 18 | - [Sensu Core](#sensu-core) 19 | - [Check definition](#check-definition) 20 | - [Installation from source](#installation-from-source) 21 | - [Additional notes](#additional-notes) 22 | - [Contributing](#contributing) 23 | 24 | ### Overview 25 | 26 | This plugin provides native network instrumentation for monitoring and metrics collection, including hardware, TCP response, RBLs, whois, port status, and more. 27 | 28 | ### Files 29 | * bin/check-banner.rb 30 | * bin/check-jsonwhois-domain-expiration.rb 31 | * bin/check-mtu.rb 32 | * bin/check-multicast-groups.rb 33 | * bin/check-netfilter-conntrack.rb 34 | * bin/check-netstat-tcp.rb 35 | * bin/check-ping.rb 36 | * bin/check-ports-bind.rb 37 | * bin/check-ports-nmap.rb 38 | * bin/check-ports.rb 39 | * bin/check-rbl.rb 40 | * bin/check-socat.rb 41 | * bin/check-whois-domain-expiration-multi.rb 42 | * bin/check-whois-domain-expiration.rb 43 | * bin/metrics-interface.rb 44 | * bin/metrics-net.rb 45 | * bin/metrics-netif.rb 46 | * bin/metrics-netstat-tcp.rb 47 | * bin/metrics-ping.rb 48 | * bin/metrics-sockstat.rb 49 | 50 | **check-banner** 51 | Connects to a TCP port on one or more hosts, reads until a char (EOF, EOL, etc), and tests output against a pattern. 52 | 53 | **check-jsonwhois-domain-expiration** 54 | Checks domain expiration dates using the https://jsonwhois.com API. 55 | 56 | **check-mtu** 57 | Checks maximum transmission unit (MTU) of a network interface. In many setups, MTUs are tuned and MTU mismatches cause issues. Having a check for MTU settings helps catch these mistakes. Also, some instances in Amazon EC2 have a default MTU size of 9,000 bytes, which is undesirable in some environments. This check can catch undesirable setups. 58 | 59 | **check-multicast-groups** 60 | Checks whether specific multicast groups are configured on specific interfaces. Requires the `netstat` command. 61 | 62 | **check-netfilter-conntrack** 63 | Checks Netfilter connection tracking table condition. 64 | 65 | **check-netstat-tcp** 66 | Alert based on thresholds of discrete TCP socket states reported by netstat. 67 | 68 | **check-ping** 69 | Ping check script for Sensu. 70 | 71 | **check-ports-bind** 72 | Connects to a TCP/UDP `address:port` to check whether open or closed. 73 | 74 | **check-ports-nmap** 75 | Fetches port status using nmap. Catches bad network access control lists (ACLs) and service down events for network resources. 76 | 77 | **check-ports** 78 | Connects to a TCP/UDP port on one or more ports check whether ropen or closed. This check now uses a TCPSocket, not nmap (**check-ports-nmap** above uses nmap). 79 | 80 | **check-rbl** 81 | Checks whether an IP is blacklisted in the common DNS blacklists or a list you add. 82 | 83 | **check-socat** 84 | Inspects sockets, such as checking whether socat can receive particular a UDP multicast packet within a certain number of seconds or whether a UPD multicast packet contains an expected packet. 85 | 86 | **check-whois-domain-expiration-multi** 87 | Checks expiration dates for multiple domains using the `whois` gem. 88 | 89 | **check-whois-domain-expiration** 90 | Checks a domain's expiration dates using the `whois` gem. 91 | 92 | **metrics-interface** 93 | Provides interface metrics. 94 | 95 | **metrics-net** 96 | Fetches metrics from all interfaces on the box using the /sys/class interface. 97 | 98 | **metrics-netif** 99 | Fetches network interface throughput metrics. 100 | 101 | **metrics-netstat-tcp** 102 | Fetches metrics on TCP socket states from netstat. Particularly useful on high-traffic web or proxy servers with large numbers of short-lived TCP connections coming and going. 103 | 104 | **metrics-ping** 105 | Pings a host and outputs ping statistics. 106 | 107 | **metrics-sockstat** 108 | Parses /proc/net/sockstat and outputs all fields as metrics. 109 | 110 | ## Usage examples 111 | 112 | ### Help 113 | 114 | **check-banner.rb** 115 | ``` 116 | Usage: check-banner.rb (options) 117 | -c, --count NUMBER Number of successful matches, default(1) 118 | -C, --critmessage MESSAGE Custom critical message to send 119 | -e, --exclude_newline Exclude newline character at end of write STRING 120 | -H, --hostnames HOSTNAME(S) Host(s) to connect to, comma seperated 121 | -O, --okmessage MESSAGE Custom ok message to send 122 | -q, --pattern PAT Pattern to search for 123 | -p, --port PORT 124 | -r, --readtill CHAR Read till CHAR is reached 125 | -S, --ssl Enable SSL socket for secure connection 126 | -t, --timeout SECS Connection timeout 127 | -w, --write STRING write STRING to the socket 128 | ``` 129 | 130 | **metrics-interface.rb** 131 | ``` 132 | Usage: metrics-interface.rb (options) 133 | -x INTERFACE[,INTERFACE], List of interfaces to exclude 134 | --exclude-interface 135 | -i INTERFACE[,INTERFACE], List of interfaces to include 136 | --include-interface 137 | -s, --scheme SCHEME Metric naming scheme, text to prepend to metric 138 | ``` 139 | 140 | ## Configuration 141 | ### Sensu Go 142 | #### Asset registration 143 | 144 | Assets are the best way to make use of this plugin. If you're not using an asset, please consider doing so! If you're using sensuctl 5.13 or later, you can use the following command to add the asset: 145 | 146 | `sensuctl asset add sensu-plugins/sensu-plugins-network-checks` 147 | 148 | If you're using an earlier version of sensuctl, you can download the asset definition from [this project's Bonsai asset index page](https://bonsai.sensu.io/assets/sensu-plugins/sensu-plugins-network-checks). 149 | 150 | #### Asset definition 151 | 152 | ```yaml 153 | --- 154 | type: Asset 155 | api_version: core/v2 156 | metadata: 157 | name: sensu-plugins-network-checks 158 | spec: 159 | url: https://assets.bonsai.sensu.io/c90a2b1335ee9cda57046bd4551b303098a98237/sensu-plugins-network-checks_4.1.0_centos_linux_amd64.tar.gz 160 | sha512: 0fe990e4eb53c308c6245d8ac8cc7268d34e3235d5188ecda3a7308889a09b8b722a77f57169d6d9a7c2f1bf3b523fcdcb618aeb4cd6b83bd74feb3683d721b7 161 | ``` 162 | 163 | #### Check definition 164 | 165 | ```yaml 166 | --- 167 | type: CheckConfig 168 | spec: 169 | command: "check-banner.rb" 170 | handlers: [] 171 | high_flap_threshold: 0 172 | interval: 10 173 | low_flap_threshold: 0 174 | publish: true 175 | runtime_assets: 176 | - sensu-plugins/sensu-plugins-network-checks 177 | - sensu/sensu-ruby-runtime 178 | subscriptions: 179 | - linux 180 | ``` 181 | 182 | ### Sensu Core 183 | 184 | #### Check definition 185 | ```json 186 | { 187 | "checks": { 188 | "check-banner": { 189 | "command": "check-banner.rb", 190 | "subscribers": ["linux"], 191 | "interval": 10, 192 | "refresh": 10, 193 | "handlers": ["influxdb"] 194 | } 195 | } 196 | } 197 | ``` 198 | 199 | ## Installation from source 200 | 201 | ### Sensu Go 202 | 203 | See the instructions above for [asset registration](#asset-registration). 204 | 205 | ### Sensu Core 206 | 207 | Install and setup plugins on [Sensu Core](https://docs.sensu.io/sensu-core/latest/installation/installing-plugins/). 208 | 209 | ## Additional notes 210 | 211 | ### Sensu Go Ruby Runtime Assets 212 | 213 | The Sensu assets packaged from this repository are built against the Sensu Ruby runtime environment. When using these assets as part of a Sensu Go resource (check, mutator, or handler), make sure to include the corresponding [Sensu Ruby Runtime Asset](https://bonsai.sensu.io/assets/sensu/sensu-ruby-runtime) in the list of assets needed by the resource. 214 | 215 | ## Contributing 216 | 217 | See [CONTRIBUTING.md](https://github.com/sensu-plugins/sensu-plugins-network-checks/blob/master/CONTRIBUTING.md) for information about contributing to this plugin. 218 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | This project adheres to [Semantic Versioning](http://semver.org/). 3 | 4 | This CHANGELOG follows the format listed [here](https://github.com/sensu-plugins/community/blob/master/HOW_WE_CHANGELOG.md) 5 | 6 | ## [Unreleased] 7 | ## [5.0.0] 2020-01-26 8 | ### Breaking Changes 9 | - `metrics-sockstat.rb`: Added hostname to default metric scheme. 10 | - `metrics-netstat-tcp.rb`: Added support to choose between local and remote port metrics. Defaults to local. Metric scheme extended to indicate local or remote. 11 | 12 | ### Added 13 | - `metrics-interface.rb`: options to include or exclude interfaces via regex. `-X`, `--exclude-interfaces-regex` and `-I`, `--include-interfaces-regex` as alternative to `-x` and `-i`. 14 | - `check-ports-bind.rb`: Allow to specify the protocol to check for each port individually (address:port/proto) 15 | 16 | 17 | ### Changed 18 | - Updated net-ping runtime dependancy to '2.0.6' 19 | - Updated rake development dependancy to '~> 12.3' 20 | - Removed centos build from bonsai.yml 21 | - Updated whois-parser to ~> 1.2 22 | - Updated rubocop dependency to '~> 0.79.0' 23 | - Reconciled issues identified by updating rubocop 24 | - Updated rake requirement to '~> 13.0' 25 | - Updated rdoc requirement to '~> 6.2.0' 26 | 27 | ## [4.1.1] - 2019-12-18 28 | ### Changed 29 | - Updated README to conform with standardization guidelines (sensu-plugins/community#134) 30 | - Updated bundler development depedency to '~> 2.1' 31 | - Removed codeclimate development dependency 32 | 33 | ## [4.1.0] - 2019-012-12 34 | ### Added 35 | - Updated asset build targets to support centos6 36 | 37 | ## [4.0.0] - 2019-04-18 38 | ### Breaking Changes 39 | - Update minimum required ruby version to 2.3. Drop unsupported ruby versions. 40 | - Bump `sensu-plugin` dependency from `~> 1.2` to `~> 4.0` you can read the changelog entries for [4.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#400---2018-02-17), [3.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#300---2018-12-04), and [2.0](https://github.com/sensu-plugins/sensu-plugin/blob/master/CHANGELOG.md#v200---2017-03-29) 41 | 42 | ### Added 43 | - Travis build automation to generate Sensu Asset tarballs that can be used n conjunction with Sensu provided ruby runtime assets and the Bonsai Asset Index 44 | - Require latest sensu-plugin for [Sensu Go support](https://github.com/sensu-plugins/sensu-plugin#sensu-go-enablement) 45 | 46 | ## [3.2.1] - 2018-08-31 47 | ### Fixed 48 | - check-whois-domain-expiration.rb: corrected warn and critical flag value parsing per: https://github.com/sensu-plugins/sensu-plugins-network-checks/issues/81 (@aww-yiss) 49 | 50 | ## [3.2.0] - 2018-08-06 51 | ### Added 52 | - metrics-net.rb: allow including only interfaces whos status is `up` (@johannagnarsson) 53 | 54 | ## [3.1.2] - 2018-06-10 55 | ### Fixed 56 | - metrics-netif.rb: allow you to actually pass in a value for `--average-key` (@scones) 57 | 58 | ## [3.1.1] - 2018-03-27 59 | ### Security 60 | - updated yard dependency to `~> 0.9.11` per: https://nvd.nist.gov/vuln/detail/CVE-2017-17042 (@majormoses) 61 | 62 | ## [3.1.0] - 2018-03-17 63 | ### Changed 64 | - check-netfilter-conntrack.rb: ditch the associated shellscript and turned into pure Ruby. 65 | 66 | ## [3.0.0] - 2018-03-17 67 | ### Security 68 | - updated rubocop dependency to `~> 0.51.0` per: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8418. (@majormoses) 69 | 70 | ### Breaking Changes 71 | - removed ruby `< 2.1` support (@majormoses) 72 | 73 | ### Changed 74 | - appeased the cops and updated cop config (@majormoses) 75 | 76 | ## [2.3.1] - 2018-02-28 77 | ### Changed 78 | - update whois-parser gem dependency to version 1.0.1 (@amdprophet) 79 | 80 | ## [2.3.0] - 2018-02-09 81 | ### Changed 82 | - check-whois-domain-expiration.rb, check-whois-domain-expiration-multi.rb and check-jsonwhois-domain-expiration.rb: better resilience against errors, better reporting of erroneous responses (@DrMurx) 83 | 84 | ## [2.2.0] - 2018-02-02 85 | ### Added 86 | - check-ports.rb: support for multiple hosts (@DDSloan96) 87 | 88 | ### Changed 89 | - updated changelog guidelines location (@majormoses) 90 | 91 | ## [2.1.1] - 2018-01-06 92 | ### Fixed 93 | - check-whois-domain-expiration-multi.rb: report a `critical` error on a domain that it gets back an empty expires_on. (@edgan) 94 | 95 | ## [2.1.0] - 2017-11-03 96 | ### Added 97 | - metrics-interface.rb: Option to listen to specfic interface. (@parin921996) 98 | 99 | ## [2.0.1] - 2017-08-01 100 | ### Added 101 | - ruby 2.4 testing to travis (@majormoses) 102 | 103 | ### Fixed 104 | - misc changelog na PR template fixes (@majormoses) 105 | - check-whois-domain-expiration.rb: "Check failed to run: invalid date" caused by a change to the whois gem which removed the parser from the main repository (issue #59) (@akatch) 106 | 107 | ## [2.0.0] 2017-06-07 108 | ### Breaking Changes 109 | - check-multicast-groups.rb: Stop loading system wide settings from `settings['check-multicast-groups']` and use only the config specified as `-c` (#57 via @maoe) 110 | 111 | ## [1.2.0] - 2017-06-07 112 | ### Added 113 | - Added check-ports-bind.rb (@hanynowsky) 114 | - Added interval option to metrics-netif.rb (@Evesy) 115 | - metrics-netif.rb: expose average-key option that is used to `grep` to more easily handle locale issues (@majormoses) 116 | - metrics-netif.rb: unknown if it can not find `sar` in its `$PATH` (@majormoses) 117 | - check-ping.rb: unknown if using `--reports` and do not have `mtr` installed and in "$PATH" (@majormoses) 118 | 119 | ### Fixed 120 | - metrics-netstat-tcp.rb: Option to disable IPv6 check (#44 via @MattMencel) 121 | - check-multicast-groups.rb: Fix undefined method deep_merge for []:Array (@maoe) 122 | - check-whois-domain-expiration-multi.rb does not fail as timeout is cast to integer (@majormoses) 123 | 124 | ### Changed 125 | - check-banner.rb: Option to enable SSL socket for secure connection 126 | 127 | ## [1.1.0] - 2016-08-07 128 | ### Added 129 | - metrics-netstat-tcp.rb: Add IPv6 state counts (@mlf4aiur) 130 | 131 | ### Fixed 132 | - check-ping.rb: Fix false positives (#34 via @kai101) 133 | 134 | ## [1.0.0] - 2016-06-14 135 | ### Fixed 136 | - check-whois-domain-expiration-multi.rb: Handle whois errors better by retrying failed lookups 137 | - Use Timeout.timeout not deprecated Object#timeout 138 | 139 | ### Added 140 | - check-whois-domain-expiration-multi.rb: Add timeout option 141 | - Support for Ruby 2.3.0 142 | 143 | ### Removed 144 | - Support for Ruby 1.9.3 145 | 146 | ### Changed 147 | - Update to Rubocop 0.40 and cleanup 148 | 149 | ## [0.2.4] - 2016-04-02 150 | ### Fixed 151 | - metrics-ping.rb: Fix error when a host cannot be pinged. Convert to a proper metrics check. 152 | 153 | ### Added 154 | - basic check for netfilter conntrack 155 | - Option for newline character in write string for check-banner 156 | 157 | ## [0.1.4] - 2016-01-22 158 | ### Added 159 | - IPv6 support for check-ping 160 | 161 | ## [0.1.3] - 2016-01-15 162 | ### Added 163 | - Added check-socat.rb 164 | 165 | ## [0.1.2] - 2016-01-08 166 | ### Fixed 167 | - Bugfix for `check-whois-domain-expiration-multi` and `check-jsonwhois-domain-expiration`: return a warning-level response if there were any domains with expiration dates in the warning time period but none in the critical time period. 168 | 169 | ## [0.1.1] - 2015-12-21 170 | ### Added 171 | - activesupport runtime dependency locked to 4.2.5 to ensure that we do not need ruby > 2.2 172 | 173 | ## [0.1.0] - 2015-12-03 174 | ### Added 175 | - Added include and ignore options for devices at `metrics-net.rb` 176 | 177 | ### Changed 178 | - Updated whois gem to 3.6.3 179 | 180 | ## [0.0.8] - 2015-11-12 181 | ### Added 182 | - Support multiple port ranges for check-ports 183 | 184 | ### Changed 185 | - Updated net-ping gem to 1.7.8 186 | - Updated whois gem to 3.6.2 187 | 188 | ## [0.0.7] - 2015-10-27 189 | ### Added 190 | - Added `check-jsonwhois-domain-expiration.rb` plugin which uses the API at https://jsonwhois.com to check domain expiry. 191 | 192 | ## [0.0.6] - 2015-10-01 193 | ### Added 194 | - Added new port check 195 | 196 | ### Changed 197 | - Changed name of check-ports to check-ports-nmap 198 | 199 | ## [0.0.5] - 2015-08-05 200 | ### Added 201 | - Added new metrics-ping.rb plugin 202 | - Added check-whois-domain-expiration-multi.rb plugin to check multiple domains for expiration using whois records 203 | 204 | ### Changed 205 | - general gem cleanup 206 | 207 | ## [0.0.4] - 2015-07-14 208 | ### Changed 209 | - updated sensu-plugin gem to 1.2.0 210 | 211 | ## [0.0.3] - 2015-06-03 212 | ### Fixed 213 | - check-rbl.rb had a typo in the require statement for 'dnsbl-client' 214 | 215 | ### Changed 216 | - cleaned up the gemspec 217 | - cleaned up Rakefile 218 | - updated documentation links 219 | 220 | ### Refactored 221 | - renamed check-ports.rb to check-ports-nmap.rb 222 | - added check-ports.rb based in TCPSocket instead of nmap 223 | 224 | ### Refactored 225 | - renamed check-ports.rb to check-ports-nmap.rb 226 | - added check-ports.rb based in TCPSocket instead of nmap 227 | 228 | ## [0.0.2] - 2015-06-03 229 | 230 | ### Fixed 231 | - added binstubs 232 | 233 | ### Changed 234 | - removed cruft from /lib 235 | 236 | ## 0.0.1 - 2015-05-01 237 | 238 | ### Added 239 | - initial release 240 | 241 | #### 0.0.1.alpha.7 242 | 243 | * add gem metadata 244 | * add chef provisioner to Vagrantfile 245 | * fix ruobcop issues 246 | * pin all dependencies 247 | 248 | #### 0.0.1.alpha.6 249 | 250 | * added check-whois-domain-expiration #7 251 | 252 | #### 0.0.1.alpha.5 253 | 254 | * updated check-banner to allow checking for no banner 255 | 256 | #### 0.0.1.alpha.4 257 | 258 | * added check-mtu functionality #7 259 | 260 | #### 0.0.1.alpha.3 261 | 262 | * additional functionality to check-banner #6 263 | 264 | #### 0.0.1.alpha.2 265 | 266 | * all tests pass 267 | * initial gem release 268 | 269 | #### 0.0.1.alpha.1 270 | 271 | * initial release, same as community repo 272 | 273 | [Unreleased]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/5.0.0...HEAD 274 | [5.0.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/4.1.1...5.0.0 275 | [4.1.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/4.0.0...4.1.1 276 | [4.1.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/4.0.0...4.1.0 277 | [4.0.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.2.1...4.0.0 278 | [3.2.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.2.0...3.2.1 279 | [3.2.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.1.2...3.2.0 280 | [3.1.2]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.1.1...3.1.2 281 | [3.1.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.1.0...3.1.1 282 | [3.1.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/3.0.0...3.1.0 283 | [3.0.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.3.0...3.0.0 284 | [2.3.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.3.0...2.3.1 285 | [2.3.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.2.0...2.3.0 286 | [2.2.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.1.1...2.2.0 287 | [2.1.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.1.0...2.1.1 288 | [2.1.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.0.1...2.1.0 289 | [2.0.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/2.0.0...2.0.1 290 | [2.0.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/1.2.0...2.0.0 291 | [1.2.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/1.1.0...1.2.0 292 | [1.1.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/1.0.0...1.1.0 293 | [1.0.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.2.4...1.0.0 294 | [0.2.4]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.1.4...0.2.4 295 | [0.1.4]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.1.3...0.1.4 296 | [0.1.3]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.1.2...0.1.3 297 | [0.1.2]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.1.1...0.1.2 298 | [0.1.1]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.1.0...0.Ï.1 299 | [0.1.0]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.8...0.1.0 300 | [0.0.7]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.7...0.0.8 301 | [0.0.7]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.6...0.0.7 302 | [0.0.6]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.5...0.0.6 303 | [0.0.5]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.4...0.0.5 304 | [0.0.4]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.3...0.0.4 305 | [0.0.3]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.2...0.0.3 306 | [0.0.2]: https://github.com/sensu-plugins/sensu-plugins-network-checks/compare/0.0.1...0.0.2 307 | --------------------------------------------------------------------------------