riemann-tools 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -6
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +18 -0
  5. data/CHANGELOG.md +23 -0
  6. data/Gemfile +22 -0
  7. data/Rakefile +1 -1
  8. data/bin/riemann-hwmon +8 -0
  9. data/bin/riemann-tls-check +8 -0
  10. data/lib/riemann/tools/apache_status.rb +2 -0
  11. data/lib/riemann/tools/bench.rb +4 -2
  12. data/lib/riemann/tools/consul_health.rb +2 -0
  13. data/lib/riemann/tools/dir_files_count.rb +2 -0
  14. data/lib/riemann/tools/dir_space.rb +2 -0
  15. data/lib/riemann/tools/diskstats.rb +6 -4
  16. data/lib/riemann/tools/fd.rb +2 -0
  17. data/lib/riemann/tools/freeswitch.rb +2 -0
  18. data/lib/riemann/tools/haproxy.rb +2 -0
  19. data/lib/riemann/tools/health.rb +79 -11
  20. data/lib/riemann/tools/http_check.rb +56 -17
  21. data/lib/riemann/tools/hwmon.rb +111 -0
  22. data/lib/riemann/tools/mdstat_parser.tab.rb +4 -2
  23. data/lib/riemann/tools/net.rb +3 -7
  24. data/lib/riemann/tools/nginx_status.rb +8 -4
  25. data/lib/riemann/tools/ntp.rb +2 -0
  26. data/lib/riemann/tools/portcheck.rb +2 -0
  27. data/lib/riemann/tools/proc.rb +2 -0
  28. data/lib/riemann/tools/tls_check.rb +604 -0
  29. data/lib/riemann/tools/utils.rb +39 -0
  30. data/lib/riemann/tools/varnish.rb +2 -0
  31. data/lib/riemann/tools/version.rb +1 -1
  32. data/lib/riemann/tools.rb +26 -9
  33. data/riemann-tools.gemspec +2 -11
  34. data/tools/riemann-aws/lib/riemann/tools/aws/billing.rb +3 -1
  35. data/tools/riemann-aws/lib/riemann/tools/aws/rds_status.rb +2 -0
  36. data/tools/riemann-aws/lib/riemann/tools/aws/s3_status.rb +1 -1
  37. data/tools/riemann-aws/lib/riemann/tools/aws/sqs_status.rb +2 -0
  38. data/tools/riemann-aws/lib/riemann/tools/aws/status.rb +11 -9
  39. data/tools/riemann-chronos/lib/riemann/tools/chronos.rb +2 -0
  40. data/tools/riemann-docker/lib/riemann/tools/docker.rb +5 -5
  41. data/tools/riemann-marathon/lib/riemann/tools/marathon.rb +2 -0
  42. data/tools/riemann-munin/lib/riemann/tools/munin.rb +2 -0
  43. data/tools/riemann-rabbitmq/lib/riemann/tools/rabbitmq.rb +7 -7
  44. data/tools/riemann-riak/lib/riemann/tools/riak.rb +4 -2
  45. metadata +10 -129
