logstash-output-tcp 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ded80f3a5c5c29bbdd311093b9c72a829f328cee904156ae5110d3a91b1902e4
4
- data.tar.gz: adb70823265a2cdf190608f999860ea47d79482073e09ffc551756ce887aec9a
3
+ metadata.gz: d30cf25cfbc7e1cc2e72d5e1eb8ab8179d42b31413a549801a012d5e1be7e640
4
+ data.tar.gz: 53632382865d4bade1e6d56e9872b1b5db690fd9b866cb4d0952dfa8d1156cee
5
5
  SHA512:
6
- metadata.gz: 257e5b2bbfd74a4df83a615de141bfa5e1dd14d395461d672bb872804f7454b496481307f175e8d4b677fb9b424682a560f3ca7ceee981282a9ce88b36de7cf9
7
- data.tar.gz: a94a4e1664272fa51c13c9d73deacfe6a0438adda36f890e2a0d2ef5ae1e0349c9f79fda178ebdc2669cd23def15e904269969bec485e260ccecceb72aced69f
6
+ metadata.gz: e48f3511c25df04edb79dfd0425c6a84d18808bce5f8d4910cabe024955e6c16f8b856a8ac7fac01cb7721e7628934101e14e7180497a7c6cc1096169290963e
7
+ data.tar.gz: 94758e5adb6be529f4c70715aa0796d1a15bd8d17e91d35c63fdc1b29eccea58c0f36e4c29b0cf911a3691fade34c895922ca9b0384cd3df405f723a97ff642f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 6.1.0
2
+ - Feat: ssl_supported_protocols (TLSv1.3) [#47](https://github.com/logstash-plugins/logstash-output-tcp/pull/47)
3
+ - Fix: close server and client sockets on plugin close
4
+
5
+ ## 6.0.2
6
+ - Fix: unable to start with password protected key [#45](https://github.com/logstash-plugins/logstash-output-tcp/pull/45)
7
+
8
+ ## 6.0.1
9
+ - Fixed logging fail retry to stdout [#43](https://github.com/logstash-plugins/logstash-output-tcp/pull/43)
10
+ - Fixed to use `reconnect_interval` when establish a connection
11
+
1
12
  ## 6.0.0
2
13
  - Removed obsolete field `message_format`
3
14
 
data/LICENSE CHANGED
@@ -1,13 +1,202 @@
1
- Copyright (c) 2012-2018 Elasticsearch <http://www.elastic.co>
2
1
 
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
6
5
 
7
- http://www.apache.org/licenses/LICENSE-2.0
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
7
 
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Logstash Plugin
2
2
 
3
- [![Travis Build Status](https://travis-ci.org/logstash-plugins/logstash-output-tcp.svg)](https://travis-ci.org/logstash-plugins/logstash-output-tcp)
3
+ [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-output-tcp.svg)](https://travis-ci.com/logstash-plugins/logstash-output-tcp)
4
4
 
5
5
  This is a plugin for [Logstash](https://github.com/elastic/logstash).
6
6
 
data/docs/index.asciidoc CHANGED
@@ -45,6 +45,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
45
45
  | <<plugins-{type}s-{plugin}-ssl_enable>> |<<boolean,boolean>>|No
46
46
  | <<plugins-{type}s-{plugin}-ssl_key>> |a valid filesystem path|No
47
47
  | <<plugins-{type}s-{plugin}-ssl_key_passphrase>> |<<password,password>>|No
48
+ | <<plugins-{type}s-{plugin}-ssl_supported_protocols>> |<<string,string>>|No
48
49
  | <<plugins-{type}s-{plugin}-ssl_verify>> |<<boolean,boolean>>|No
49
50
  |=======================================================================
50
51
 
@@ -130,6 +131,20 @@ SSL key path
130
131
 
131
132
  SSL key passphrase
132
133
 
134
+ [id="plugins-{type}s-{plugin}-ssl_supported_protocols"]
135
+ ===== `ssl_supported_protocols`
136
+
137
+ * Value type is <<string,string>>
138
+ * Allowed values are: `'TLSv1.1'`, `'TLSv1.2'`, `'TLSv1.3'`
139
+ * Default depends on the JDK being used. With up-to-date Logstash, the default is `['TLSv1.2', 'TLSv1.3']`.
140
+ `'TLSv1.1'` is not considered secure and is only provided for legacy applications.
141
+
142
+ List of allowed SSL/TLS versions to use when establishing a secure connection.
143
+
144
+ NOTE: If you configure the plugin to use `'TLSv1.1'` on any recent JVM, such as the one packaged with Logstash,
145
+ the protocol is disabled by default and needs to be enabled manually by changing `jdk.tls.disabledAlgorithms` in
146
+ the *$JDK_HOME/conf/security/java.security* configuration file. That is, `TLSv1.1` needs to be removed from the list.
147
+
133
148
  [id="plugins-{type}s-{plugin}-ssl_verify"]
134
149
  ===== `ssl_verify`
135
150
 
@@ -51,42 +51,50 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
51
51
  # SSL key passphrase
52
52
  config :ssl_key_passphrase, :validate => :password, :default => nil
53
53
 
54
+ # NOTE: the default setting [] uses SSL engine defaults
55
+ config :ssl_supported_protocols, :validate => ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], :default => [], :list => true
56
+
54
57
  class Client
55
- public
58
+
56
59
  def initialize(socket, logger)
57
60
  @socket = socket
58
61
  @logger = logger
59
62
  @queue = Queue.new
60
63
  end
61
64
 
62
- public
63
65
  def run
64
66
  loop do
65
67
  begin
66
68
  @socket.write(@queue.pop)
67
69
  rescue => e
68
- @logger.warn("tcp output exception", :socket => @socket,
69
- :exception => e)
70
+ log_warn 'socket write failed:', e, socket: (@socket ? @socket.to_s : nil)
70
71
  break
71
72
  end
72
73
  end
73
74
  end # def run
74
75
 
75
- public
76
76
  def write(msg)
77
77
  @queue.push(msg)
78
78
  end # def write
79
+
80
+ def close
81
+ @socket.close
82
+ rescue => e
83
+ log_warn 'socket close failed:', e, socket: (@socket ? @socket.to_s : nil)
84
+ end
79
85
  end # class Client
80
86
 
81
- private
82
87
  def setup_ssl
83
88
  require "openssl"
84
89
 
85
- @ssl_context = OpenSSL::SSL::SSLContext.new
90
+ @ssl_context = new_ssl_context
86
91
  if @ssl_cert
87
92
  @ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_cert))
88
93
  if @ssl_key
89
- @ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@ssl_key),@ssl_key_passphrase)
94
+ # if we have an encrypted key and a password is not provided (nil) than OpenSSL::PKey::RSA
95
+ # prompts the user to enter a password interactively - we do not want to do that,
96
+ # for plain-text keys the default '' password argument gets simply ignored
97
+ @ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@ssl_key), @ssl_key_passphrase.value || '')
90
98
  end
91
99
  end
92
100
  if @ssl_verify
@@ -101,50 +109,74 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
101
109
  @ssl_context.cert_store = @cert_store
102
110
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
103
111
  end
104
- end # def setup_ssl
105
112
 
106
- public
113
+ @ssl_context.min_version = :TLS1_1 # not strictly required - JVM should have disabled TLSv1
114
+ if ssl_supported_protocols.any?
115
+ disabled_protocols = ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'] - ssl_supported_protocols
116
+ unless OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3 # work-around JRuby-OpenSSL bug - missing constant
117
+ @ssl_context.max_version = :TLS1_2 if disabled_protocols.delete('TLSv1.3')
118
+ end
119
+ # mapping 'TLSv1.2' -> OpenSSL::SSL::OP_NO_TLSv1_2
120
+ disabled_protocols.map! { |v| OpenSSL::SSL.const_get "OP_NO_#{v.sub('.', '_')}" }
121
+ @ssl_context.options = disabled_protocols.reduce(@ssl_context.options, :|)
122
+ end
123
+ @ssl_context
124
+ end
125
+ private :setup_ssl
126
+
127
+ # @note to be able to hook up into #ssl_context from tests
128
+ def new_ssl_context
129
+ OpenSSL::SSL::SSLContext.new
130
+ end
131
+ private :new_ssl_context
132
+
133
+ # @overload Base#register
107
134
  def register
108
135
  require "socket"
109
136
  require "stud/try"
110
- if @ssl_enable
111
- setup_ssl
112
- end # @ssl_enable
137
+ @closed = Concurrent::AtomicBoolean.new(false)
138
+ setup_ssl if @ssl_enable
113
139
 
114
140
  if server?
115
141
  @logger.info("Starting tcp output listener", :address => "#{@host}:#{@port}")
116
142
  begin
117
143
  @server_socket = TCPServer.new(@host, @port)
118
144
  rescue Errno::EADDRINUSE
119
- @logger.error("Could not start TCP server: Address in use",
120
- :host => @host, :port => @port)
145
+ @logger.error("Could not start tcp server: Address in use", host: @host, port: @port)
121
146
  raise
122
147
  end
123
148
  if @ssl_enable
124
149
  @server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
125
150
  end # @ssl_enable
126
- @client_threads = []
151
+ @client_threads = Concurrent::Array.new
127
152
 
128
153
  @accept_thread = Thread.new(@server_socket) do |server_socket|
154
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|server_accept")
129
155
  loop do
130
- Thread.start(server_socket.accept) do |client_socket|
156
+ break if @closed.value
157
+ client_socket = server_socket.accept_nonblock exception: false
158
+ if client_socket == :wait_readable
159
+ IO.select [ server_socket ]
160
+ next
161
+ end
162
+ Thread.start(client_socket) do |client_socket|
131
163
  # monkeypatch a 'peer' method onto the socket.
