logstash-output-tcp 6.1.2 → 6.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/docs/index.asciidoc +83 -6
- data/lib/logstash/outputs/tcp.rb +192 -23
- data/logstash-output-tcp.gemspec +2 -1
- data/spec/outputs/tcp_spec.rb +194 -9
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12843c570af7ef789c697435e55375ecda6d6ed85a63181ae0501cc21298bbeb
|
4
|
+
data.tar.gz: 95a41abe82bb81210646428fb6d228a390a7ba87043e817dab5a75b19405195e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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|
|
44
|
-
| <<plugins-{type}s-{plugin}-ssl_cert>> |a valid filesystem path|
|
45
|
-
| <<plugins-{type}s-{plugin}-
|
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
|
|
data/lib/logstash/outputs/tcp.rb
CHANGED
@@ -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 :
|
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 @
|
99
|
-
@ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@
|
111
|
-
|
112
|
-
|
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
|
-
@
|
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 @
|
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 @
|
223
|
+
if @ssl_enabled
|
164
224
|
@server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
|
165
|
-
end # @
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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 @
|
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?
|
data/logstash-output-tcp.gemspec
CHANGED
@@ -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
|
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
|
|
data/spec/outputs/tcp_spec.rb
CHANGED
@@ -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("
|
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)
|
149
|
-
|
150
|
-
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(:
|
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("
|
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("
|
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("
|
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
|
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:
|
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:
|