@@ -0,0 +1,604 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'resolv'
5
+
6
+ require 'riemann/tools'
7
+ require 'riemann/tools/utils'
8
+
9
+ module URI
10
+ {
11
+ 'IMAP' => 143,
12
+ 'IMAPS' => 993,
13
+ 'MYSQL' => 3306,
14
+ 'POSTGRES' => 5432,
15
+ }.each do |scheme, port|
16
+ klass = Class.new(Generic)
17
+ klass.const_set('DEFAULT_PORT', port)
18
+
19
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('3.1.0')
20
+ scheme_list[scheme] = klass
21
+ else
22
+ register_scheme(scheme, klass)
23
+ end
24
+ end
25
+ end
26
+
27
+ module Riemann
28
+ module Tools
29
+ class TLSCheck
30
+ include Riemann::Tools
31
+
32
+ # Ruby OpenSSL does not expose ERR_error_string(3), and depending on the
33
+ # version of OpenSSL the available values change. Build a local list of
34
+ # mappings from include/openssl/x509_vfy.h.in and crypto/x509/x509_txt.c
35
+ # for lookups.
36
+ OPENSSL_ERROR_STRINGS = [
37
+ 'ok',
38
+ 'unspecified certificate verification error',
39
+ 'unable to get issuer certificate',
40
+ 'unable to get certificate CRL',
41
+ "unable to decrypt certificate's signature",
42
+ "unable to decrypt CRL's signature",
43
+ 'unable to decode issuer public key',
44
+ 'certificate signature failure',
45
+ 'CRL signature failure',
46
+ 'certificate is not yet valid',
47
+ 'certificate has expired',
48
+ 'CRL is not yet valid',
49
+ 'CRL has expired',
50
+ "format error in certificate's notBefore field",
51
+ "format error in certificate's notAfter field",
52
+ "format error in CRL's lastUpdate field",
53
+ "format error in CRL's nextUpdate field",
54
+ 'out of memory',
55
+ 'self-signed certificate',
56
+ 'self-signed certificate in certificate chain',
57
+ 'unable to get local issuer certificate',
58
+ 'unable to verify the first certificate',
59
+ 'certificate chain too long',
60
+ 'certificate revoked',
61
+ "issuer certificate doesn't have a public key",
62
+ 'path length constraint exceeded',
63
+ 'unsuitable certificate purpose',
64
+ 'certificate not trusted',
65
+ 'certificate rejected',
66
+ ].freeze
67
+
68
+ class TLSCheckResult
69
+ include Riemann::Tools::Utils
70
+
71
+ attr_reader :uri, :address, :tls_socket
72
+
73
+ def initialize(uri, address, tls_socket, checker)
74
+ @uri = uri
75
+ @address = address
76
+ @tls_socket = tls_socket
77
+ @checker = checker
78
+ end
79
+
80
+ def peer_cert
81
+ tls_socket.peer_cert
82
+ end
83
+
84
+ def peer_cert_chain
85
+ tls_socket.peer_cert_chain
86
+ end
87
+
88
+ def exception
89
+ tls_socket.exception if tls_socket.respond_to?(:exception)
90
+ end
91
+
92
+ def valid_identity?
93
+ OpenSSL::SSL.verify_certificate_identity(peer_cert, uri.host)
94
+ end
95
+
96
+ def acceptable_identities
97
+ res = []
98
+
99
+ peer_cert.extensions.each do |ext|
100
+ next unless ext.oid == 'subjectAltName'
101
+
102
+ ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
103
+ sequence = OpenSSL::ASN1.decode(ostr.value)
104
+ res = sequence.value.map(&:value)
105
+ end
106
+
107
+ res << peer_cert.subject.to_s unless res.any?
108
+
109
+ res
110
+ end
111
+
112
+ def not_valid_yet?
113
+ utcnow < not_before
114
+ end
115
+
116
+ def not_after
117
+ peer_cert.not_after
118
+ end
119
+
120
+ def not_after_ago
121
+ not_after - utcnow
122
+ end
123
+
124
+ def not_after_ago_in_words
125
+ when_from_utcnow(not_after)
126
+ end
127
+
128
+ def not_before
129
+ peer_cert.not_before
130
+ end
131
+
132
+ def not_before_away
133
+ utcnow - not_before
134
+ end
135
+
136
+ def not_before_away_in_words
137
+ when_from_utcnow(not_before)
138
+ end
139
+
140
+ def validity_duration
141
+ not_after - not_before
142
+ end
143
+
144
+ def renewal_duration
145
+ [validity_duration * @checker.opts[:renewal_duration_ratio], @checker.opts[:renewal_duration_days] * 3600 * 24].min
146
+ end
147
+
148
+ def expired_or_expire_soon?
149
+ utcnow + (renewal_duration / 3) > not_after
150
+ end
151
+
152
+ def expire_soonish?
153
+ utcnow + (2 * renewal_duration / 3) > not_after
154
+ end
155
+
156
+ def expired?
157
+ utcnow > not_after
158
+ end
159
+
160
+ def verify_result
161
+ tls_socket.verify_result
162
+ end
163
+
164
+ def trusted?
165
+ verify_result == OpenSSL::X509::V_OK
166
+ end
167
+
168
+ def ocsp_status
169
+ @ocsp_status ||= check_ocsp_status
170
+ end
171
+
172
+ def ocsp?
173
+ !ocsp_status.empty?
174
+ end
175
+
176
+ def valid_ocsp?
177
+ ocsp_status == 'successful'
178
+ end
179
+
180
+ def check_ocsp_status
181
+ subject = peer_cert
182
+ issuer = peer_cert_chain[1]
183
+
184
+ return '' unless issuer
185
+
186
+ digest = OpenSSL::Digest.new('SHA1')
187
+ certificate_id = OpenSSL::OCSP::CertificateId.new(subject, issuer, digest)
188
+
189
+ request = OpenSSL::OCSP::Request.new
190
+ request.add_certid(certificate_id)
191
+
192
+ request.add_nonce
193
+
194
+ authority_info_access = subject.extensions.find do |extension|
195
+ extension.oid == 'authorityInfoAccess'
196
+ end
197
+
198
+ return '' unless authority_info_access
199
+
200
+ descriptions = authority_info_access.value.split("\n")
201
+ ocsp = descriptions.find do |description|
202
+ description.start_with? 'OCSP'
203
+ end
204
+
205
+ ocsp_uri = URI(ocsp[/URI:(.*)/, 1])
206
+
207
+ http_response = ::Net::HTTP.start(ocsp_uri.hostname, ocsp_uri.port) do |http|
208
+ ocsp_uri.path = '/' if ocsp_uri.path.empty?
209
+ http.post(ocsp_uri.path, request.to_der, 'content-type' => 'application/ocsp-request')
210
+ end
211
+
212
+ response = OpenSSL::OCSP::Response.new http_response.body
213
+ response_basic = response.basic
214
+
215
+ return '' unless response_basic&.verify([issuer], @checker.store)
216
+
217
+ response.status_string
218
+ end
219
+ end
220
+
221
+ class UnexpectedMessage < StandardError
222
+ def initialize(msg)
223
+ super(%(Unexpected message: "#{msg.chomp}"))
224
+ end
225
+ end
226
+
227
+ opt :uri, 'URI to check', short: :none, type: :strings
228
+ opt :checks, 'A list of checks to run.', short: :none, type: :strings, default: %w[identity not-after not-before ocsp trust]
229
+
230
+ opt :renewal_duration_days, 'Number of days before certificate expiration it is considered renewalable', short: :none, type: :integer, default: 90
231
+ opt :renewal_duration_ratio, 'Portion of the certificate lifespan it is considered renewalable', short: :none, type: :float, default: 1.0 / 3
232
+
233
+ opt :trust, 'Additionnal CA to trust', short: :none, type: :strings, default: []
234
+
235
+ opt :resolvers, 'Run this number of resolver threads', short: :none, type: :integer, default: 5
236
+ opt :workers, 'Run this number of worker threads', short: :none, type: :integer, default: 20
237
+ opt :connect_timeout, 'Timeout to espablish a connection (default to half the interval caped to 10s)', short: :none, type: :integer
238
+
239
+ def initialize
240
+ super
241
+
242
+ opts[:connect_timeout] ||= [10, opts[:interval] / 2].min
243
+
244
+ @resolve_queue = Queue.new
245
+ @work_queue = Queue.new
246
+
247
+ opts[:resolvers].times do
248
+ Thread.new do
249
+ loop do
250
+ uri = @resolve_queue.pop
251
+ host = uri.host
252
+
253
+ addresses = if host == 'localhost'
254
+ Socket.ip_address_list.select { |address| address.ipv6_loopback? || address.ipv4_loopback? }.map(&:ip_address)
255
+ else
256
+ Resolv::DNS.new.getaddresses(host)
257
+ end
258
+ if addresses.empty?
259
+ host = host[1...-1] if host[0] == '[' && host[-1] == ']'
260
+ begin
261
+ addresses << IPAddr.new(host)
262
+ rescue IPAddr::InvalidAddressError
263
+ # Ignore
264
+ end
265
+ end
266
+
267
+ @work_queue.push([uri, addresses])
268
+ end
269
+ end
270
+ end
271
+
272
+ opts[:workers].times do
273
+ Thread.new do
274
+ loop do
275
+ uri, addresses = @work_queue.pop
276
+ test_uri_addresses(uri, addresses)
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ def tick
283
+ report(
284
+ service: 'riemann tls-check resolvers utilization',
285
+ metric: (opts[:resolvers].to_f - @resolve_queue.num_waiting) / opts[:resolvers],
286
+ state: @resolve_queue.num_waiting.positive? ? 'ok' : 'critical',
287
+ tags: %w[riemann],
288
+ )
289
+ report(
290
+ service: 'riemann tls-check resolvers saturation',
291
+ metric: @resolve_queue.length,
292
+ state: @resolve_queue.empty? ? 'ok' : 'critical',
293
+ tags: %w[riemann],
294
+ )
295
+ report(
296
+ service: 'riemann tls-check workers utilization',
297
+ metric: (opts[:workers].to_f - @work_queue.num_waiting) / opts[:workers],
298
+ state: @work_queue.num_waiting.positive? ? 'ok' : 'critical',
299
+ tags: %w[riemann],
300
+ )
301
+ report(
302
+ service: 'riemann tls-check workers saturation',
303
+ metric: @work_queue.length,
304
+ state: @work_queue.empty? ? 'ok' : 'critical',
305
+ tags: %w[riemann],
306
+ )
307
+
308
+ opts[:uri].each do |uri|
309
+ @resolve_queue.push(URI(uri))
310
+ end
311
+ end
312
+
313
+ def test_uri_addresses(uri, addresses)
314
+ addresses.each do |address|
315
+ test_uri_address(uri, address.to_s)
316
+ end
317
+ end
318
+
319
+ def test_uri_address(uri, address)
320
+ socket = tls_socket(uri, address)
321
+ tls_check_result = TLSCheckResult.new(uri, address, socket, self)
322
+ report_availability(tls_check_result)
323
+ return unless socket.peer_cert
324
+
325
+ report_not_before(tls_check_result) if opts[:checks].include?('not-before')
326
+ report_not_after(tls_check_result) if opts[:checks].include?('not-after')
327
+ report_identity(tls_check_result) if opts[:checks].include?('identity')
328
+ report_trust(tls_check_result) if opts[:checks].include?('trust')
329
+ report_ocsp(tls_check_result) if opts[:checks].include?('ocsp')
330
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, UnexpectedMessage => e
331
+ report_unavailability(uri, address, e)
332
+ end
333
+
334
+ def report_availability(tls_check_result)
335
+ if tls_check_result.exception
336
+ report(
337
+ service: "#{tls_endpoint_name(tls_check_result)} availability",
338
+ state: 'critical',
339
+ description: tls_check_result.exception.message,
340
+ )
341
+ else
342
+ issues = []
343
+
344
+ issues << 'Certificate is not valid yet' if tls_check_result.not_valid_yet?
345
+ issues << 'Certificate has expired' if tls_check_result.expired?
346
+ issues << 'Certificate identity could not be verified' unless tls_check_result.valid_identity?
347
+ issues << 'Certificate is not trusted' unless tls_check_result.trusted?
348
+ issues << 'Certificate OCSP verification failed' if tls_check_result.ocsp? && !tls_check_result.valid_ocsp?
349
+
350
+ report(
351
+ service: "#{tls_endpoint_name(tls_check_result)} availability",
352
+ state: issues.empty? ? 'ok' : 'critical',
353
+ description: issues.join("\n"),
354
+ )
355
+ end
356
+ end
357
+
358
+ def report_unavailability(uri, address, exception)
359
+ report(
360
+ service: "#{tls_endpoint_name2(uri, address)} availability",
361
+ state: 'critical',
362
+ description: exception.message,
363
+ )
364
+ end
365
+
366
+ def report_not_after(tls_check_result)
367
+ report(
368
+ service: "#{tls_endpoint_name(tls_check_result)} not after",
369
+ state: not_after_state(tls_check_result),
370
+ metric: tls_check_result.not_after_ago,
371
+ description: tls_check_result.not_after_ago_in_words,
372
+ )
373
+ end
374
+
375
+ def report_not_before(tls_check_result)
376
+ report(
377
+ service: "#{tls_endpoint_name(tls_check_result)} not before",
378
+ state: not_before_state(tls_check_result),
379
+ metric: tls_check_result.not_before_away,
380
+ description: tls_check_result.not_before_away_in_words,
381
+ )
382
+ end
383
+
384
+ def report_identity(tls_check_result)
385
+ report(
386
+ service: "#{tls_endpoint_name(tls_check_result)} identity",
387
+ state: tls_check_result.valid_identity? ? 'ok' : 'critical',
388
+ description: "Valid for:\n#{tls_check_result.acceptable_identities.join("\n")}",
389
+ )
390
+ end
391
+
392
+ def report_trust(tls_check_result)
393
+ commont_attrs = {
394
+ service: "#{tls_endpoint_name(tls_check_result)} trust",
395
+ }
396
+ extra_attrs = if tls_check_result.exception
397
+ {
398
+ state: 'critical',
399
+ description: tls_check_result.exception.message,
400
+ }
401
+ else
402
+ {
403
+ state: tls_check_result.trusted? ? 'ok' : 'critical',
404
+ description: if OPENSSL_ERROR_STRINGS[tls_check_result.verify_result]
405
+ format('%<code>d - %<msg>s', code: tls_check_result.verify_result, msg: OPENSSL_ERROR_STRINGS[tls_check_result.verify_result])
406
+ else
407
+ tls_check_result.verify_result.to_s
408
+ end,
409
+ }
410
+ end
411
+ report(commont_attrs.merge(extra_attrs))
412
+ end
413
+
414
+ def report_ocsp(tls_check_result)
415
+ return unless tls_check_result.ocsp?
416
+
417
+ report(
418
+ service: "#{tls_endpoint_name(tls_check_result)} OCSP status",
419
+ state: tls_check_result.valid_ocsp? ? 'ok' : 'critical',
420
+ description: tls_check_result.ocsp_status,
421
+ )
422
+ end
423
+
424
+ # not_before not_after
425
+ # |<----------------------------->| validity_duration
426
+ # …ccccccccoooooooooooooooooooooooooooooooooooooo… not_before_state
427
+ #
428
+ # time --->>>>
429
+ def not_before_state(tls_check_result)
430
+ tls_check_result.not_valid_yet? ? 'critical' : 'ok'
431
+ end
432
+
433
+ # not_before not_after
434
+ # |<----------------------------->| validity_duration
435
+ # |<--------->| renewal_duration
436
+ # | ⅓ | ⅓ | ⅓ |
437
+ # …oooooooooooooooooooooooooooooooowwwwcccccccccc… not_after_state
438
+ #
439
+ # time --->>>>
440
+ def not_after_state(tls_check_result)
441
+ if tls_check_result.expired_or_expire_soon?
442
+ 'critical'
443
+ elsif tls_check_result.expire_soonish?
444
+ 'warning'
445
+ else
446
+ 'ok'
447
+ end
448
+ end
449
+
450
+ def tcp_socket(host, port)
451
+ Socket.tcp(host, port, connect_timeout: opts[:connect_timeout])
452
+ end
453
+
454
+ def tls_socket(uri, address)
455
+ case uri.scheme
456
+ when 'smtp'
457
+ smtp_tls_socket(uri, address)
458
+ when 'imap'
459
+ imap_tls_socket(uri, address)
460
+ when 'ldap'
461
+ ldap_tls_socket(uri, address)
462
+ when 'mysql'
463
+ mysql_tls_socket(uri, address)
464
+ when 'postgres'
465
+ postgres_tls_socket(uri, address)
466
+ else
467
+ raw_tls_socket(uri, address)
468
+ end
469
+ end
470
+
471
+ def mysql_tls_socket(uri, address)
472
+ socket = tcp_socket(address, uri.port)
473
+ length = "#{socket.read(3)}\0".unpack1('L*')
474
+ _sequence = socket.read(1)
475
+ body = socket.read(length)
476
+ initial_handshake_packet = body.unpack('cZ*La8aScSS')
477
+
478
+ capabilities = initial_handshake_packet[5] | (initial_handshake_packet[8] << 16)
479
+
480
+ ssl_flag = 1 << 11
481
+ raise 'No TLS support' if (capabilities & ssl_flag).zero?
482
+
483
+ socket.write(['2000000185ae7f0000000001210000000000000000000000000000000000000000000000'].pack('H*'))
484
+ tls_handshake(socket, uri.host)
485
+ end
486
+
487
+ def postgres_tls_socket(uri, address)
488
+ socket = tcp_socket(address, uri.port)
489
+ socket.write(['0000000804d2162f'].pack('H*'))
490
+ raise 'Unexpected reply' unless socket.read(1) == 'S'
491
+
492
+ tls_handshake(socket, uri.host)
493
+ end
494
+
495
+ def smtp_tls_socket(uri, address)
496
+ socket = tcp_socket(address, uri.port)
497
+ read_socket_lines_until_prefix_matched(socket, '220 ', also_accept_prefixes: ['220-'])
498
+ socket.send("EHLO #{my_hostname}\r\n", 0)
499
+ read_socket_lines_until_prefix_matched(socket, '250 ', also_accept_prefixes: ['250-'])
500
+ socket.send("STARTTLS\r\n", 0)
501
+ socket.gets
502
+
503
+ tls_handshake(socket, uri.host)
504
+ end
505
+
506
+ def my_hostname
507
+ Addrinfo.tcp(Socket.gethostname, 8023).getnameinfo.first
508
+ rescue SocketError
509
+ Socket.gethostname
510
+ end
511
+
512
+ def read_socket_lines_until_prefix_matched(socket, prefix, also_accept_prefixes: [])
513
+ loop do
514
+ line = socket.gets
515
+ break if line.start_with?(prefix)
516
+ next if also_accept_prefixes.map { |accepted_prefix| line.start_with?(accepted_prefix) }.any?
517
+
518
+ raise UnexpectedMessage, line
519
+ end
520
+ end
521
+
522
+ def imap_tls_socket(uri, address)
523
+ socket = tcp_socket(address, uri.port)
524
+ read_socket_lines_until_prefix_matched(socket, '* OK')
525
+ socket.send(". CAPABILITY\r\n", 0)
526
+ read_socket_lines_until_prefix_matched(socket, '. OK', also_accept_prefixes: ['* CAPABILITY'])
527
+ socket.send(". STARTTLS\r\n", 0)
528
+ read_socket_lines_until_prefix_matched(socket, '. OK')
529
+
530
+ tls_handshake(socket, uri.host)
531
+ end
532
+
533
+ def ldap_tls_socket(uri, address)
534
+ socket = tcp_socket(address, uri.port)
535
+ socket.write(['301d02010177188016312e332e362e312e342e312e313436362e3230303337'].pack('H*'))
536
+ expected_res = ['300c02010178070a010004000400'].pack('H*')
537
+ res = socket.read(expected_res.length)
538
+
539
+ return nil unless res == expected_res
540
+
541
+ tls_handshake(socket, uri.host)
542
+ end
543
+
544
+ def raw_tls_socket(uri, address)
545
+ raise "No default port for #{uri.scheme} scheme" unless uri.port
546
+
547
+ socket = tcp_socket(address, uri.port)
548
+ tls_handshake(socket, uri.host)
549
+ end
550
+
551
+ def tls_handshake(raw_socket, hostname)
552
+ tls_socket = OpenSSL::SSL::SSLSocket.new(raw_socket, ssl_context)
553
+ tls_socket.hostname = hostname
554
+ begin
555
+ tls_socket.connect
556
+ rescue OpenSSL::SSL::SSLError => e
557
+ # This may fail for example if a client certificate is required but
558
+ # not provided. In this case, the remote certificate is available and
559
+ # we can ignore this issue. In other cases, the remote certificate is
560
+ # not available, in this case we want to stop and report the issue
561
+ # (e.g. connecting to a host with a SNI for a name not handled by
562
+ # that host).
563
+ tls_socket.define_singleton_method(:exception) do
564
+ e
565
+ end
566
+ end
567
+ tls_socket
568
+ end
569
+
570
+ def ssl_context
571
+ @ssl_context ||= begin
572
+ ctx = OpenSSL::SSL::SSLContext.new
573
+ ctx.cert_store = store
574
+ ctx.verify_hostname = false
575
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
576
+ ctx
577
+ end
578
+ end
579
+
580
+ def store
581
+ @store ||= begin
582
+ store = OpenSSL::X509::Store.new
583
+ store.set_default_paths
584
+ opts[:trust].each do |path|
585
+ if File.directory?(path)
586
+ store.add_path(path)
587
+ else
588
+ store.add_file(path)
589
+ end
590
+ end
591
+ store
592
+ end
593
+ end
594
+
595
+ def tls_endpoint_name(tls_check_result)
596
+ tls_endpoint_name2(tls_check_result.uri, tls_check_result.address)
597
+ end
598
+
599
+ def tls_endpoint_name2(uri, address)
600
+ "TLS certificate #{uri} #{endpoint_name(IPAddr.new(address), uri.port)}"
601
+ end
602
+ end
603
+ end
604
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'socket'
4
+
3
5
  module Riemann