132
164
  client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
133
- @logger.debug("Accepted connection", :client => client_socket.peer,
134
- :server => "#{@host}:#{@port}")
165
+ @logger.debug("accepted connection", client: client_socket.peer, server: "#{@host}:#{@port}")
135
166
  client = Client.new(client_socket, @logger)
136
167
  Thread.current[:client] = client
168
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|client_socket-#{@client_threads.size}")
137
169
  @client_threads << Thread.current
138
- client.run
170
+ client.run unless @closed.value
139
171
  end
140
172
  end
141
173
  end
142
174
 
143
175
  @codec.on_event do |event, payload|
176
+ @client_threads.select!(&:alive?)
144
177
  @client_threads.each do |client_thread|
145
178
  client_thread[:client].write(payload)
146
179
  end
147
- @client_threads.reject! {|t| !t.alive? }
148
180
  end
149
181
  else
150
182
  client_socket = nil
@@ -160,8 +192,7 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
160
192
  # Now send the payload
161
193
  client_socket.syswrite(payload) if w.any?
162
194
  rescue => e
163
- @logger.warn("tcp output exception", :host => @host, :port => @port,
164
- :exception => e, :backtrace => e.backtrace)
195
+ log_warn "client socket failed:", e, host: @host, port: @port, socket: (client_socket ? client_socket.to_s : nil)
165
196
  client_socket.close rescue nil
166
197
  client_socket = nil
167
198
  sleep @reconnect_interval
@@ -169,37 +200,69 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
169
200
  end
170
201
  end
171
202
  end
172
- end # def register
203
+ end
204
+
205
+ # @overload Base#receive
206
+ def receive(event)
207
+ @codec.encode(event)
208
+ end
209
+
210
+ # @overload Base#close
211
+ def close
212
+ @closed.make_true
213
+ @server_socket.close rescue nil if @server_socket
214
+
215
+ return unless @client_threads
216
+ @client_threads.each do |thread|
217
+ client = thread[:client]
218
+ client.close rescue nil if client
219
+ end
220
+ end
173
221
 
174
222
  private
223
+
175
224
  def connect
176
- Stud::try do
225
+ begin
177
226
  client_socket = TCPSocket.new(@host, @port)
178
227
  if @ssl_enable
179
228
  client_socket = OpenSSL::SSL::SSLSocket.new(client_socket, @ssl_context)
180
229
  begin
181
230
  client_socket.connect
182
231
  rescue OpenSSL::SSL::SSLError => ssle
183
- @logger.error("SSL Error", :exception => ssle,
184
- :backtrace => ssle.backtrace)
232
+ log_error 'connect ssl failure:', ssle, backtrace: false
185
233
  # NOTE(mrichar1): Hack to prevent hammering peer
186
234
  sleep(5)
187
235
  raise
188
236
  end
189
237
  end
190
238
  client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
191
- @logger.debug("Opened connection", :client => "#{client_socket.peer}")
239
+ @logger.debug("opened connection", :client => client_socket.peer)
192
240
  return client_socket
241
+ rescue => e
242
+ log_error 'failed to connect:', e
243
+ sleep @reconnect_interval
244
+ retry
193
245
  end
194
246
  end # def connect
195
247
 
196
- private
197
248
  def server?
198
249
  @mode == "server"
199
250
  end # def server?
200
251
 
201
- public
202
- def receive(event)
203
- @codec.encode(event)
204
- end # def receive
252
+ def pipeline_id
253
+ execution_context.pipeline_id || 'main'
254
+ end
255
+
256
+ def log_warn(msg, e, backtrace: @logger.debug?, **details)
257
+ details = details.merge message: e.message, exception: e.class
258
+ details[:backtrace] = e.backtrace if backtrace
259
+ @logger.warn(msg, details)
260
+ end
261
+
262
+ def log_error(msg, e, backtrace: @logger.info?, **details)
263
+ details = details.merge message: e.message, exception: e.class
264
+ details[:backtrace] = e.backtrace if backtrace
265
+ @logger.error(msg, details)
266
+ end
267
+
205
268
  end # class LogStash::Outputs::Tcp
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-tcp'
4
- s.version = '6.0.0'
4
+ s.version = '6.1.0'
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"
@@ -21,11 +21,14 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
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
27
 
28
+ s.add_runtime_dependency 'jruby-openssl', '>= 0.12.2' # 0.12 supports TLSv1.3
29
+
28
30
  s.add_development_dependency 'logstash-devutils'
31
+ s.add_development_dependency 'logstash-codec-plain'
29
32
  s.add_development_dependency 'flores'
30
33
  end
