logstash-output-tcp 6.1.2 → 6.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd2acefd6c26058e3e2afc94abb73de74e2816ca57a6379229909434ad4c72e9
4
- data.tar.gz: 38633ca4cf74d5360fa3644b9624cb21db997e9cda5ff8846f5ca510b811c133
3
+ metadata.gz: 12843c570af7ef789c697435e55375ecda6d6ed85a63181ae0501cc21298bbeb
4
+ data.tar.gz: 95a41abe82bb81210646428fb6d228a390a7ba87043e817dab5a75b19405195e
5
5
  SHA512:
6
- metadata.gz: ef785946ab821ca9347ef5fd04e17909ef573155ce790955640b875582a268363c436c3fa6a6c8c8cc19d086a6a9e6d5b8452448be59fbd80328481a7e0f292e
7
- data.tar.gz: ac1c3b27079ecb0bb91eaa4a11eea17a1f79d4b6d935e72d82085b34f6731ee2ed6e6240250e4c18429b669d1430875d8ef958aa4c009ae71bb75d3e73f6a414
6
+ metadata.gz: 50d788801e0d243a7e3ba77cf4c96fb31e5a273656a99a6f5b5714076cb1bc4a5a0b5c3dcf8b3d36c7402d38a442129ebaf72b1f9cce7ec4dd22a532a7cd72ec
7
+ data.tar.gz: 43e9a8f1c6e401801eb8ead0fc6773ff39c3f2efa0f112cbf63075050769f2acb09f870734001c9678ee679fd2f633243c22cdb3d4cf8455ea6ae69f672d5fcc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 6.2.1
2
+ - Document correct default plugin codec [#54](https://github.com/logstash-plugins/logstash-output-tcp/pull/54)
3
+
4
+ ## 6.2.0
5
+ - Reviewed and deprecated SSL settings to comply with Logstash's naming convention [#53](https://github.com/logstash-plugins/logstash-output-tcp/pull/53)
6
+ - Deprecated `ssl_enable` in favor of `ssl_enabled`
7
+ - Deprecated `ssl_cert` in favor of `ssl_certificate`
8
+ - Deprecated `ssl_verify` in favor of `ssl_client_authentication` when mode is `server`
9
+ - Deprecated `ssl_verify` in favor of `ssl_verification_mode` when mode is `client`
10
+ - Added `ssl_cipher_suites` configuration
11
+ - Added SSL configuration validations
12
+
1
13
  ## 6.1.2
2
14
  - Changed the client mode to write using the non-blocking method. [#52](https://github.com/logstash-plugins/logstash-output-tcp/pull/52)
3
15
 
data/docs/index.asciidoc CHANGED
@@ -23,7 +23,7 @@ include::{include_path}/plugin_header.asciidoc[]
23
23
 
24
24
  Write events over a TCP socket.
25
25
 
26
- Each event json is separated by a newline.
26
+ By default this plugin uses the `json` codec. In order to have each event json separated by a newline, use the `json_lines` codec.
27
27
 
28
28
  Can either accept connections from clients or connect to a server,
29
29
  depending on `mode`.
@@ -40,12 +40,18 @@ This plugin supports the following configuration options plus the <<plugins-{typ
40
40
  | <<plugins-{type}s-{plugin}-mode>> |<<string,string>>, one of `["server", "client"]`|No
41
41
  | <<plugins-{type}s-{plugin}-port>> |<<number,number>>|Yes
42
42
  | <<plugins-{type}s-{plugin}-reconnect_interval>> |<<number,number>>|No
43
- | <<plugins-{type}s-{plugin}-ssl_cacert>> |a valid filesystem path|No
44
- | <<plugins-{type}s-{plugin}-ssl_cert>> |a valid filesystem path|No
45
- | <<plugins-{type}s-{plugin}-ssl_enable>> |<<boolean,boolean>>|No
43
+ | <<plugins-{type}s-{plugin}-ssl_cacert>> |a valid filesystem path|__Deprecated__
44
+ | <<plugins-{type}s-{plugin}-ssl_cert>> |a valid filesystem path|__Deprecated__
45
+ | <<plugins-{type}s-{plugin}-ssl_certificate>> |a valid filesystem path|No
46
+ | <<plugins-{type}s-{plugin}-ssl_certificate_authorities>> |<<array,array>>|No
47
+ | <<plugins-{type}s-{plugin}-ssl_cipher_suites>> |<<string,string>>|No
48
+ | <<plugins-{type}s-{plugin}-ssl_client_authentication>> |<<string,string>>, one of `["none", "optional", "required"]`|No
49
+ | <<plugins-{type}s-{plugin}-ssl_enable>> |<<boolean,boolean>>|__Deprecated__
50
+ | <<plugins-{type}s-{plugin}-ssl_enabled>> |<<boolean,boolean>>|No
46
51
  | <<plugins-{type}s-{plugin}-ssl_key>> |a valid filesystem path|No
47
52
  | <<plugins-{type}s-{plugin}-ssl_key_passphrase>> |<<password,password>>|No
48
53
  | <<plugins-{type}s-{plugin}-ssl_supported_protocols>> |<<string,string>>|No
54
+ | <<plugins-{type}s-{plugin}-ssl_verification_mode>> |<<string,string>>, one of `["full", "none"]`|No
49
55
  | <<plugins-{type}s-{plugin}-ssl_verify>> |<<boolean,boolean>>|No
50
56
  |=======================================================================
51
57
 
@@ -93,6 +99,7 @@ When connect failed,retry interval in sec.
93
99
 
94
100
  [id="plugins-{type}s-{plugin}-ssl_cacert"]
95
101
  ===== `ssl_cacert`
102
+ deprecated[6.2.0, Replaced by <<plugins-{type}s-{plugin}-ssl_certificate_authorities>>]
96
103
 
97
104
  * Value type is <<path,path>>
98
105
  * There is no default value for this setting.
@@ -101,14 +108,69 @@ The SSL CA certificate, chainfile or CA path. The system CA path is automaticall
101
108
 
102
109
  [id="plugins-{type}s-{plugin}-ssl_cert"]
103
110
  ===== `ssl_cert`
111
+ deprecated[6.2.0, Replaced by <<plugins-{type}s-{plugin}-ssl_certificate>>]
104
112
 
105
113
  * Value type is <<path,path>>
106
114
  * There is no default value for this setting.
107
115
 
108
116
  SSL certificate path
109
117
 
118
+ [id="plugins-{type}s-{plugin}-ssl_certificate"]
119
+ ===== `ssl_certificate`
120
+
121
+ * Value type is <<path,path>>
122
+ * There is no default value for this setting.
123
+
124
+ Path to certificate in PEM format. This certificate will be presented
125
+ to the other part of the TLS connection.
126
+
127
+ [id="plugins-{type}s-{plugin}-ssl_certificate_authorities"]
128
+ ===== `ssl_certificate_authorities`
129
+
130
+ * Value type is <<array,array>>
131
+ * Default value is `[]`
132
+
133
+ Validate client certificate or certificate chain against these authorities.
134
+ You can define multiple files. All the certificates will be read and added to the trust store.
135
+ The system CA path is automatically included.
136
+
137
+ [id="plugins-{type}s-{plugin}-ssl_cipher_suites"]
138
+ ===== `ssl_cipher_suites`
139
+
140
+ * Value type is a list of <<string,string>>
141
+ * There is no default value for this setting
142
+
143
+ The list of cipher suites to use, listed by priorities.
144
+ Supported cipher suites vary depending on the Java and protocol versions.
145
+
146
+ [id="plugins-{type}s-{plugin}-ssl_client_authentication"]
147
+ ===== `ssl_client_authentication`
148
+
149
+ * Value can be any of: `none`, `optional`, `required`
150
+ * Default value is `none`
151
+
152
+ Controls the server's behavior in regard to requesting a certificate from client connections:
153
+ `none` disables the client authentication. `required` forces a client to present a certificate, while `optional` requests a client certificate
154
+ but the client is not required to present one.
155
+
156
+ When mutual TLS is enabled (`optional` or `required`), the certificate presented by the client must be signed by trusted
157
+ <<plugins-{type}s-{plugin}-ssl_certificate_authorities>> (CAs).
158
+ Please note that the server does not validate the client certificate CN (Common Name) or SAN (Subject Alternative Name).
159
+
160
+ NOTE: This setting can be used only if <<plugins-{type}s-{plugin}-mode>> is `server` and <<plugins-{type}s-{plugin}-ssl_certificate_authorities>> is set.
161
+
162
+
110
163
  [id="plugins-{type}s-{plugin}-ssl_enable"]
111
164
  ===== `ssl_enable`
165
+ deprecated[6.2.0, Replaced by <<plugins-{type}s-{plugin}-ssl_enabled>>]
166
+
167
+ * Value type is <<boolean,boolean>>
168
+ * Default value is `false`
169
+
170
+ Enable SSL (must be set for other `ssl_` options to take effect).
171
+
172
+ [id="plugins-{type}s-{plugin}-ssl_enabled"]
173
+ ===== `ssl_enabled`
112
174
 
113
175
  * Value type is <<boolean,boolean>>
114
176
  * Default value is `false`
@@ -145,8 +207,25 @@ NOTE: If you configure the plugin to use `'TLSv1.1'` on any recent JVM, such as
145
207
  the protocol is disabled by default and needs to be enabled manually by changing `jdk.tls.disabledAlgorithms` in
146
208
  the *$JDK_HOME/conf/security/java.security* configuration file. That is, `TLSv1.1` needs to be removed from the list.
147
209
 
210
+ [id="plugins-{type}s-{plugin}-ssl_verification_mode"]
211
+ ===== `ssl_verification_mode`
212
+
213
+ * Value can be any of: `full`, `none`
214
+ * Default value is `full`
215
+
216
+ Defines how to verify the certificates presented by another part in the TLS connection:
217
+
218
+ `full` validates that the server certificate has an issue date that's within
219
+ the not_before and not_after dates; chains to a trusted Certificate Authority (CA), and
220
+ has a hostname or IP address that matches the names within the certificate.
221
+
222
+ `none` performs no certificate validation.
223
+
224
+ NOTE: This setting can be used only if <<plugins-{type}s-{plugin}-mode>> is `client`.
225
+
148
226
  [id="plugins-{type}s-{plugin}-ssl_verify"]
149
227
  ===== `ssl_verify`
228
+ deprecated[6.2.0, Replaced by <<plugins-{type}s-{plugin}-ssl_client_authentication>> and <<plugins-{type}s-{plugin}-ssl_verification_mode>>]
150
229
 
151
230
  * Value type is <<boolean,boolean>>
152
231
  * Default value is `false`
@@ -154,8 +233,6 @@ the *$JDK_HOME/conf/security/java.security* configuration file. That is, `TLSv1.
154
233
  Verify the identity of the other end of the SSL connection against the CA.
155
234
  For input, sets the field `sslsubject` to that of the client certificate.
156
235
 
157
-
158
-
159
236
  [id="plugins-{type}s-{plugin}-common-options"]
160
237
  include::{include_path}/{type}.asciidoc[]
161
238
 
@@ -3,6 +3,7 @@ require "logstash/outputs/base"
3
3
  require "logstash/namespace"
4
4
  require "thread"
5
5
  require "logstash/util/socket_peer"
6
+ require "logstash/plugin_mixins/normalize_config_support"
6
7
 
7
8
  # Write events over a TCP socket.
8
9
  #
@@ -12,6 +13,8 @@ require "logstash/util/socket_peer"
12
13
  # depending on `mode`.
13
14
  class LogStash::Outputs::Tcp < LogStash::Outputs::Base
14
15
 
16
+ include LogStash::PluginMixins::NormalizeConfigSupport
17
+
15
18
  config_name "tcp"
16
19
  concurrency :single
17
20
 
@@ -33,17 +36,41 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
33
36
  config :mode, :validate => ["server", "client"], :default => "client"
34
37
 
35
38
  # Enable SSL (must be set for other `ssl_` options to take effect).
36
- config :ssl_enable, :validate => :boolean, :default => false
39
+ config :ssl_enable, :validate => :boolean, :default => false, :deprecated => "Use 'ssl_enabled' instead."
40
+
41
+ # Enable SSL (must be set for other `ssl_` options to take effect).
42
+ config :ssl_enabled, :validate => :boolean, :default => false
43
+
44
+ # Controls the server’s behavior in regard to requesting a certificate from client connections.
45
+ # `none`: No client authentication
46
+ # `optional`: Requests a client certificate but the client is not required to present one.
47
+ # `required`: Forces a client to present a certificate.
48
+ # This option needs to be used with `ssl_certificate_authorities` and a defined list of CAs.
49
+ config :ssl_client_authentication, :validate => %w[none optional required], :default => 'none'
37
50
 
38
51
  # Verify the identity of the other end of the SSL connection against the CA.
39
52
  # For input, sets the field `sslsubject` to that of the client certificate.
40
- config :ssl_verify, :validate => :boolean, :default => false
53
+ config :ssl_verify, :validate => :boolean, :default => false, :deprecated => "Use 'ssl_client_authentication' when `mode` is 'server' or 'ssl_verification_mode' when mode is `client`"
54
+
55
+ # Options to verify the server's certificate.
56
+ # "full": validates that the provided certificate has an issue date that’s within the not_before and not_after dates;
57
+ # chains to a trusted Certificate Authority (CA); has a hostname or IP address that matches the names within the certificate.
58
+ # "certificate": Validates the provided certificate and verifies that it’s signed by a trusted authority (CA), but does’t check the certificate hostname.
59
+ # "none": performs no certificate validation. Disabling this severely compromises security (https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf)
60
+ config :ssl_verification_mode, :validate => %w[full none], :default => 'full'
41
61
 
42
62
  # The SSL CA certificate, chainfile or CA path. The system CA path is automatically included.
43
- config :ssl_cacert, :validate => :path
63
+ config :ssl_cacert, :validate => :path, :deprecated => "Use 'ssl_certificate_authorities' instead."
64
+
65
+ # Validate client certificate or certificate chain against these authorities. You can define multiple files.
66
+ # All the certificates will be read and added to the trust store.
67
+ config :ssl_certificate_authorities, :validate => :path, :list => true
68
+
69
+ # SSL certificate path
70
+ config :ssl_cert, :validate => :path, :deprecated => "Use 'ssl_certificate' instead."
44
71
 
45
72
  # SSL certificate path
46
- config :ssl_cert, :validate => :path
73
+ config :ssl_certificate, :validate => :path
47
74
 
48
75
  # SSL key path
49
76
  config :ssl_key, :validate => :path
@@ -54,6 +81,9 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
54
81
  # NOTE: the default setting [] uses SSL engine defaults
55
82
  config :ssl_supported_protocols, :validate => ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], :default => [], :list => true
56
83
 
84
+ # The list of ciphers suite to use
85
+ config :ssl_cipher_suites, :validate => :string, :list => true
86
+
57
87
  class Client
58
88
 
59
89
  ##
@@ -95,8 +125,8 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
95
125
  require "openssl"
96
126
 
97
127
  @ssl_context = new_ssl_context
98
- if @ssl_cert
99
- @ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_cert))
128
+ if @ssl_certificate
129
+ @ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_certificate))
100
130
  if @ssl_key
101
131
  # if we have an encrypted key and a password is not provided (nil) than OpenSSL::PKey::RSA
102
132
  # prompts the user to enter a password interactively - we do not want to do that,
@@ -104,17 +134,21 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
104
134
  @ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@ssl_key), @ssl_key_passphrase.value || '')
105
135
  end
106
136
  end
107
- if @ssl_verify
108
- @cert_store = OpenSSL::X509::Store.new
109
- # Load the system default certificate path to the store
110
- @cert_store.set_default_paths
111
- if File.directory?(@ssl_cacert)
112
- @cert_store.add_path(@ssl_cacert)
137
+
138
+ @ssl_context.cert_store = load_cert_store
139
+ if server?
140
+ if @ssl_client_authentication == 'none'
141
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
142
+ else
143
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
144
+ @ssl_context.verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT if @ssl_client_authentication == 'required'
145
+ end
146
+ else
147
+ if @ssl_verification_mode == 'none'
148
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
113
149
  else
114
- @cert_store.add_file(@ssl_cacert)
150
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
115
151
  end
116
- @ssl_context.cert_store = @cert_store
117
- @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
118
152
  end
119
153
 
120
154
  @ssl_context.min_version = :TLS1_1 # not strictly required - JVM should have disabled TLSv1
@@ -127,6 +161,9 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
127
161
  disabled_protocols.map! { |v| OpenSSL::SSL.const_get "OP_NO_#{v.sub('.', '_')}" }
128
162
  @ssl_context.options = disabled_protocols.reduce(@ssl_context.options, :|)
129
163
  end
164
+
165
+ @ssl_context.ciphers = @ssl_cipher_suites if @ssl_cipher_suites&.any?
166
+
130
167
  @ssl_context
131
168
  end
132
169
  private :setup_ssl
@@ -137,13 +174,36 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
137
174
  end
138
175
  private :new_ssl_context
139
176
 
177
+ def new_ssl_certificate_store
178
+ OpenSSL::X509::Store.new
179
+ end
180
+ private :new_ssl_certificate_store
181
+
182
+ def load_cert_store
183
+ cert_store = new_ssl_certificate_store
184
+ cert_store.set_default_paths
185
+ @ssl_certificate_authorities&.each do |cert|
186
+ cert_store.add_file(cert)
187
+ end
188
+ cert_store
189
+ end
190
+ private :load_cert_store
191
+
192
+ def initialize(*args)
193
+ super(*args)
194
+ setup_ssl_params!
195
+ end
196
+
140
197
  # @overload Base#register
141
198
  def register
142
199
  require "socket"
143
200
  require "stud/try"
201
+
202
+ validate_ssl_config!
203
+
144
204
  @closed = Concurrent::AtomicBoolean.new(false)
145
205
  @thread_no = Concurrent::AtomicFixnum.new(0)
146
- setup_ssl if @ssl_enable
206
+ setup_ssl if @ssl_enabled
147
207
 
148
208
  if server?
149
209
  run_as_server
@@ -160,20 +220,40 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
160
220
  @logger.error("Could not start tcp server: Address in use", host: @host, port: @port)
161
221
  raise
162
222
  end
163
- if @ssl_enable
223
+ if @ssl_enabled
164
224
  @server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
165
- end # @ssl_enable
225
+ end # @ssl_enabled
166
226
  @client_threads = Concurrent::Array.new
167
227
 
168
228
  @accept_thread = Thread.new(@server_socket) do |server_socket|
169
229
  LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|server_accept")
170
230
  loop do
171
231
  break if @closed.value
172
- client_socket = server_socket.accept_nonblock exception: false
173
- if client_socket == :wait_readable
174
- IO.select [ server_socket ]
175
- next
232
+ # OpenSSL::SSL::SSLServer does not support the #accept_nonblock method.
233
+ # When SSL is enabled, it needs to use the blocking counterpart and ignore
234
+ # SSLError errors, as they may be client's issues such as missing client's
235
+ # certificates, ciphers, etc. If it's not rescued here, it would close the
236
+ # TCP server and exit the plugin.
237
+ # On the other hand, IOError should normally happen when the pipeline configuration
238
+ # is reloaded, as the stream gets closed in the thread
239
+ if @ssl_enabled
240
+ begin
241
+ client_socket = server_socket.accept
242
+ rescue OpenSSL::SSL::SSLError => e
243
+ log_warn("SSL Error", e)
244
+ retry unless @closed.value
245
+ rescue IOError => e
246
+ log_warn("IO Error", e)
247
+ retry unless @closed.value
248
+ end
249
+ else
250
+ client_socket = server_socket.accept_nonblock exception: false
251
+ if client_socket == :wait_readable
252
+ IO.select [ server_socket ]
253
+ next
254
+ end
176
255
  end
256
+
177
257
  Thread.start(client_socket) do |client_socket|
178
258
  # monkeypatch a 'peer' method onto the socket.
179
259
  client_socket.extend(::LogStash::Util::SocketPeer)
@@ -256,7 +336,7 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
256
336
  def connect
257
337
  begin
258
338
  client_socket = TCPSocket.new(@host, @port)
259
- if @ssl_enable
339
+ if @ssl_enabled
260
340
  client_socket = OpenSSL::SSL::SSLSocket.new(client_socket, @ssl_context)
261
341
  begin
262
342
  client_socket.connect
@@ -277,6 +357,95 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
277
357
  end
278
358
  end # def connect
279
359
 
360
+ def validate_ssl_config!
361
+ unless @ssl_enabled
362
+ ignored_ssl_settings = original_params.select { |k| k != 'ssl_enabled' && k != 'ssl_enable' && k.start_with?('ssl_') }
363
+ @logger.warn("Configured SSL settings are not used when `#{provided_ssl_enabled_config_name}` is set to `false`: #{ignored_ssl_settings.keys}") if ignored_ssl_settings.any?
364
+ return
365
+ end
366
+
367
+ if @ssl_certificate && !@ssl_key
368
+ raise LogStash::ConfigurationError, "Using an `ssl_certificate` requires an `ssl_key`"
369
+ elsif @ssl_key && !@ssl_certificate
370
+ raise LogStash::ConfigurationError, 'An `ssl_certificate` is required when using an `ssl_key`'
371
+ end
372
+
373
+ if server?
374
+ validate_server_ssl_config!
375
+ else
376
+ validate_client_ssl_config!
377
+ end
378
+ end
379
+
380
+ def validate_client_ssl_config!
381
+ if original_params.include?('ssl_client_authentication')
382
+ raise LogStash::ConfigurationError, "`ssl_client_authentication` must not be configured when mode is `client`, use `ssl_verification_mode` instead."
383
+ end
384
+ end
385
+
386
+ def validate_server_ssl_config!
387
+ if original_params.include?('ssl_verification_mode')
388
+ raise LogStash::ConfigurationError, "`ssl_verification_mode` must not be configured when mode is `server`, use `ssl_client_authentication` instead."
389
+ end
390
+
391
+ if @ssl_certificate.nil?
392
+ raise LogStash::ConfigurationError, "An `ssl_certificate` is required when `#{provided_ssl_enabled_config_name}` => true"
393
+ end
394
+
395
+ if requires_ssl_certificate_authorities? && (@ssl_certificate_authorities.nil? || @ssl_certificate_authorities.empty?)
396
+ raise LogStash::ConfigurationError, "An `ssl_certificate_authorities` is required when `ssl_client_authentication` => `#{@ssl_client_authentication}`"
397
+ end
398
+ end
399
+
400
+ def requires_ssl_certificate_authorities?
401
+ original_params.include?('ssl_client_authentication') && @ssl_client_authentication != 'none'
402
+ end
403
+
404
+ def provided_ssl_enabled_config_name
405
+ original_params.include?('ssl_enable') ? 'ssl_enable' : 'ssl_enabled'
406
+ end
407
+
408
+ def setup_ssl_params!
409
+ @ssl_enabled = normalize_config(:ssl_enabled) do |normalizer|
410
+ normalizer.with_deprecated_alias(:ssl_enable)
411
+ end
412
+
413
+ @ssl_certificate = normalize_config(:ssl_certificate) do |normalizer|
414
+ normalizer.with_deprecated_alias(:ssl_cert)
415
+ end
416
+
417
+ if server?
418
+ @ssl_client_authentication = normalize_config(:ssl_client_authentication) do |normalizer|
419
+ normalizer.with_deprecated_mapping(:ssl_verify) do |ssl_verify|
420
+ ssl_verify == true ? 'required' : 'none'
421
+ end
422
+ end
423
+ else
424
+ @ssl_verification_mode = normalize_config(:ssl_verification_mode) do |normalize|
425
+ normalize.with_deprecated_mapping(:ssl_verify) do |ssl_verify|
426
+ ssl_verify == true ? 'full' : 'none'
427
+ end
428
+ end
429
+
430
+ # Keep backwards compatibility with the default :ssl_verify value (false)
431
+ if !original_params.include?('ssl_verify') && !original_params.include?('ssl_verification_mode')
432
+ @ssl_verification_mode = 'none'
433
+ end
434
+ end
435
+
436
+ @ssl_certificate_authorities = normalize_config(:ssl_certificate_authorities) do |normalize|
437
+ normalize.with_deprecated_mapping(:ssl_cacert) do |ssl_cacert|
438
+ if File.directory?(ssl_cacert)
439
+ Dir.children(ssl_cacert)
440
+ .map{ |f| File.join(ssl_cacert, f) }
441
+ .reject{ |f| File.directory?(f) || File.basename(f).start_with?('.') }
442
+ else
443
+ [ssl_cacert]
444
+ end
445
+ end
446
+ end
447
+ end
448
+
280
449
  def server?
281
450
  @mode == "server"
282
451
  end # def server?
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-tcp'
4
- s.version = '6.1.2'
4
+ s.version = '6.2.1'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Writes events over a TCP socket"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
24
24
  s.add_runtime_dependency 'logstash-core', '>= 8.1.0'
25
25
  s.add_runtime_dependency 'logstash-codec-json'
26
26
  s.add_runtime_dependency 'stud'
27
+ s.add_runtime_dependency 'logstash-mixin-normalize_config_support', '~>1.0'
27
28
 
28
29
  s.add_runtime_dependency 'jruby-openssl', '>= 0.12.2' # 0.12 supports TLSv1.3
29
30
 
@@ -108,6 +108,20 @@ describe LogStash::Outputs::Tcp do
108
108
  end
109
109
  end
110
110
 
111
+ context "with cipher suites" do
112
+ let(:config) do
113
+ super().merge('ssl_cipher_suites' => %w[TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA])
114
+ end
115
+
116
+ it "limits the ciphers selection" do
117
+ ssl_context = OpenSSL::SSL::SSLContext.new
118
+ allow(subject).to receive(:new_ssl_context).and_return(ssl_context)
119
+ subject.send :setup_ssl
120
+ expect(ssl_context.ciphers.length).to eq(2)
121
+ expect(ssl_context.ciphers).to satisfy { |arr| arr[0].include?('AES128-GCM-SHA256') && arr[1].include?('EDH-RSA-DES-CBC3-SHA') }
122
+ end
123
+ end
124
+
111
125
  context "client mode" do
112
126
  before { subject.register }
113
127
 
@@ -136,7 +150,7 @@ describe LogStash::Outputs::Tcp do
136
150
  end
137
151
 
138
152
  context "when enabling SSL" do
139
- let(:config) { super().merge("ssl_enable" => true, 'codec' => 'plain') }
153
+ let(:config) { super().merge("ssl_enabled" => true, 'codec' => 'plain') }
140
154
  context "and not providing a certificate/key pair" do
141
155
  it "registers without error" do
142
156
  expect { subject.register }.to_not raise_error
@@ -145,13 +159,18 @@ describe LogStash::Outputs::Tcp do
145
159
 
146
160
  context "and providing a certificate/key pair" do
147
161
  let(:cert_key_pair) { Flores::PKI.generate }
148
- let(:certificate) { cert_key_pair.first }
149
- let(:cert_file) do
150
- path = Tempfile.new('foo').path
151
- IO.write(path, certificate.to_s)
162
+ let(:certificate) do
163
+ path = Tempfile.new('certificate').path
164
+ IO.write(path, cert_key_pair.first.to_s)
152
165
  path
153
166
  end
154
- let(:config) { super().merge("ssl_cert" => cert_file) }
167
+ let(:key) do
168
+ path = Tempfile.new('key').path
169
+ IO.write(path, cert_key_pair[1].to_s)
170
+ path
171
+ end
172
+ let(:config) { super().merge("ssl_certificate" => certificate, "ssl_key" => key) }
173
+
155
174
  it "registers without error" do
156
175
  expect { subject.register }.to_not raise_error
157
176
  end
@@ -162,7 +181,7 @@ describe LogStash::Outputs::Tcp do
162
181
  context "ES generated plain-text certificate/key" do
163
182
  let(:key_file) { File.join(FIXTURES_PATH, 'plaintext/instance.key') }
164
183
  let(:crt_file) { File.join(FIXTURES_PATH, 'plaintext/instance.crt') }
165
- let(:config) { super().merge("ssl_cert" => crt_file, "ssl_key" => key_file) }
184
+ let(:config) { super().merge("ssl_certificate" => crt_file, "ssl_key" => key_file) }
166
185
 
167
186
  it "registers without error" do
168
187
  expect { subject.register }.to_not raise_error
@@ -238,7 +257,7 @@ describe LogStash::Outputs::Tcp do
238
257
  context "encrypted key using PKCS#1" do
239
258
  let(:key_file) { File.join(FIXTURES_PATH, 'encrypted/instance.key') }
240
259
  let(:crt_file) { File.join(FIXTURES_PATH, 'encrypted/instance.crt') }
241
- let(:config) { super().merge("ssl_cert" => crt_file, "ssl_key" => key_file) }
260
+ let(:config) { super().merge("ssl_certificate" => crt_file, "ssl_key" => key_file) }
242
261
 
243
262
  it "registers with error (due missing password)" do
244
263
  expect { subject.register }.to raise_error(OpenSSL::PKey::RSAError) # TODO need a better error
@@ -258,7 +277,7 @@ describe LogStash::Outputs::Tcp do
258
277
  context "and protocol is TLSv1.3" do
259
278
  let(:key_file) { File.join(FIXTURES_PATH, 'plaintext/instance.key') }
260
279
  let(:crt_file) { File.join(FIXTURES_PATH, 'plaintext/instance.crt') }
261
- let(:config) { super().merge("ssl_cert" => crt_file, "ssl_key" => key_file) }
280
+ let(:config) { super().merge("ssl_certificate" => crt_file, "ssl_key" => key_file) }
262
281
 
263
282
  let(:secure_server) do
264
283
  ssl_context = OpenSSL::SSL::SSLContext.new
@@ -307,5 +326,171 @@ describe LogStash::Outputs::Tcp do
307
326
  expect(buffer).to eq(message * 2)
308
327
  end
309
328
  end
329
+
330
+ context "with only ssl_certificate set" do
331
+ let(:config) { super().merge("ssl_certificate" => File.join(FIXTURES_PATH, 'plaintext/instance.crt')) }
332
+
333
+ it "should raise a configuration error to request also `ssl_key`" do
334
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError, /Using an `ssl_certificate` requires an `ssl_key`/)
335
+ end
336
+ end
337
+
338
+ context "with only ssl_key set" do
339
+ let(:config) { super().merge("ssl_key" => File.join(FIXTURES_PATH, 'plaintext/instance.key')) }
340
+
341
+ it "should raise a configuration error to request also `ssl_key`" do
342
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError, /An `ssl_certificate` is required when using an `ssl_key`/)
343
+ end
344
+ end
345
+
346
+ context "and mode is server" do
347
+ let(:config) do
348
+ {
349
+ "host" => "127.0.0.1",
350
+ "port" => port,
351
+ "mode" => 'server',
352
+ "ssl_enabled" => true,
353
+ "ssl_certificate" => File.join(FIXTURES_PATH, 'plaintext/instance.crt'),
354
+ "ssl_key" => File.join(FIXTURES_PATH, 'plaintext/instance.key'),
355
+ }
356
+ end
357
+
358
+ context "with no ssl_certificate" do
359
+ let(:config) { super().reject { |k| "ssl_key".eql?(k) || "ssl_certificate".eql?(k) } }
360
+
361
+ it "should raise a configuration error" do
362
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError, /An `ssl_certificate` is required when `ssl_enabled` => true/)
363
+ end
364
+ end
365
+
366
+ context "with ssl_client_authentication = `none` and no ssl_certificate_authorities" do
367
+ let(:config) { super().merge(
368
+ 'ssl_client_authentication' => 'none',
369
+ 'ssl_certificate_authorities' => []
370
+ ) }
371
+
372
+ it "should register without errors" do
373
+ expect { subject.register }.to_not raise_error
374
+ end
375
+ end
376
+
377
+ context "with deprecated ssl_verify = true and no ssl_certificate_authorities" do
378
+ let(:config) { super().merge(
379
+ 'ssl_verify' => true,
380
+ 'ssl_certificate_authorities' => []
381
+ ) }
382
+
383
+ it "should register without errors" do
384
+ expect { subject.register }.to_not raise_error
385
+ end
386
+ end
387
+
388
+ %w[required optional].each do |ssl_client_authentication|
389
+ context "with ssl_client_authentication = `#{ssl_client_authentication}` and no ssl_certificate_authorities" do
390
+ let(:config) { super().merge(
391
+ 'ssl_client_authentication' => ssl_client_authentication,
392
+ 'ssl_certificate_authorities' => []
393
+ ) }
394
+
395
+ it "should raise a configuration error" do
396
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError, /An `ssl_certificate_authorities` is required when `ssl_client_authentication` => `#{ssl_client_authentication}`/)
397
+ end
398
+ end
399
+ end
400
+
401
+ context "with ssl_verification_mode" do
402
+ let(:config) do
403
+ super().merge 'ssl_verification_mode' => 'full'
404
+ end
405
+
406
+ it "should raise a configuration error" do
407
+ expect{subject.register}.to raise_error(LogStash::ConfigurationError, /`ssl_verification_mode` must not be configured when mode is `server`, use `ssl_client_authentication` instead/)
408
+ end
409
+ end
410
+ end
411
+
412
+ context "with deprecated settings" do
413
+ let(:ssl_verify) { true }
414
+ let(:certificate_path) { File.join(FIXTURES_PATH, 'plaintext/instance.crt') }
415
+ let(:config) do
416
+ {
417
+ "host" => "127.0.0.1",
418
+ "port" => port,
419
+ "ssl_enable" => true,
420
+ "ssl_cert" => certificate_path,
421
+ "ssl_key" => File.join(FIXTURES_PATH, 'plaintext/instance.key'),
422
+ "ssl_verify" => ssl_verify
423
+ }
424
+ end
425
+
426
+ context "and mode is server" do
427
+ let(:config) { super().merge("mode" => 'server') }
428
+ [true, false].each do |verify|
429
+ context "and ssl_verify is #{verify}" do
430
+ let(:ssl_verify) { verify }
431
+
432
+ it "should set new configs variables" do
433
+ subject.register
434
+ expect(subject.instance_variable_get(:@ssl_enabled)).to eql(true)
435
+ expect(subject.instance_variable_get(:@ssl_client_authentication)).to eql(verify ? 'required' : 'none')
436
+ expect(subject.instance_variable_get(:@ssl_certificate)).to eql(certificate_path)
437
+ end
438
+ end
439
+ end
440
+ end
441
+
442
+ context "and mode is client" do
443
+ let(:config) { super().merge("mode" => 'client') }
444
+ [true, false].each do |verify|
445
+ context "and ssl_verify is #{verify}" do
446
+ let(:ssl_verify) { verify }
447
+
448
+ it "should set new configs variables" do
449
+ subject.register
450
+ expect(subject.instance_variable_get(:@ssl_enabled)).to eql(true)
451
+ expect(subject.instance_variable_get(:@ssl_verification_mode)).to eql(verify ? 'full' : 'none')
452
+ expect(subject.instance_variable_get(:@ssl_certificate)).to eql(certificate_path)
453
+ end
454
+ end
455
+ end
456
+ end
457
+ end
458
+
459
+ context "with ssl_client_authentication" do
460
+ let(:config) do
461
+ super().merge 'ssl_client_authentication' => 'required'
462
+ end
463
+
464
+ it "should raise a configuration error" do
465
+ expect{subject.register}.to raise_error(LogStash::ConfigurationError, /`ssl_client_authentication` must not be configured when mode is `client`, use `ssl_verification_mode` instead/)
466
+ end
467
+ end
468
+
469
+ context "with ssl_certificate_authorities" do
470
+ let(:certificate_path) { File.join(FIXTURES_PATH, 'plaintext/instance.crt') }
471
+ let(:config) do
472
+ super().merge('ssl_certificate_authorities' => [certificate_path])
473
+ end
474
+
475
+ it "sets cert_store values" do
476
+ ssl_store = double(OpenSSL::X509::Store.new)
477
+ allow(ssl_store).to receive(:set_default_paths)
478
+ allow(ssl_store).to receive(:add_file)
479
+ allow(subject).to receive(:new_ssl_certificate_store).and_return(ssl_store)
480
+ subject.send :setup_ssl
481
+ expect(ssl_store).to have_received(:add_file).with(certificate_path)
482
+ end
483
+ end
484
+
485
+ context "CAs certificates" do
486
+ it "includes openssl default paths" do
487
+ ssl_store = double(OpenSSL::X509::Store.new)
488
+ allow(ssl_store).to receive(:set_default_paths)
489
+ allow(subject).to receive(:new_ssl_certificate_store).and_return(ssl_store)
490
+ subject.send :setup_ssl
491
+ expect(ssl_store).to have_received(:set_default_paths)
492
+ end
493
+ end
494
+
310
495
  end
311
496
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-tcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.2
4
+ version: 6.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-23 00:00:00.000000000 Z
11
+ date: 2024-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '1.0'
81
+ name: logstash-mixin-normalize_config_support
82
+ prerelease: false
83
+ type: :runtime
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.0'
75
89
  - !ruby/object:Gem::Dependency
76
90
  requirement: !ruby/object:Gem::Requirement
77
91
  requirements: