sensu-plugins-http-boutetnico 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +211 -0
- data/bin/check-head-redirect.rb +151 -0
- data/bin/check-http-cors.rb +145 -0
- data/bin/check-http-json.rb +232 -0
- data/bin/check-http.rb +461 -0
- data/bin/check-https-cert.rb +122 -0
- data/bin/check-last-modified.rb +178 -0
- data/bin/metrics-curl.rb +86 -0
- data/bin/metrics-http-json-deep.rb +133 -0
- data/bin/metrics-http-json.rb +137 -0
- data/bin/metrics-libcurl.rb +167 -0
- data/lib/sensu-plugins-http.rb +5 -0
- data/lib/sensu-plugins-http/aws-v4.rb +40 -0
- data/lib/sensu-plugins-http/common.rb +35 -0
- data/lib/sensu-plugins-http/version.rb +11 -0
- metadata +290 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-http-json
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# Takes either a URL or a combination of host/path/query/port/ssl, and checks
|
9
|
+
# for valid JSON output in the response. Can also optionally validate simple
|
10
|
+
# string key/value pairs, and optionally check if a specified value is within
|
11
|
+
# bounds.
|
12
|
+
#
|
13
|
+
# OUTPUT:
|
14
|
+
# plain text
|
15
|
+
#
|
16
|
+
# PLATFORMS:
|
17
|
+
# Linux
|
18
|
+
#
|
19
|
+
# DEPENDENCIES:
|
20
|
+
# gem: sensu-plugin
|
21
|
+
# gem: json
|
22
|
+
#
|
23
|
+
# USAGE:
|
24
|
+
# Check that will verify http status and JSON validity
|
25
|
+
# ./check-http-json.rb -u http://my.site.com/health.json
|
26
|
+
#
|
27
|
+
# Check that will verify http status, JSON validity, and that page.totalElements value is
|
28
|
+
# greater than 10
|
29
|
+
# ./check-http-json.rb -u http://my.site.com/metric.json --key page.totalElements --value-greater-than 10
|
30
|
+
#
|
31
|
+
# Check that will POST json
|
32
|
+
# ./check-http-json.rb -u http://my.site.com/metric.json -m POST --header 'Content-type: application/json' --post-body '{"serverId": "myserver"}'
|
33
|
+
#
|
34
|
+
# NOTES:
|
35
|
+
# Based on Check HTTP by Sonian Inc.
|
36
|
+
#
|
37
|
+
# LICENSE:
|
38
|
+
# Copyright 2013 Matt Revell <nightowlmatt@gmail.com>
|
39
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
40
|
+
# for details.
|
41
|
+
#
|
42
|
+
|
43
|
+
require 'sensu-plugin/check/cli'
|
44
|
+
require 'json'
|
45
|
+
require 'net/http'
|
46
|
+
require 'net/https'
|
47
|
+
|
48
|
+
#
|
49
|
+
# Check JSON
|
50
|
+
#
|
51
|
+
class CheckJson < Sensu::Plugin::Check::CLI
|
52
|
+
option :url, short: '-u URL'
|
53
|
+
option :host, short: '-h HOST'
|
54
|
+
option :path, short: '-p PATH'
|
55
|
+
option :query, short: '-q QUERY'
|
56
|
+
option :port, short: '-P PORT', proc: proc(&:to_i)
|
57
|
+
option :method, short: '-m GET|POST'
|
58
|
+
option :postbody, short: '-b /file/with/post/body'
|
59
|
+
option :post_body, long: '--post-body VALUE'
|
60
|
+
option :header, short: '-H HEADER', long: '--header HEADER'
|
61
|
+
option :ssl, short: '-s', boolean: true, default: false
|
62
|
+
option :insecure, short: '-k', boolean: true, default: false
|
63
|
+
option :user, short: '-U', long: '--username USER'
|
64
|
+
option :password, short: '-a', long: '--password PASS'
|
65
|
+
option :cert, short: '-c FILE', long: '--cert FILE'
|
66
|
+
option :certkey, long: '--cert-key FILE'
|
67
|
+
option :cacert, short: '-C FILE', long: '--cacert FILE'
|
68
|
+
option :timeout, short: '-t SECS', proc: proc(&:to_i), default: 15
|
69
|
+
option :key, short: '-K KEY', long: '--key KEY'
|
70
|
+
option :value, short: '-v VALUE', long: '--value VALUE'
|
71
|
+
option :valueGt, long: '--value-greater-than VALUE'
|
72
|
+
option :valueLt, long: '--value-less-than VALUE'
|
73
|
+
option :whole_response, short: '-w', long: '--whole-response', boolean: true, default: false
|
74
|
+
option :dump_json, short: '-d', long: '--dump-json', boolean: true, default: false
|
75
|
+
option :pretty, long: '--pretty', boolean: true, default: false
|
76
|
+
|
77
|
+
option :response_code,
|
78
|
+
long: '--response-code REGEX',
|
79
|
+
description: 'Critical if HTTP response code does not match REGEX',
|
80
|
+
default: '^2([0-9]{2})$'
|
81
|
+
|
82
|
+
def run
|
83
|
+
if config[:url]
|
84
|
+
uri = URI.parse(config[:url])
|
85
|
+
config[:host] = uri.host
|
86
|
+
config[:path] = uri.path
|
87
|
+
config[:query] = uri.query
|
88
|
+
config[:port] = uri.port
|
89
|
+
config[:ssl] = uri.scheme == 'https'
|
90
|
+
else
|
91
|
+
# #YELLOW
|
92
|
+
unless config[:host] && config[:path]
|
93
|
+
unknown 'No URL specified'
|
94
|
+
end
|
95
|
+
config[:port] ||= config[:ssl] ? 443 : 80
|
96
|
+
end
|
97
|
+
|
98
|
+
begin
|
99
|
+
Timeout.timeout(config[:timeout]) do
|
100
|
+
acquire_resource
|
101
|
+
end
|
102
|
+
rescue Timeout::Error
|
103
|
+
critical 'Connection timed out'
|
104
|
+
rescue StandardError => e
|
105
|
+
critical "Connection error: #{e.message}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def deep_value(data, desired_key, parent = '')
|
110
|
+
case data
|
111
|
+
when Array
|
112
|
+
data.each_with_index do |value, index|
|
113
|
+
arr_key = parent + '[' + index.to_s + ']'
|
114
|
+
|
115
|
+
if arr_key == desired_key
|
116
|
+
return value.nil? ? 'null' : value
|
117
|
+
end
|
118
|
+
|
119
|
+
if desired_key.include? arr_key
|
120
|
+
search = deep_value(value, desired_key, arr_key)
|
121
|
+
|
122
|
+
return search unless search.nil?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
when Hash
|
126
|
+
data.each do |key, value|
|
127
|
+
key_prefix = parent.empty? ? '' : '.'
|
128
|
+
hash_key = parent + key_prefix + key
|
129
|
+
|
130
|
+
if hash_key == desired_key
|
131
|
+
return value.nil? ? 'null' : value
|
132
|
+
end
|
133
|
+
|
134
|
+
if desired_key.include?(hash_key + '.') || desired_key.include?(hash_key + '[')
|
135
|
+
search = deep_value(value, desired_key, hash_key)
|
136
|
+
|
137
|
+
return search unless search.nil?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def json_valid?(str)
|
144
|
+
::JSON.parse(str)
|
145
|
+
true
|
146
|
+
rescue ::JSON::ParserError
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
def acquire_resource
|
151
|
+
http = Net::HTTP.new(config[:host], config[:port])
|
152
|
+
|
153
|
+
if config[:ssl]
|
154
|
+
http.use_ssl = true
|
155
|
+
if config[:cert]
|
156
|
+
cert_data = File.read(config[:cert])
|
157
|
+
http.cert = OpenSSL::X509::Certificate.new(cert_data)
|
158
|
+
if config[:certkey]
|
159
|
+
cert_data = File.read(config[:certkey])
|
160
|
+
end
|
161
|
+
http.key = OpenSSL::PKey::RSA.new(cert_data, nil)
|
162
|
+
end
|
163
|
+
http.ca_file = config[:cacert] if config[:cacert]
|
164
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config[:insecure]
|
165
|
+
end
|
166
|
+
|
167
|
+
req = if config[:method] == 'POST'
|
168
|
+
Net::HTTP::Post.new([config[:path], config[:query]].compact.join('?'))
|
169
|
+
else
|
170
|
+
Net::HTTP::Get.new([config[:path], config[:query]].compact.join('?'))
|
171
|
+
end
|
172
|
+
if config[:postbody]
|
173
|
+
post_body = IO.readlines(config[:postbody])
|
174
|
+
req.body = post_body.join
|
175
|
+
end
|
176
|
+
if config[:post_body]
|
177
|
+
req.body = config[:post_body]
|
178
|
+
end
|
179
|
+
unless config[:user].nil? && config[:password].nil?
|
180
|
+
req.basic_auth config[:user], config[:password]
|
181
|
+
end
|
182
|
+
if config[:header]
|
183
|
+
config[:header].split(',').each do |header|
|
184
|
+
h, v = header.split(':', 2)
|
185
|
+
req[h] = v.strip
|
186
|
+
end
|
187
|
+
end
|
188
|
+
res = http.request(req)
|
189
|
+
|
190
|
+
if res.code !~ /#{config[:response_code]}/
|
191
|
+
critical "http code: #{res.code}: body: #{res.body}" if config[:whole_response]
|
192
|
+
critical res.code
|
193
|
+
end
|
194
|
+
critical 'invalid JSON from request' unless json_valid?(res.body)
|
195
|
+
ok 'valid JSON returned' if config[:key].nil? && config[:value].nil?
|
196
|
+
|
197
|
+
json = ::JSON.parse(res.body)
|
198
|
+
|
199
|
+
begin
|
200
|
+
leaf = deep_value(json, config[:key])
|
201
|
+
|
202
|
+
raise "could not find key: #{config[:key]}" if leaf.nil?
|
203
|
+
|
204
|
+
message = "key has expected value: '#{config[:key]}' "
|
205
|
+
if config[:value]
|
206
|
+
raise "unexpected value for key: '#{leaf}' != '#{config[:value]}'" unless leaf.to_s == config[:value].to_s
|
207
|
+
|
208
|
+
message += "equals '#{config[:value]}'"
|
209
|
+
end
|
210
|
+
if config[:valueGt]
|
211
|
+
raise "unexpected value for key: '#{leaf}' not > '#{config[:valueGt]}'" unless leaf.to_f > config[:valueGt].to_f
|
212
|
+
|
213
|
+
message += "greater than '#{config[:valueGt]}'"
|
214
|
+
end
|
215
|
+
if config[:valueLt]
|
216
|
+
raise "unexpected value for key: '#{leaf}' not < '#{config[:valueLt]}'" unless leaf.to_f < config[:valueLt].to_f
|
217
|
+
|
218
|
+
message += "less than '#{config[:valueLt]}'"
|
219
|
+
end
|
220
|
+
|
221
|
+
ok message
|
222
|
+
rescue StandardError => e
|
223
|
+
if config[:dump_json]
|
224
|
+
json_response = config[:pretty] ? ::JSON.pretty_generate(json) : json
|
225
|
+
message = "key check failed: #{e}. Response: #{json_response}"
|
226
|
+
else
|
227
|
+
message = "key check failed: #{e}"
|
228
|
+
end
|
229
|
+
critical message
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/bin/check-http.rb
ADDED
@@ -0,0 +1,461 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: false
|
3
|
+
|
4
|
+
#
|
5
|
+
# check-http
|
6
|
+
#
|
7
|
+
# DESCRIPTION:
|
8
|
+
# Takes either a URL or a combination of host/path/port/ssl, and checks for
|
9
|
+
# a 200 response (that matches a pattern, if given). Can use client certs.
|
10
|
+
#
|
11
|
+
# OUTPUT:
|
12
|
+
# plain text
|
13
|
+
#
|
14
|
+
# PLATFORMS:
|
15
|
+
# Linux
|
16
|
+
#
|
17
|
+
# DEPENDENCIES:
|
18
|
+
# gem: sensu-plugin
|
19
|
+
#
|
20
|
+
# USAGE:
|
21
|
+
# Basic HTTP check - expect a 200 response
|
22
|
+
# check-http.rb -u http://my.site.com
|
23
|
+
#
|
24
|
+
# Pattern check - expect a 200 response and the string 'OK' in the body
|
25
|
+
# check-http.rb -u http://my.site.com/health -q 'OK'
|
26
|
+
#
|
27
|
+
# Check if a response is greater than the specified minimum value
|
28
|
+
# check-http.rb -u https://my.site.com/redirect --min-bytes 10
|
29
|
+
#
|
30
|
+
# Check response code - expect a 301 response
|
31
|
+
# check-http.rb -u https://my.site.com/redirect --response-code 301 -r
|
32
|
+
#
|
33
|
+
# Use a proxy to check a URL
|
34
|
+
# check-http.rb -u https://www.google.com --proxy-url http://my.proxy.com:3128
|
35
|
+
#
|
36
|
+
# Use a proxy with username and password to check a URL
|
37
|
+
# NOTE: Use 'check token substition' to avoid credentials leakage!
|
38
|
+
# check-http.rb -u https://www.google.com --proxy-url http://a_user:a_pass@my.proxy.com:3128
|
39
|
+
#
|
40
|
+
# Check something with needing to set multiple headers
|
41
|
+
# check-http.rb -u https://www.google.com --header 'Origin: ma.local.box, SomeRandomHeader: foo'
|
42
|
+
#
|
43
|
+
# Check something that requires a POST with json data
|
44
|
+
# check-http.rb -u https://httpbin.org/post --method POST --header 'Content-type: application/json' --body '{"foo": "bar"}'
|
45
|
+
# NOTES:
|
46
|
+
#
|
47
|
+
# LICENSE:
|
48
|
+
# Copyright 2011 Sonian, Inc <chefs@sonian.net>
|
49
|
+
# Updated by Lewis Preson 2012 to accept basic auth credentials
|
50
|
+
# Updated by SweetSpot 2012 to require specified redirect
|
51
|
+
# Updated by Chris Armstrong 2013 to accept multiple headers
|
52
|
+
# Updated by Mark Clarkson 2018 to accept proxy auth credentials
|
53
|
+
# Released under the same terms as Sensu (the MIT license); see LICENSE
|
54
|
+
# for details.
|
55
|
+
#
|
56
|
+
|
57
|
+
require 'sensu-plugins-http'
|
58
|
+
require 'sensu-plugin/check/cli'
|
59
|
+
require 'net/http'
|
60
|
+
require 'net/https'
|
61
|
+
require 'digest'
|
62
|
+
require 'resolv-replace'
|
63
|
+
|
64
|
+
#
|
65
|
+
# Check HTTP
|
66
|
+
#
|
67
|
+
class CheckHttp < Sensu::Plugin::Check::CLI
|
68
|
+
option :ua,
|
69
|
+
short: '-x USER-AGENT',
|
70
|
+
long: '--user-agent USER-AGENT',
|
71
|
+
description: 'Specify a USER-AGENT',
|
72
|
+
default: 'Sensu-HTTP-Check'
|
73
|
+
|
74
|
+
option :url,
|
75
|
+
short: '-u URL',
|
76
|
+
long: '--url URL',
|
77
|
+
description: 'A URL to connect to'
|
78
|
+
|
79
|
+
option :host,
|
80
|
+
short: '-h HOST',
|
81
|
+
long: '--hostname HOSTNAME',
|
82
|
+
description: 'A HOSTNAME to connect to'
|
83
|
+
|
84
|
+
option :port,
|
85
|
+
short: '-P PORT',
|
86
|
+
long: '--port PORT',
|
87
|
+
proc: proc(&:to_i),
|
88
|
+
description: 'Select another port'
|
89
|
+
|
90
|
+
option :request_uri,
|
91
|
+
short: '-p PATH',
|
92
|
+
long: '--request-uri PATH',
|
93
|
+
description: 'Specify a uri path'
|
94
|
+
|
95
|
+
option :method,
|
96
|
+
short: '-m GET|HEAD|POST|PUT',
|
97
|
+
long: '--method GET|HEAD|POST|PUT',
|
98
|
+
description: 'Specify a GET, HEAD, POST, or PUT operation; defaults to GET',
|
99
|
+
in: %w[GET HEAD POST PUT],
|
100
|
+
default: 'GET'
|
101
|
+
|
102
|
+
option :header,
|
103
|
+
short: '-H HEADER',
|
104
|
+
long: '--header HEADER',
|
105
|
+
description: 'Send one or more comma-separated headers with the request'
|
106
|
+
|
107
|
+
option :headerfile,
|
108
|
+
long: '--headerfile FILE',
|
109
|
+
description: 'Send headers with the request, read from FILE, separated by newline'
|
110
|
+
|
111
|
+
option :body,
|
112
|
+
short: '-d BODY',
|
113
|
+
long: '--body BODY',
|
114
|
+
description: 'Send a data body string with the request'
|
115
|
+
|
116
|
+
option :ssl,
|
117
|
+
short: '-s',
|
118
|
+
boolean: true,
|
119
|
+
description: 'Enabling SSL connections',
|
120
|
+
default: false
|
121
|
+
|
122
|
+
option :insecure,
|
123
|
+
short: '-k',
|
124
|
+
boolean: true,
|
125
|
+
description: 'Enabling insecure connections',
|
126
|
+
default: false
|
127
|
+
|
128
|
+
option :user,
|
129
|
+
short: '-U',
|
130
|
+
long: '--username USER',
|
131
|
+
description: 'A username to connect as'
|
132
|
+
|
133
|
+
option :password,
|
134
|
+
short: '-a',
|
135
|
+
long: '--password PASS',
|
136
|
+
description: 'A password to use for the username'
|
137
|
+
|
138
|
+
option :cert,
|
139
|
+
short: '-c FILE',
|
140
|
+
long: '--cert FILE',
|
141
|
+
description: 'Cert to use'
|
142
|
+
|
143
|
+
option :cacert,
|
144
|
+
short: '-C FILE',
|
145
|
+
long: '--cacert FILE',
|
146
|
+
description: 'A CA Cert to use'
|
147
|
+
|
148
|
+
option :expiry,
|
149
|
+
short: '-e EXPIRY',
|
150
|
+
long: '--expiry EXPIRY',
|
151
|
+
proc: proc(&:to_i),
|
152
|
+
description: 'Warn EXPIRE days before cert expires'
|
153
|
+
|
154
|
+
option :pattern,
|
155
|
+
short: '-q PAT',
|
156
|
+
long: '--query PAT',
|
157
|
+
description: 'Query for a specific pattern that must exist'
|
158
|
+
|
159
|
+
option :negpattern,
|
160
|
+
short: '-n PAT',
|
161
|
+
long: '--negquery PAT',
|
162
|
+
description: 'Query for a specific pattern that must be absent'
|
163
|
+
|
164
|
+
option :sha256checksum,
|
165
|
+
short: '-S CHECKSUM',
|
166
|
+
long: '--checksum CHECKSUM',
|
167
|
+
description: 'SHA-256 checksum'
|
168
|
+
|
169
|
+
option :timeout,
|
170
|
+
short: '-t SECS',
|
171
|
+
long: '--timeout SECS',
|
172
|
+
proc: proc(&:to_i),
|
173
|
+
description: 'Set the total execution timeout in seconds',
|
174
|
+
default: 15
|
175
|
+
|
176
|
+
option :open_timeout,
|
177
|
+
long: '--open-timeout SECS',
|
178
|
+
proc: proc(&:to_i),
|
179
|
+
description: 'Number of seconds to wait for the connection to open',
|
180
|
+
default: 15
|
181
|
+
|
182
|
+
option :read_timeout,
|
183
|
+
long: '--read-timeout SECS',
|
184
|
+
proc: proc(&:to_i),
|
185
|
+
description: 'Number of seconds to wait for one block to be read',
|
186
|
+
default: 15
|
187
|
+
|
188
|
+
option :dns_timeout,
|
189
|
+
long: '--dns-timeout SECS',
|
190
|
+
proc: proc(&:to_f),
|
191
|
+
description: 'Number of seconds to allow for DNS resolution. Accepts decimal number.',
|
192
|
+
default: 0.8
|
193
|
+
|
194
|
+
option :redirectok,
|
195
|
+
short: '-r',
|
196
|
+
boolean: true,
|
197
|
+
description: 'Check if a redirect is ok',
|
198
|
+
default: false
|
199
|
+
|
200
|
+
option :redirectto,
|
201
|
+
short: '-R URL',
|
202
|
+
long: '--redirect-to URL',
|
203
|
+
description: 'Redirect to another page'
|
204
|
+
|
205
|
+
option :whole_response,
|
206
|
+
short: '-w',
|
207
|
+
long: '--whole-response',
|
208
|
+
boolean: true,
|
209
|
+
default: false,
|
210
|
+
description: 'Print whole output when check fails'
|
211
|
+
|
212
|
+
option :response_bytes,
|
213
|
+
short: '-b BYTES',
|
214
|
+
long: '--response-bytes BYTES',
|
215
|
+
description: 'Print BYTES of the output',
|
216
|
+
proc: proc(&:to_i)
|
217
|
+
|
218
|
+
option :require_bytes,
|
219
|
+
short: '-B BYTES',
|
220
|
+
long: '--require-bytes BYTES',
|
221
|
+
description: 'Check the response contains exactly BYTES bytes',
|
222
|
+
proc: proc(&:to_i)
|
223
|
+
|
224
|
+
option :min_bytes,
|
225
|
+
short: '-g BYTES',
|
226
|
+
long: '--min-bytes BYTES',
|
227
|
+
description: 'Check the response contains at least BYTES bytes',
|
228
|
+
proc: proc(&:to_i)
|
229
|
+
|
230
|
+
option :response_code,
|
231
|
+
long: '--response-code REGEX',
|
232
|
+
description: 'Critical if HTTP response code does not match REGEX'
|
233
|
+
|
234
|
+
option :proxy_url,
|
235
|
+
long: '--proxy-url PROXY_URL',
|
236
|
+
description: 'Use a proxy server to connect'
|
237
|
+
|
238
|
+
option :no_proxy,
|
239
|
+
long: '--noproxy',
|
240
|
+
boolean: true,
|
241
|
+
description: 'Do not use proxy server even from environment http_proxy setting',
|
242
|
+
default: false
|
243
|
+
|
244
|
+
option :aws_v4,
|
245
|
+
long: '--aws-v4',
|
246
|
+
boolean: true,
|
247
|
+
description: 'Sign http request with AWS v4 signature',
|
248
|
+
default: false
|
249
|
+
|
250
|
+
option :aws_v4_region,
|
251
|
+
long: '--aws-v4-region REGION',
|
252
|
+
description: 'Region to use for AWS v4 signing. Defaults to AWS_REGION or AWS_DEFAULT_REGION'
|
253
|
+
|
254
|
+
option :aws_v4_service,
|
255
|
+
long: '--aws-v4-service SERVICE',
|
256
|
+
description: 'Service name to use when building the v4 signature',
|
257
|
+
default: 'execute-api'
|
258
|
+
|
259
|
+
include SensuPluginsHttp::AwsV4
|
260
|
+
|
261
|
+
def run
|
262
|
+
if config[:url]
|
263
|
+
uri = URI.parse(config[:url])
|
264
|
+
config[:host] = uri.host
|
265
|
+
config[:port] = uri.port
|
266
|
+
config[:request_uri] = uri.request_uri
|
267
|
+
config[:ssl] = uri.scheme == 'https'
|
268
|
+
else
|
269
|
+
# #YELLOW
|
270
|
+
unless config[:host] && config[:request_uri]
|
271
|
+
unknown 'No URL specified'
|
272
|
+
end
|
273
|
+
config[:port] ||= config[:ssl] ? 443 : 80
|
274
|
+
end
|
275
|
+
|
276
|
+
# Use Ruby DNS Resolver and set DNS resolution timeout to dns_timeout value
|
277
|
+
hosts_resolver = Resolv::Hosts.new
|
278
|
+
dns_resolver = Resolv::DNS.new
|
279
|
+
dns_resolver.timeouts = config[:dns_timeout]
|
280
|
+
Resolv::DefaultResolver.replace_resolvers([hosts_resolver, dns_resolver])
|
281
|
+
|
282
|
+
begin
|
283
|
+
Timeout.timeout(config[:timeout]) do
|
284
|
+
acquire_resource
|
285
|
+
end
|
286
|
+
rescue Net::OpenTimeout
|
287
|
+
critical 'Request timed out opening connection'
|
288
|
+
rescue Net::ReadTimeout
|
289
|
+
critical 'Request timed out reading data'
|
290
|
+
rescue Timeout::Error
|
291
|
+
critical 'Request timed out'
|
292
|
+
rescue StandardError => e
|
293
|
+
critical "Request error: #{e.message}"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def acquire_resource
|
298
|
+
http = nil
|
299
|
+
|
300
|
+
if config[:no_proxy]
|
301
|
+
http = Net::HTTP.new(config[:host], config[:port], nil, nil)
|
302
|
+
elsif config[:proxy_url]
|
303
|
+
proxy_uri = URI.parse(config[:proxy_url])
|
304
|
+
if proxy_uri.host.nil?
|
305
|
+
unknown 'Invalid proxy url specified'
|
306
|
+
end
|
307
|
+
http = if proxy_uri.user && proxy_uri.password
|
308
|
+
Net::HTTP.new(config[:host], config[:port], proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
309
|
+
else
|
310
|
+
Net::HTTP.new(config[:host], config[:port], proxy_uri.host, proxy_uri.port)
|
311
|
+
end
|
312
|
+
else
|
313
|
+
http = Net::HTTP.new(config[:host], config[:port])
|
314
|
+
end
|
315
|
+
http.read_timeout = config[:read_timeout]
|
316
|
+
http.open_timeout = config[:open_timeout]
|
317
|
+
http.ssl_timeout = config[:timeout]
|
318
|
+
http.continue_timeout = config[:timeout]
|
319
|
+
http.keep_alive_timeout = config[:timeout]
|
320
|
+
|
321
|
+
warn_cert_expire = nil
|
322
|
+
if config[:ssl]
|
323
|
+
http.use_ssl = true
|
324
|
+
if config[:cert]
|
325
|
+
cert_data = File.read(config[:cert])
|
326
|
+
http.cert = OpenSSL::X509::Certificate.new(cert_data)
|
327
|
+
http.key = OpenSSL::PKey::RSA.new(cert_data, nil)
|
328
|
+
end
|
329
|
+
http.ca_file = config[:cacert] if config[:cacert]
|
330
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config[:insecure]
|
331
|
+
|
332
|
+
unless config[:expiry].nil?
|
333
|
+
expire_warn_date = Time.now + (config[:expiry] * 60 * 60 * 24)
|
334
|
+
# We can't raise inside the callback, have to check when we finish.
|
335
|
+
http.verify_callback = proc do |preverify_ok, ssl_context|
|
336
|
+
if ssl_context.current_cert.not_after <= expire_warn_date
|
337
|
+
warn_cert_expire = ssl_context.current_cert.not_after
|
338
|
+
end
|
339
|
+
|
340
|
+
preverify_ok
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
req = case config[:method]
|
346
|
+
when 'GET'
|
347
|
+
Net::HTTP::Get.new(config[:request_uri], 'User-Agent' => config[:ua])
|
348
|
+
when 'HEAD'
|
349
|
+
Net::HTTP::Head.new(config[:request_uri], 'User-Agent' => config[:ua])
|
350
|
+
when 'POST'
|
351
|
+
Net::HTTP::Post.new(config[:request_uri], 'User-Agent' => config[:ua])
|
352
|
+
when 'PUT'
|
353
|
+
Net::HTTP::Put.new(config[:request_uri], 'User-Agent' => config[:ua])
|
354
|
+
end
|
355
|
+
|
356
|
+
unless config[:user].nil? && config[:password].nil?
|
357
|
+
req.basic_auth config[:user], config[:password]
|
358
|
+
end
|
359
|
+
if config[:header]
|
360
|
+
config[:header].split(',').each do |header|
|
361
|
+
h, v = header.split(':', 2)
|
362
|
+
req[h.strip] = v.strip
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
if config[:headerfile]
|
367
|
+
File.readlines(config[:headerfile]).each do |line|
|
368
|
+
h, v = line.split(':', 2)
|
369
|
+
req[h.strip] = v.strip
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
req.body = config[:body] if config[:body]
|
374
|
+
|
375
|
+
req = apply_v4_signature(http, req, config) if config[:aws_v4]
|
376
|
+
|
377
|
+
res = http.request(req)
|
378
|
+
|
379
|
+
body = if config[:whole_response]
|
380
|
+
"\n" + res.body.to_s
|
381
|
+
else
|
382
|
+
body = if config[:response_bytes] # rubocop:disable Lint/UselessAssignment
|
383
|
+
"\n" + res.body[0..config[:response_bytes]]
|
384
|
+
else
|
385
|
+
''
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
if config[:require_bytes] && res.body.length != config[:require_bytes]
|
390
|
+
critical "Response was #{res.body.length} bytes instead of #{config[:require_bytes]}" + body
|
391
|
+
end
|
392
|
+
|
393
|
+
if config[:min_bytes] && res.body.length < config[:min_bytes]
|
394
|
+
critical "Response was #{res.body.length} bytes instead of the indicated minimum #{config[:min_bytes]}" + body
|
395
|
+
end
|
396
|
+
|
397
|
+
unless warn_cert_expire.nil?
|
398
|
+
warning "Certificate will expire #{warn_cert_expire}"
|
399
|
+
end
|
400
|
+
|
401
|
+
size = res.body.nil? ? '0' : res.body.size
|
402
|
+
|
403
|
+
handle_response(res, size, body)
|
404
|
+
end
|
405
|
+
|
406
|
+
def handle_response(res, size, body)
|
407
|
+
case res.code
|
408
|
+
when /^2/
|
409
|
+
if config[:redirectto]
|
410
|
+
critical "Expected redirect to #{config[:redirectto]} but got #{res.code}" + body
|
411
|
+
elsif config[:pattern]
|
412
|
+
if res.body =~ /#{config[:pattern]}/
|
413
|
+
ok "#{res.code}, found /#{config[:pattern]}/ in #{size} bytes" + body
|
414
|
+
else
|
415
|
+
critical "#{res.code}, did not find /#{config[:pattern]}/ in #{size} bytes: #{res.body[0...200]}..."
|
416
|
+
end
|
417
|
+
elsif config[:negpattern]
|
418
|
+
if res.body =~ /#{config[:negpattern]}/
|
419
|
+
critical "#{res.code}, found /#{config[:negpattern]}/ in #{size} bytes: #{res.body[0...200]}..."
|
420
|
+
else
|
421
|
+
ok "#{res.code}, did not find /#{config[:negpattern]}/ in #{size} bytes" + body
|
422
|
+
end
|
423
|
+
elsif config[:sha256checksum]
|
424
|
+
if Digest::SHA256.hexdigest(res.body).eql? config[:sha256checksum]
|
425
|
+
ok "#{res.code}, checksum match #{config[:sha256checksum]} in #{size} bytes" + body
|
426
|
+
else
|
427
|
+
critical "#{res.code}, checksum did not match #{config[:sha256checksum]} in #{size} bytes: #{res.body[0...200]}..."
|
428
|
+
end
|
429
|
+
else
|
430
|
+
ok("#{res.code}, #{size} bytes" + body) unless config[:response_code]
|
431
|
+
end
|
432
|
+
when /^3/
|
433
|
+
if config[:redirectok] || config[:redirectto]
|
434
|
+
if config[:redirectok]
|
435
|
+
# #YELLOW
|
436
|
+
ok("#{res.code}, #{size} bytes" + body) unless config[:response_code] # rubocop:disable Metrics/BlockNesting
|
437
|
+
elsif config[:redirectto]
|
438
|
+
# #YELLOW
|
439
|
+
if config[:redirectto] == res['Location'] # rubocop:disable Metrics/BlockNesting
|
440
|
+
ok "#{res.code} found redirect to #{res['Location']}" + body
|
441
|
+
else
|
442
|
+
critical "Expected redirect to #{config[:redirectto]} instead redirected to #{res['Location']}" + body
|
443
|
+
end
|
444
|
+
end
|
445
|
+
else
|
446
|
+
warning res.code + body
|
447
|
+
end
|
448
|
+
when /^4/, /^5/
|
449
|
+
critical(res.code + body) unless config[:response_code]
|
450
|
+
else
|
451
|
+
warning(res.code + body) unless config[:response_code]
|
452
|
+
end
|
453
|
+
|
454
|
+
if config[:response_code] && res.code =~ /#{config[:response_code]}/
|
455
|
+
ok "#{res.code}, #{size} bytes" + body
|
456
|
+
|
457
|
+
else
|
458
|
+
critical res.code + body
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|