31
34
 
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDIjCCAgqgAwIBAgIUTCjyocBgCYSWU/GYI58F0IBXLHswDQYJKoZIhvcNAQEL
3
+ BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
4
+ cmF0ZWQgQ0EwHhcNMjIwMzIxMDc1NTU2WhcNMjUwMzIwMDc1NTU2WjATMREwDwYD
5
+ VQQDEwhpbnN0YW5jZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKT
6
+ f0FF/R9WBC7lwN0IJdOn+jPJJ+4M3LY/sfFuTaAUgPSaiRLLzCjox1p1Xy9z+xyk
7
+ C2Dkhb4FdOZBNqS/w0Z7lkrha8VZg2UKW4+i1lq4ANPl9sP9YCbh1R+SRdpHnZX7
8
+ APG798+0+NC4GmEeOIFfWsEE4MiFXAPunhAlAbxMl6e5htX4cgYWMbH67Ur1/uQB
9
+ 26Y6PBahKP7dbxaL/ugPE6MDmmA+cNRWMa21Ay2h94IesRXwNRFn1gQ1SqKU0hBJ
10
+ KX7u0IoYyS6nbDlF6E+npLLWzDMCHsykrnadWV5kDSNv1t7Wvk0noNStrryOXgtc
11
+ 7iCCOX1oqsP/yDNl9OECAwEAAaNNMEswHQYDVR0OBBYEFBtzuCJPEhHW3ZD1bReq
12
+ S6K3P09OMB8GA1UdIwQYMBaAFJcC1Mw29qzRV0dPCCc6GyB8LXx3MAkGA1UdEwQC
13
+ MAAwDQYJKoZIhvcNAQELBQADggEBAHvXsfMgY+PTBCnW3A6QCGZ3jTSRqqXRFYB8
14
+ b/unfhD4iUFlgO4a/UCL2MTsFYXkzvlzwQyH6ll9/rTslBvJLiqi3+IZMfGNoaNI
15
+ LhTLy0NF9scPju7Q6MxcPAceI4gmxKyBWJxm4XYJ1VwmPKJNzKqFEwfPWMakxUaT
16
+ wiuhVodqZweWi7S2keYiduBLEbqWBJPKAnb8e3PIILBqL0nfhmVw0NSAD19d9oLR
17
+ 89AaY8zQ4YL4Txs4FSrK0VQl3L6NAtAaq9JvXqzXD/E0EO1YL8ykM3Zash/GG9iN
18
+ B9Momegqn8/q7pfoWdkE2oL9KUKispUT9Fq3mY6KHZL1T4SbxL8=
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,30 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: AES-128-CBC,635374A0A566A0189BE4366E77881457
4
+
5
+ LbCmLGC8BAWKYUt+fYIIxUC5rh3D7CFN8LXjjGIYMVRs2seMqhCNJjtdLQ6poin5
6
+ H6W/c5+VXzzsgczFgZnQecoqWgqg4FoTZSacVVx/waekkdua9zjPReastV+7AcBG
7
+ MJJi8lYEIv+qacqxEzDJSis7XO0YXp2OBqUtfcaT5PunJ1bj5SQnqf7k1hb193bZ
8
+ C03MhZ6HdblY6IKvslqV5VWU38gPN0F6JN5/7vLgZyExYjTA1wkzdJXCzKAEwlfU
9
+ vF5/AKDGFpQAYMTnPwszpeUj+wyuAwaxt8uNkVFrfVIf+eaPqX1OStEAIiRNZerl
10
+ dTsKxLdYBBJz6G9wZLz+bWNpFsvS9gG1LlvMsycnwrfmwWTidmS8nDdLVfja2/XH
11
+ LXvwSnI+NHtnQ/R+duMZpJrqQljBZAZsyzW8Eyjaqi1ybwi50J/pwsX/J0jVQly/
12
+ Gr8A/SDfr1qJ82SQUfi0ZGoKVXaSJ31pPKBy9LpDGFZS7rFJsL/Aysd2lhyF/Tj1
13
+ kG60MAxFjfoRsNhBWj7pm/13hVlidwNrmhbzrnsFPuWDVWFYq71oljJ+YLlbljLB
14
+ L6rnQXr2iy+y3W03lryAsxtCrcJ6Sa+9EUNp55loGhK3EXCXnhNr3B1SfxGCXbAv
15
+ GI2h1RRbn/Pnht17pkiXsajE8vESWLqzZj6gtmYmMHQ+GY5l8SHTTMhV2DpYZFfE
16
+ nnUxyPOlXnm2uegQRf/ee8kKeUQZl9Bs2ZyniE0uzRGZqhQLlJCwYhOmEYFsv5kk
17
+ vD1Go4N2NqbMOipCFHbnm3IvG1rOBYtqHzHLwycsJw+2f1Dyu5T9GlEcJL7GeCWO
18
+ muY4Gjx7p84N1wwJ/IN4BRjkMD0qAXbKg9cnh/nDSaFgMez6jiaR+lJGV/srfpwB
19
+ 6bDOpRN/RarjRcQGVEfkP/UqJVWU8ZwnNqar5202WGSXI9HUYTQapVCh2NvBdYOS
20
+ VV+Ah2qE48s3qI95h80Ix0G7Or7ALJbSn6d190frUnXOUOye4+4MB3w+G0kGTQq8
21
+ w+pjqrSAGLA3yobrPki374X3ZMdYbdggDHn3pl/NvQVHZ4FRmYXyUVZfckV/7w0C
22
+ t1wBqh83ZM3XusivAy0/g6/124Xe2jkl6B0aNlAtxj3VTMBJNjLRIpoX5hR6TZq8
23
+ cHRvWaIOif8oOn6Q6PIGtpsAjejZ0jUPuvPHUwuYSYFUk/WH09k8tXD/ZZnoKZWV
24
+ xqrx3cBCKjoBu9HnPsPoQ6sedaM/L/i3osW7GBSMt2GApOqDoKQ70Ya6TSmbnDYv
25
+ roYy/R5vDqzTGK0gUyLNNCNhpuzEXjjaCwXegJ/GYl3mwcB8o5y1insyqfc0JPy1
26
+ oF0tYxw62Ovx9Udg9Ib94q0BQDhi5L55QZYKQAAz4+CW71aG30ETw32EtnUMnB4f
27
+ 2cswibLj7JhER/4pjOoKHF47wLJtlWlaceFvGWzno8uSY74qaRQHslwNHwx61I2q
28
+ rhsbD1wsvyEkxlIdSIlyUf8yj5LXWNMoixFppqh4VY4OAINxkNBEaJRzU6aMKn1n
29
+ Btvg2OePOvebCvJRvWousIgB2brZ5ioU336Tjb0uDeHIPF51+o78wtaS+jK084Bu
30
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDIjCCAgqgAwIBAgIURM555Ac4/c8UOP5fB12+1bo+b7owDQYJKoZIhvcNAQEL
3
+ BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l
4
+ cmF0ZWQgQ0EwHhcNMjIwMzIxMDc1NTA4WhcNMjUwMzIwMDc1NTA4WjATMREwDwYD
5
+ VQQDEwhpbnN0YW5jZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI0s
6
+ sYPGWeca0YMIVATxnEA/+WFL/eWWcbaX5JeaJA+g6WxMs2B6FEMfaSxauGrHZJWt
7
+ YNP1s0pptzJEPAZyx3Tkv8oNtSai3rpOocY3ebE/SWXlQd7xHWvy+w5ls1LRHINU
8
+ pEnMZhjdHqXFjIipmsAXihzU/BrHkWGfddco88YuaMgtWbLIqQr/oHyd2F6KgQby
9
+ HPSD35hCYvLQiHEY7vIntA6A9mYSdbZg0ZvI+QGEvZmhYXZjKOy7Vfg8p1zmfbIn
10
+ XdddCOkBp5gXDu8aRxigaQAi3Y+ESnVPxDr62+VEzCdmd/PCW8blHfvE9bTgqjYm
11
+ gH01PAoqQewkuxclufECAwEAAaNNMEswHQYDVR0OBBYEFNIW9layQi7V2kbYjwIu
12
+ NWT0QXOdMB8GA1UdIwQYMBaAFEyJvLXizLXiGu8sBnIcIS001ArxMAkGA1UdEwQC
13
+ MAAwDQYJKoZIhvcNAQELBQADggEBAAOnGIKiJxEc684Y6w1WpRftiD8VnkzE+A8n
14
+ uVXJibZzEKqxMWZ83A4lTugG+vKpOj1+8hcYUdXhIo2Qt92ArIOr2S7awdkMeoRq
15
+ eIKPElC9/8mm3572kdlqBuiT12bA2oD4zrI2hS0HV21DMLAxoorzLqn0TjMnnpm3
16
+ J1KLJzzMSRtiUtwiLXG4tx2ThXfTwcJmsRMQqZW/mFajEVs0jafUAeBH7hZoZX8o
17
+ 7e0O3TCQ5JWW3/xeGK/JnAZ1IyA6jQU/J5LbGgPVu0FBGmVn3PhtwYASrdXWzK4s
18
+ ncofcBHflmi8dkiKHVWNnOTdZdzkApYo59/eq4iDESR8YNgOCKA=
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpAIBAAKCAQEAjSyxg8ZZ5xrRgwhUBPGcQD/5YUv95ZZxtpfkl5okD6DpbEyz
3
+ YHoUQx9pLFq4asdkla1g0/WzSmm3MkQ8BnLHdOS/yg21JqLeuk6hxjd5sT9JZeVB
4
+ 3vEda/L7DmWzUtEcg1SkScxmGN0epcWMiKmawBeKHNT8GseRYZ911yjzxi5oyC1Z
5
+ ssipCv+gfJ3YXoqBBvIc9IPfmEJi8tCIcRju8ie0DoD2ZhJ1tmDRm8j5AYS9maFh
6
+ dmMo7LtV+DynXOZ9sidd110I6QGnmBcO7xpHGKBpACLdj4RKdU/EOvrb5UTMJ2Z3
7
+ 88JbxuUd+8T1tOCqNiaAfTU8CipB7CS7FyW58QIDAQABAoIBAD10dD4J7X72JLgm
8
+ uvR//OXXM4cQXpFAAXZb/s2j8wi+on5bkUZxPjrOBKmjQF5zOC0UEW+TqJ2/EVmX
9
+ bI3eD0eqgHbDqtUL12tA6Zlw8s+e3iO2Pgt/6K/iUTm+OebWUtQ012Osz9EJCNte
10
+ +MNRGaV/WccdTDWYJIhbsx+bmyrsxxW49rwvgxI26tpJYp0QEmwEiDqfc8kRFWgy
11
+ xVSt8Rjwk418YdsYUIGrO6RsGkwsCOTJWUJpBOzLBKwj4do5l5fC2g1/MSlj7Odj
12
+ 0HH/riBmzBj/YcyMlkhfGU0Zj/OlDnz8FP5HhfpYsGxvrjOYJ49Cl4cu8r47wnFP
13
+ nx4RMmUCgYEA4U9hOtp8oJ+Ztio/1I6bAb/hKLJ3LNeV46697/lTy6tSVmYJ1DGJ
14
+ YU3P5wmOpjZC2xs22VyX/vgC1v5RT0Jg8CGgXukmMV1gQQch6gAQci9+ZJ4LRz9U
15
+ ++dXyKuUbqncx9ezFwddBr+jQuu6Wn2AHljEp7wZGaijxD2lsp4B8vcCgYEAoGd8
16
+ w+hre2pGy5PKJPSoPp+9tNVJasXe3DM6ZYEZkd+96gStA+8UlSYe9hP+Taik7oUJ
17
+ SYzELfyU8s+9XWozaa1T5PeyY8jIbEnUHWy8Uag4dHY7ooR+7b3iAD4RLfx3o2GP
18
+ vJwP3i2FwklDbbyugVXZBfkSf2NJ4QidYIWzGFcCgYAD4MHjqW8LtLOIlyGSHwI7
19
+ /Xl6ode7RdqmmJNcVgZDMyevpQH2TQP4UMaLS3bRFY4BB27iPt2+3bXuzWHI43OX
20
+ rnx8JbcqkljdxamnxWiDDp42TSIUj9p+m3S/V3Suku3h4qyKcO4A97tvo28Jr69M
21
+ 1mpMGMi10FlBP25irKWL8QKBgQCZqKlnjrWwA24QROJnpoupeiMkIRH0m9rS/Kwb
22
+ YqHZEQoALTyEwTnpaxxLxXlecYiWCZGNCLFCEG2rcQBJhZv8xxLQC8yzNDtzKQJu
23
+ saRxYQG75ytXky94lebzLoIMmIcPVz13g9TblKZHKSHT9OUCdvewdhqXN8klLrh8
24
+ J3gafwKBgQDcn/8lzfmbsKO/V9fzEbqUQJOJkwyf2adLFD+KlHM3+VAghnj70j05
25
+ 1XDkQ3LOgKrqPdC/HIGjjlkTn6vaieXltHguMg99nPpzj+LXrtN22ru2eTjjUNsD
26
+ +uB9N4kgUAR00xu60xkkOSK1TcudeL5uwejO2ul0tkDu1YGDvhXymA==
27
+ -----END RSA PRIVATE KEY-----
@@ -4,18 +4,118 @@ require "flores/pki"
4
4
 