4
6
  module Tools
5
7
  module Utils # :nodoc:
@@ -60,6 +62,43 @@ module Riemann
60
62
 
61
63
  (header + lines[0, count]).join("\n")
62
64
  end
65
+
66
+ def when_from_utcnow(date)
67
+ if date > utcnow
68
+ "in #{distance_of_time_in_words_to_utcnow(date)}"
69
+ else
70
+ "#{distance_of_time_in_words_to_utcnow(date)} ago"
71
+ end
72
+ end
73
+
74
+ # Stolen from ActionView, to avoid pulling a lot of dependencies
75
+ def distance_of_time_in_words_to_utcnow(to_time)
76
+ distance_in_seconds = (to_time - utcnow).round.abs
77
+ distance_in_minutes = distance_in_seconds / 60
78
+
79
+ case distance_in_minutes
80
+ when 0 then 'less than 1 minute'
81
+ when 1...45 then pluralize_string('%d minute', distance_in_minutes)
82
+ when 45...1440 then pluralize_string('about %d hour', (distance_in_minutes.to_f / 60.0).round)
83
+ # 24 hours up to 30 days
84
+ when 1440...43_200 then pluralize_string('%d day', (distance_in_minutes.to_f / 1440.0).round)
85
+ # 30 days up to 60 days
86
+ when 43_200...86_400 then pluralize_string('about %d month', (distance_in_minutes.to_f / 43_200.0).round)
87
+ # 60 days up to 365 days
88
+ when 86_400...525_600 then pluralize_string('%d month', (distance_in_minutes.to_f / 43_200.0).round)
89
+ else
90
+ pluralize_string('about %d year', (distance_in_minutes.to_f / 525_600.0).round)
91
+ end
92
+ end
93
+
94
+ def pluralize_string(string, number)
95
+ format(string, number) + (number == 1 ? '' : 's')
96
+ end
97
+
98
+ # The current date and time, but in UTC
99
+ def utcnow
100
+ Time.at(Time.now, in: '+00:00')
101
+ end
63
102
  end
64
103
  end
65
104
  end
@@ -12,6 +12,8 @@ module Riemann
12
12
  opt :varnish_host, 'Varnish hostname', default: `hostname`.chomp
13
13
 
14
14
  def initialize
15
+ super
16
+
15
17
  cmd = 'varnishstat -V'
16
18
  Open3.popen3(cmd) do |_stdin, _stdout, stderr, _wait_thr|
17
19
  @ver = /varnishstat \(varnish-(\d+)/.match(stderr.read)[1].to_i
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Riemann
4
4
  module Tools # :nodoc:
5
- VERSION = '1.10.0'
5
+ VERSION = '1.11.0'
6
6
  end
7
7
  end