fluent-plugin-ssl-check 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -3
- data/Gemfile.lock +1 -1
- data/README.md +51 -0
- data/fluent-plugin-ssl-check.gemspec +1 -1
- data/lib/fluent/plugin/in_ssl_check.rb +48 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab7b3def2e942f92cf1b8a1fa46f4c7bafe50fbee692334736e19574849e9911
|
4
|
+
data.tar.gz: 5083966b749ba1407cdce60b33f8952dce4b300c5bd7e33496e1b1e02ae0814a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe0d1bd834d8831a510df5754cb488d2e543fbca465bf3b344312b92f236278fbd75cf972efee0d0ffc45838f4550d09f81dba34fbcbf0445810a831b6fda365
|
7
|
+
data.tar.gz: 8cef76bf6831eae2d1053a99699fc07f31077939bd3bc5ab28a402be4d709f9f4eec53ec9c14e954a01cefd5866a399e4d2ceeb373bd0e94d205023bf0375b3a
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[Fluentd](https://fluentd.org/) input plugin to check ssl service.
|
4
4
|
|
5
|
+
|
5
6
|
## plugins
|
6
7
|
|
7
8
|
### in - ssl_check
|
@@ -30,6 +31,10 @@ Options are:
|
|
30
31
|
* interval: check every X seconds
|
31
32
|
* ca_path: directory that contains CA files
|
32
33
|
* ca_file: specify a CA file directly
|
34
|
+
* sni: want the sni support (true)
|
35
|
+
* verify_mode: none or peer
|
36
|
+
* cert: client cert for ssl connection
|
37
|
+
* key: client key associated to client cert for ssl connection
|
33
38
|
* timeout: timeout for ssl check execution (5sec)
|
34
39
|
* log_events: emit log format (true)
|
35
40
|
* metric_events: emit metric format (false)
|
@@ -38,6 +43,52 @@ Options are:
|
|
38
43
|
|
39
44
|
If no port is specified with host, default port is 443.
|
40
45
|
|
46
|
+
|
47
|
+
## output examples
|
48
|
+
|
49
|
+
### log output example
|
50
|
+
|
51
|
+
``` json
|
52
|
+
{
|
53
|
+
"timestamp": "2023-10-03T09:59:41.580+02:00",
|
54
|
+
"status": 1,
|
55
|
+
"host": "www.google.fr",
|
56
|
+
"port": 443,
|
57
|
+
"ssl_version": "TLSv1.2",
|
58
|
+
"ssl_dn": "/CN=*.google.fr",
|
59
|
+
"ssl_not_after": "2023-11-27T08:25:08.000Z",
|
60
|
+
"expire_in_days": 55,
|
61
|
+
"serial": "4e79dbb13c6b57b309780da2d1edbda4"
|
62
|
+
}
|
63
|
+
```
|
64
|
+
|
65
|
+
### metric output example
|
66
|
+
|
67
|
+
``` json
|
68
|
+
{
|
69
|
+
"timestamp": "2023-10-03T10:06:21.417+02:00",
|
70
|
+
"metric_name": "ssl_status",
|
71
|
+
"metric_value": 1,
|
72
|
+
"host": "www.google.fr",
|
73
|
+
"port": 443,
|
74
|
+
"ssl_dn": "/CN=*.google.fr",
|
75
|
+
"ssl_version": "TLSv1.2",
|
76
|
+
"ssl_not_after": "2023-11-27T08:25:08.000Z",
|
77
|
+
"serial": "4e79dbb13c6b57b309780da2d1edbda4"
|
78
|
+
}
|
79
|
+
|
80
|
+
{
|
81
|
+
"timestamp": "2023-10-03T10:06:21.417+02:00",
|
82
|
+
"metric_name": "ssl_expirency",
|
83
|
+
"metric_value": 55,
|
84
|
+
"host": "www.google.fr",
|
85
|
+
"port": 443,
|
86
|
+
"ssl_dn": "/CN=*.google.fr",
|
87
|
+
"serial": "4e79dbb13c6b57b309780da2d1edbda4"
|
88
|
+
}
|
89
|
+
```
|
90
|
+
|
91
|
+
|
41
92
|
## Installation
|
42
93
|
|
43
94
|
Manual install, by executing:
|
@@ -33,10 +33,10 @@ module Fluent
|
|
33
33
|
Fluent::Plugin.register_input(NAME, self)
|
34
34
|
|
35
35
|
DEFAULT_TAG = NAME
|
36
|
-
DEFAULT_HOST = 'localhost'
|
37
36
|
DEFAULT_PORT = 443
|
38
37
|
DEFAULT_INTERVAL = 600
|
39
38
|
DEFAULT_SNI = true
|
39
|
+
DEFAULT_VERIFY_MODE = :peer
|
40
40
|
DEFAULT_TIMEOUT = 5
|
41
41
|
DEFAULT_LOG_EVENTS = true
|
42
42
|
DEFAULT_METRIC_EVENTS = false
|
@@ -55,6 +55,12 @@ module Fluent
|
|
55
55
|
config_param :ca_file, :string, default: nil
|
56
56
|
desc 'SNI support'
|
57
57
|
config_param :sni, :bool, default: DEFAULT_SNI
|
58
|
+
desc 'Verify mode'
|
59
|
+
config_param :verify_mode, :enum, list: %i[none peer], default: DEFAULT_VERIFY_MODE
|
60
|
+
desc 'Client Cert'
|
61
|
+
config_param :cert, :string, default: nil
|
62
|
+
desc 'Client Key'
|
63
|
+
config_param :key, :string, default: nil
|
58
64
|
|
59
65
|
desc 'Timeout for check'
|
60
66
|
config_param :timeout, :integer, default: DEFAULT_TIMEOUT
|
@@ -70,17 +76,22 @@ module Fluent
|
|
70
76
|
|
71
77
|
helpers :timer
|
72
78
|
|
73
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
79
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Style/DoubleNegation
|
74
80
|
def configure(conf)
|
75
81
|
super
|
76
82
|
|
77
83
|
raise Fluent::ConfigError, 'tag can not be empty.' if !tag || tag.empty?
|
78
|
-
raise Fluent::ConfigError, 'hosts can not be empty.'
|
84
|
+
raise Fluent::ConfigError, 'hosts can not be empty.' unless hosts
|
79
85
|
raise Fluent::ConfigError, 'interval can not be < 1.' if !interval || interval < 1
|
80
86
|
raise Fluent::ConfigError, 'ca_path should be a dir.' if ca_path && !File.directory?(ca_path)
|
81
87
|
raise Fluent::ConfigError, 'ca_file should be a file.' if ca_file && !File.file?(ca_file)
|
88
|
+
raise Fluent::ConfigError, 'cert should be a file.' if cert && !File.file?(cert)
|
89
|
+
raise Fluent::ConfigError, 'key should be a file.' if key && !File.file?(key)
|
90
|
+
raise Fluent::ConfigError, 'cert and key should be specified.' if !!cert ^ !!key
|
91
|
+
|
92
|
+
log.warn("#{NAME}: hosts is empty, nothing to process") if hosts.empty?
|
82
93
|
end
|
83
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
94
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Style/DoubleNegation
|
84
95
|
|
85
96
|
def start
|
86
97
|
super
|
@@ -107,7 +118,9 @@ module Fluent
|
|
107
118
|
ssl_client = SslClient.new(
|
108
119
|
host: host, port: port,
|
109
120
|
ca_path: ca_path, ca_file: ca_file,
|
110
|
-
sni: sni,
|
121
|
+
sni: sni, verify_mode: ssl_verify_mode,
|
122
|
+
cert: cert, key: key,
|
123
|
+
timeout: timeout
|
111
124
|
)
|
112
125
|
ssl_client.ssl_info
|
113
126
|
end
|
@@ -121,7 +134,8 @@ module Fluent
|
|
121
134
|
'ssl_version' => ssl_info.ssl_version,
|
122
135
|
'ssl_dn' => ssl_info.subject_s,
|
123
136
|
'ssl_not_after' => ssl_info.not_after,
|
124
|
-
'expire_in_days' => ssl_info.expire_in_days
|
137
|
+
'expire_in_days' => ssl_info.expire_in_days,
|
138
|
+
'serial' => ssl_info.serial
|
125
139
|
}
|
126
140
|
record.update('error_class' => ssl_info.error_class) if ssl_info.error_class
|
127
141
|
router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
|
@@ -141,7 +155,8 @@ module Fluent
|
|
141
155
|
"#{event_prefix}port" => ssl_info.port,
|
142
156
|
"#{event_prefix}ssl_dn" => ssl_info.subject_s,
|
143
157
|
"#{event_prefix}ssl_version" => ssl_info.ssl_version,
|
144
|
-
"#{event_prefix}ssl_not_after" => ssl_info.not_after
|
158
|
+
"#{event_prefix}ssl_not_after" => ssl_info.not_after,
|
159
|
+
"#{event_prefix}serial" => ssl_info.serial
|
145
160
|
}
|
146
161
|
router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
|
147
162
|
end
|
@@ -155,11 +170,20 @@ module Fluent
|
|
155
170
|
'metric_value' => ssl_info.expire_in_days,
|
156
171
|
"#{event_prefix}host" => ssl_info.host,
|
157
172
|
"#{event_prefix}port" => ssl_info.port,
|
158
|
-
"#{event_prefix}ssl_dn" => ssl_info.subject_s
|
173
|
+
"#{event_prefix}ssl_dn" => ssl_info.subject_s,
|
174
|
+
"#{event_prefix}serial" => ssl_info.serial
|
159
175
|
}
|
160
176
|
router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
|
161
177
|
end
|
162
178
|
|
179
|
+
private
|
180
|
+
|
181
|
+
def ssl_verify_mode
|
182
|
+
return OpenSSL::SSL::VERIFY_PEER if verify_mode == :peer
|
183
|
+
|
184
|
+
OpenSSL::SSL::VERIFY_NONE
|
185
|
+
end
|
186
|
+
|
163
187
|
# ssl info
|
164
188
|
# to encapsulate extracted ssl information
|
165
189
|
class SslInfo
|
@@ -198,6 +222,10 @@ module Fluent
|
|
198
222
|
cert.not_after.iso8601(3)
|
199
223
|
end
|
200
224
|
|
225
|
+
def serial
|
226
|
+
cert&.serial&.to_s(16)&.downcase
|
227
|
+
end
|
228
|
+
|
201
229
|
def status
|
202
230
|
return KO if error
|
203
231
|
|
@@ -214,16 +242,23 @@ module Fluent
|
|
214
242
|
# ssl client
|
215
243
|
# to check ssl status
|
216
244
|
class SslClient
|
217
|
-
attr_reader :host, :port, :ca_path, :ca_file, :sni, :timeout
|
245
|
+
attr_reader :host, :port, :ca_path, :ca_file, :sni, :verify_mode, :cert, :key, :timeout
|
218
246
|
|
219
|
-
|
247
|
+
# rubocop:disable Metrics/ParameterLists
|
248
|
+
def initialize(host:, port:, ca_path: nil, ca_file: nil, sni: true, verify_mode: OpenSSL::SSL::VERIFY_PEER,
|
249
|
+
cert: nil, key: nil,
|
250
|
+
timeout: 5)
|
220
251
|
@host = host
|
221
252
|
@port = port
|
222
253
|
@ca_path = ca_path
|
223
254
|
@ca_file = ca_file
|
224
255
|
@sni = sni
|
256
|
+
@verify_mode = verify_mode
|
257
|
+
@cert = cert
|
258
|
+
@key = key
|
225
259
|
@timeout = timeout
|
226
260
|
end
|
261
|
+
# rubocop:enable Metrics/ParameterLists
|
227
262
|
|
228
263
|
def ssl_info
|
229
264
|
info = SslInfo.new(host: host, port: port)
|
@@ -257,10 +292,12 @@ module Fluent
|
|
257
292
|
|
258
293
|
def ssl_context
|
259
294
|
OpenSSL::SSL::SSLContext.new.tap do |ssl_context|
|
260
|
-
ssl_context.verify_mode =
|
295
|
+
ssl_context.verify_mode = verify_mode
|
261
296
|
ssl_context.cert_store = store
|
262
297
|
ssl_context.min_version = nil
|
263
298
|
ssl_context.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
299
|
+
ssl_context.cert = OpenSSL::X509::Certificate.new(File.open(cert)) if cert
|
300
|
+
ssl_context.key = OpenSSL::PKey::RSA.new(File.open(key)) if key
|
264
301
|
end
|
265
302
|
end
|
266
303
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-ssl-check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Tych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|