5
5
  describe LogStash::Outputs::Tcp do
6
6
  subject { described_class.new(config) }
7
- let(:config) { {
8
- "host" => "localhost",
9
- "port" => 2000 + rand(3000),
10
- } }
7
+
8
+ let(:port) do
9
+ begin
10
+ # Start high to better avoid common services
11
+ port = rand(10000..65535)
12
+ s = TCPServer.new("127.0.0.1", port)
13
+ s.close
14
+
15
+ port
16
+ rescue Errno::EADDRINUSE
17
+ retry
18
+ end
19
+ end
20
+
21
+ let(:server) { TCPServer.new("127.0.0.1", port) }
22
+
23
+ let(:config) { { "host" => "localhost", "port" => port } }
24
+
25
+ let(:event) { LogStash::Event.new('message' => 'foo bar') }
26
+
27
+ context 'failing to connect' do
28
+
29
+ before { subject.register }
30
+
31
+ let(:config) { super().merge 'port' => 1000 }
32
+
33
+ it 'fails to connect' do
34
+ expect( subject ).to receive(:log_error).and_call_original
35
+ Thread.start { subject.receive(event) }
36
+ sleep 1.0
37
+ end
38
+
39
+ end
40
+
41
+ context 'server mode' do
42
+
43
+ before { subject.register }
44
+
45
+ let(:config) { super().merge 'mode' => 'server' }
46
+
47
+ let(:client) do
48
+ Stud::try(3.times) { TCPSocket.new("127.0.0.1", port) }
49
+ end
50
+
51
+ after { subject.close }
52
+
53
+ it 'receives serialized data' do; require 'json'
54
+ client # connect
55
+ Thread.start { sleep 0.5; subject.receive event }
56
+
57
+ read = client.recv(1000)
58
+ expect( read.size ).to be > 0
59
+ expect( JSON.parse(read)['message'] ).to eql 'foo bar'
60
+ end
61
+
62
+ end
63
+
64
+ context "with forced protocol" do
65
+ let(:config) do
66
+ super().merge 'ssl_supported_protocols' => [ 'TLSv1.1' ]
67
+ end
68
+
69
+ it "limits protocol selection" do
70
+ if OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3
71
+ ssl_context = subject.send :setup_ssl
72
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_3).to_not eql 0
73
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to_not eql 0
74
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
75
+ else
76
+ ssl_context = OpenSSL::SSL::SSLContext.new
77
+ allow(subject).to receive(:new_ssl_context).and_return(ssl_context)
78
+ expect(ssl_context).to receive(:max_version=).with(:'TLS1_2').and_call_original
79
+ ssl_context = subject.send :setup_ssl
80
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to_not eql 0
81
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
82
+ end
83
+ end
84
+ end
85
+
86
+ context "with protocol range" do
87
+ let(:config) do
88
+ super().merge 'ssl_supported_protocols' => [ 'TLSv1.3', 'TLSv1.1', 'TLSv1.2' ]
89
+ end
90
+
91
+ it "does not limit protocol selection (except min_version)" do
92
+ ssl_context = OpenSSL::SSL::SSLContext.new
93
+ allow(subject).to receive(:new_ssl_context).and_return(ssl_context)
94
+ expect(ssl_context).to receive(:min_version=).with(:'TLS1_1').at_least(1).and_call_original
95
+
96
+ if OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3
97
+ subject.send :setup_ssl
98
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_3).to eql 0
99
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to eql 0
100
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
101
+ else
102
+ subject.send :setup_ssl
103
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to eql 0
104
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
105
+ end
106
+
107
+ subject.send :setup_ssl
108
+ end
109
+ end
11
110
 
12
111
  context "when enabling SSL" do
13
- let(:config) { super.merge("ssl_enable" => true) }
112
+ let(:config) { super().merge("ssl_enable" => true, 'codec' => 'plain') }
14
113
  context "and not providing a certificate/key pair" do
15
114
  it "registers without error" do
16
115
  expect { subject.register }.to_not raise_error
17
116
  end
18
117
  end
118
+
19
119
  context "and providing a certificate/key pair" do
20
120
  let(:cert_key_pair) { Flores::PKI.generate }
21
121
  let(:certificate) { cert_key_pair.first }
@@ -24,10 +124,108 @@ describe LogStash::Outputs::Tcp do
24
124
  IO.write(path, certificate.to_s)
25
125
  path
26
126
  end
27
- let(:config) { super.merge("ssl_cert" => true, "ssl_cert" => cert_file) }
127
+ let(:config) { super().merge("ssl_cert" => cert_file) }
28
128
  it "registers without error" do
29
129
  expect { subject.register }.to_not raise_error
30
130
  end
31
131
  end
132
+
133
+ FIXTURES_PATH = File.expand_path('../fixtures', File.dirname(__FILE__))
134
+
135
+ context "ES generated plain-text certificate/key" do
136
+ let(:key_file) { File.join(FIXTURES_PATH, 'plaintext/instance.key') }
137
+ let(:crt_file) { File.join(FIXTURES_PATH, 'plaintext/instance.crt') }
138
+ let(:config) { super().merge("ssl_cert" => crt_file, "ssl_key" => key_file) }
139
+
140
+ it "registers without error" do
141
+ expect { subject.register }.to_not raise_error
142
+ end
143
+
144
+ context 'with password set' do
145
+
146
+ let(:config) { super().merge("ssl_key_passphrase" => 'ignored') }
147
+
148
+ it "registers without error" do # password simply ignored
149
+ expect { subject.register }.to_not raise_error
150
+ end
151
+
152
+ end
153
+
154
+ let(:secure_server) do
155
+ ssl_context = OpenSSL::SSL::SSLContext.new
156
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
157
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(crt_file))
158
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read(key_file), nil)
159
+ ssl_context.ssl_version = server_ssl_version if server_ssl_version
160
+ ssl_context.min_version = server_min_version if server_min_version
161
+ ssl_context.max_version = server_max_version if server_max_version
162
+ OpenSSL::SSL::SSLServer.new(server, ssl_context)
163
+ end
164
+
165
+ let(:server_min_version) { nil }
166
+ let(:server_max_version) { nil }
167
+ let(:server_ssl_version) { nil }
168
+
169
+ context 'with supported protocol' do
170
+
171
+ let(:config) { super().merge("ssl_supported_protocols" => ['TLSv1.2']) }
172
+
173
+ let(:server_min_version) { 'TLS1_2' }
174
+
175
+ before { subject.register }
176
+ after { secure_server.close }
177
+
178
+ it 'reads plain data' do
179
+ Thread.start { sleep 0.25; subject.receive event }
180
+ socket = secure_server.accept
181
+ read = socket.sysread(100)
182
+ expect( read.size ).to be > 0
183
+ expect( read ).to end_with 'foo bar'
184
+ end
185
+
186
+ end
187
+
188
+ context 'with unsupported protocol (on server)' do
189
+
190
+ let(:config) { super().merge("ssl_supported_protocols" => ['TLSv1.1']) }
191
+
192
+ let(:server_min_version) { 'TLS1_2' }
193
+
194
+ before { subject.register }
195
+ after { secure_server.close }
196
+
197
+ it 'fails (and loops retrying)' do
198
+ expect(subject.logger).to receive(:error).with(/connect ssl failure/i, hash_including(message: /No appropriate protocol/i)).and_call_original
199
+ expect(subject.logger).to receive(:error).with(/failed to connect/i, hash_including(exception: OpenSSL::SSL::SSLError)).and_call_original
200
+ expect(subject).to receive(:sleep).once.and_call_original
201
+ expect(subject).to receive(:sleep).once.and_throw :TEST_DONE # to be able to abort the retry loop
202
+
203
+ Thread.start { secure_server.accept rescue nil }
204
+ expect { subject.receive event }.to throw_symbol(:TEST_DONE)
205
+ end
206
+
207
+ end if LOGSTASH_VERSION > '7.0'
208
+
209
+ end
210
+
211
+ context "encrypted key using PKCS#1" do
212
+ let(:key_file) { File.join(FIXTURES_PATH, 'encrypted/instance.key') }
213
+ let(:crt_file) { File.join(FIXTURES_PATH, 'encrypted/instance.crt') }
214
+ let(:config) { super().merge("ssl_cert" => crt_file, "ssl_key" => key_file) }
215
+
216
+ it "registers with error (due missing password)" do
217
+ expect { subject.register }.to raise_error(OpenSSL::PKey::RSAError) # TODO need a better error
218
+ end
219
+
220
+ context 'with valid password' do
221
+
222
+ let(:config) { super().merge("ssl_key_passphrase" => '1234567890') }
223
+
224
+ it "registers without error" do
225
+ expect { subject.register }.to_not raise_error
226
+ end
227
+
228
+ end
229
+ end
32
230
  end
33
231
  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.0.0
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-11 00:00:00.000000000 Z
11
+ date: 2022-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - "<="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: 8.1.0
39
+ name: logstash-core
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 8.1.0
33
47
  - !ruby/object:Gem::Dependency
34
48
  requirement: !ruby/object:Gem::Requirement
35
49
  requirements:
@@ -58,6 +72,20 @@ dependencies:
58
72
  - - ">="
59
73
  - !ruby/object:Gem::Version
60
74
  version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 0.12.2
81
+ name: jruby-openssl
82
+ prerelease: false
83
+ type: :runtime
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 0.12.2
61
89
  - !ruby/object:Gem::Dependency
62
90
  requirement: !ruby/object:Gem::Requirement
63
91
  requirements:
@@ -72,6 +100,20 @@ dependencies:
72
100
  - - ">="
73
101
  - !ruby/object:Gem::Version
74
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ name: logstash-codec-plain
110
+ prerelease: false
111
+ type: :development
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
75
117
  - !ruby/object:Gem::Dependency
76
118
  requirement: !ruby/object:Gem::Requirement
77
119
  requirements:
@@ -103,6 +145,10 @@ files:
103
145
  - docs/index.asciidoc
104
146
  - lib/logstash/outputs/tcp.rb
105
147
  - logstash-output-tcp.gemspec
148
+ - spec/fixtures/encrypted/instance.crt
149
+ - spec/fixtures/encrypted/instance.key
150
+ - spec/fixtures/plaintext/instance.crt
151
+ - spec/fixtures/plaintext/instance.key
106
152
  - spec/outputs/tcp_spec.rb
107
153
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
108
154
  licenses:
@@ -125,10 +171,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
171
  - !ruby/object:Gem::Version
126
172
  version: '0'
127
173
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.6.13
174
+ rubygems_version: 3.1.6
130
175
  signing_key:
131
176
  specification_version: 4
132
177
  summary: Writes events over a TCP socket
133
178
  test_files:
179
+ - spec/fixtures/encrypted/instance.crt
180
+ - spec/fixtures/encrypted/instance.key
181
+ - spec/fixtures/plaintext/instance.crt
182
+ - spec/fixtures/plaintext/instance.key
134
183
  - spec/outputs/tcp_spec.rb