logstash-input-tcp 6.0.8-java → 6.2.0-java

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: 574feed9b3754fbb15bad08e4d670e9771261f1f90cbe45731fd3fd58b30deec
4
- data.tar.gz: 259fc46a2885620249b23bd3d927a0a9948fc71544a7ea775569098e005da9e0
3
+ metadata.gz: 29b44274cfe25c623273407b63c29125143a6ec704c274732b73e9a3fc39f085
4
+ data.tar.gz: 4bced499147e26e9f8257d2c7a489a7dae93ec2a1c06faf2b45b3d9af801d222
5
5
  SHA512:
6
- metadata.gz: 55d972be5de090da3e6142a54413064590495b2773b1c6bc21579e3e80ff644ba57b4a7a1083bf629fddd6e4ac85282ea74e5d9da4f05810b5cb45c50f176bfb
7
- data.tar.gz: 662ca5182f7b4e4b6f40479c5a984eed7df2837c1853b1d42632270a239b162cfd490ec4d5079b59aa2cf1e6480bf3acf2e846fe268d8f3c836ea6d19daa66e5
6
+ metadata.gz: d45ef4273d94e4dc3dbcc94749d0e8d907ea96acb20ee4aeff94251a5390bb26b51105fc2f6104e09b885dc195f9fbe94261047035f905884d8306649e8aec49
7
+ data.tar.gz: f00ff1165183ce006a12541b7836bb3a09496eb4370bca185104ea81c8fc8e2360aae8ed09eeb170ab93ee9e64e7233af9360e4cbb73fc5d9b49adf27d26534b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 6.2.0
2
+ - Added ECS Compatibility Mode [#165](https://github.com/logstash-plugins/logstash-input-tcp/pull/165)
3
+ - When operating in an ECS Compatibility mode, metadata about the connection on which we are receiving data is nested in well-named fields under `[@metadata][input][tcp]` instead of at the root level.
4
+ - Fix: source address is no longer missing when a proxy is present
5
+
6
+ ## 6.1.1
7
+ - Changed jar dependencies to reflect newer versions [#179](https://github.com/logstash-plugins/logstash-input-http/pull/179)
8
+
9
+ ## 6.1.0
10
+ - Feat: improve SSL error logging/unwrapping [#178](https://github.com/logstash-plugins/logstash-input-tcp/pull/178)
11
+ - Fix: the plugin will no longer have a side effect of adding the Bouncy-Castle security provider at runtime
12
+
13
+ ## 6.0.10
14
+ - bumping dependency commons-io [#174](https://github.com/logstash-plugins/logstash-input-tcp/pull/174)
15
+
16
+ ## 6.0.9
17
+ - [DOC] Reorder options alphabetically [#171](https://github.com/logstash-plugins/logstash-input-tcp/pull/171)
18
+
1
19
  ## 6.0.8
2
20
  - [DOC] better description for `tcp_keep_alive` option [#169](https://github.com/logstash-plugins/logstash-input-tcp/pull/169)
3
21
 
data/docs/index.asciidoc CHANGED
@@ -70,6 +70,52 @@ event timestamp
70
70
  }
71
71
  }
72
72
 
73
+ [id="plugins-{type}s-{plugin}-ecs_metadata"]
74
+ ==== Event Metadata and the Elastic Common Schema (ECS)
75
+
76
+ In addition to decoding the events, this input will add metadata about the TCP connection itself to each event.
77
+ This can be helpful when applications are configured to send events directly to this input's TCP listener without including information about themselves.
78
+
79
+ Historically, this metadata was added to a variety of non-standard top-level fields, which had the potential to create confusion and schema conflicts downstream.
80
+ With ECS compatibility mode, we can ensure a pipeline still has access to this metadata throughout the event's lifecycle without polluting the top-level namespace.
81
+
82
+ [cols="3,7,5"]
83
+ |=======================================================================
84
+ | Metadata Group | ecs: `v1`, `v8` | ecs: `disabled`
85
+
86
+ .3+|Source Metadata from the TCP connection
87
+ on which events are being received, including
88
+ the sender's name, ip, and outbound port. l|[@metadata][input][tcp][source][name] l|[host]
89
+ l|[@metadata][input][tcp][source][ip] l|[@metadata][ip_address]
90
+ l|[@metadata][input][tcp][source][port] l|[port]
91
+
92
+ .2+|Proxy Metadata from a proxied TCP connection.
93
+ Available when receiving events by proxy and
94
+ `proxy_protocol => true` l|[@metadata][input][tcp][proxy][ip] l|[proxy_host]
95
+ l|[@metadata][input][tcp][proxy][port] l|[proxy_port]
96
+
97
+ .1+|SSL Subject Metadata from a secured TCP
98
+ connection. Available when `ssl_enable => true`
99
+ AND `ssl_verify => true` l|[@metadata][input][tcp][ssl][subject] l|[sslsubject]
100
+ |=======================================================================
101
+
102
+ For example, the Elastic Common Schema reserves the https://www.elastic.co/guide/en/ecs/current/ecs-host.html[top-level `host` field] for information about the host on which the event happened.
103
+ If an event is missing this metadata, it can be copied into place from the source TCP connection metadata that has been added to the event:
104
+
105
+ [source,txt]
106
+ -----
107
+ filter {
108
+ if [@metadata][input][tcp][source] and not [host] {
109
+ mutate {
110
+ copy {
111
+ "[@metadata][input][tcp][source][name]" => "[host][name]"
112
+ "[@metadata][input][tcp][source][ip]" => "[host][ip]"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ -----
118
+
73
119
  [id="plugins-{type}s-{plugin}-options"]
74
120
  ==== Tcp Input Configuration Options
75
121
 
@@ -78,6 +124,8 @@ This plugin supports the following configuration options plus the <<plugins-{typ
78
124
  [cols="<,<,<",options="header",]
79
125
  |=======================================================================
80
126
  |Setting |Input type|Required
127
+ | <<plugins-{type}s-{plugin}-dns_reverse_lookup_enabled>> |<<boolean,boolean>>|No
128
+ | <<plugins-{type}s-{plugin}-ecs_compatibility>> | <<string,string>>|No
81
129
  | <<plugins-{type}s-{plugin}-host>> |<<string,string>>|No
82
130
  | <<plugins-{type}s-{plugin}-mode>> |<<string,string>>, one of `["server", "client"]`|No
83
131
  | <<plugins-{type}s-{plugin}-port>> |<<number,number>>|Yes
@@ -90,7 +138,6 @@ This plugin supports the following configuration options plus the <<plugins-{typ
90
138
  | <<plugins-{type}s-{plugin}-ssl_key_passphrase>> |<<password,password>>|No
91
139
  | <<plugins-{type}s-{plugin}-ssl_verify>> |<<boolean,boolean>>|No
92
140
  | <<plugins-{type}s-{plugin}-tcp_keep_alive>> |<<boolean,boolean>>|No
93
- | <<plugins-{type}s-{plugin}-dns_reverse_lookup_enabled>> |<<boolean,boolean>>|No
94
141
  |=======================================================================
95
142
 
96
143
  Also see <<plugins-{type}s-{plugin}-common-options>> for a list of options supported by all
@@ -98,6 +145,30 @@ input plugins.
98
145
 
99
146
  &nbsp;
100
147
 
148
+ [id="plugins-{type}s-{plugin}-dns_reverse_lookup_enabled"]
149
+ ===== `dns_reverse_lookup_enabled`
150
+
151
+ * Value type is <<boolean,boolean>>
152
+ * Default value is `true`
153
+
154
+ It is possible to avoid DNS reverse-lookups by disabling this setting. If disabled,
155
+ the address metadata that is added to events will contain the source address as-specified
156
+ at the TCP layer and IPs will not be resolved to hostnames.
157
+
158
+ [id="plugins-{type}s-{plugin}-ecs_compatibility"]
159
+ ===== `ecs_compatibility`
160
+
161
+ * Value type is <<string,string>>
162
+ * Supported values are:
163
+ ** `disabled`: unstructured connection metadata added at root level
164
+ ** `v1`,`v8`: structured connection metadata added under `[@metadata][input][tcp]`
165
+ * Default value depends on which version of Logstash is running:
166
+ ** When Logstash provides a `pipeline.ecs_compatibility` setting, its value is used as the default
167
+ ** Otherwise, the default value is `disabled`.
168
+
169
+ Controls this plugin's compatibility with the https://www.elastic.co/guide/en/ecs/current/index.html[Elastic Common Schema (ECS)].
170
+ The value of this setting affects the <<plugins-{type}s-{plugin}-ecs_metadata,placement of a TCP connection's metadata>> on events.
171
+
101
172
  [id="plugins-{type}s-{plugin}-host"]
102
173
  ===== `host`
103
174
 
@@ -206,16 +277,6 @@ Instruct the socket to use TCP keep alive. If it's `true` then the underlying so
206
277
  will use the OS defaults settings for keep alive. If it's `false` it doesn't configure any
207
278
  keep alive setting for the underlying socket.
208
279
 
209
- [id="plugins-{type}s-{plugin}-dns_reverse_lookup_enabled"]
210
- ===== `dns_reverse_lookup_enabled`
211
-
212
- * Value type is <<boolean,boolean>>
213
- * Default value is `true`
214
-
215
- It is possible to avoid DNS reverse-lookups by disabling this setting. If disabled,
216
- the address metadata that is added to events will contain the source address as-specified
217
- at the TCP layer and IPs will not be resolved to hostnames.
218
-
219
280
 
220
281
  [id="plugins-{type}s-{plugin}-common-options"]
221
282
  include::{include_path}/{type}.asciidoc[]
@@ -5,8 +5,7 @@ require "java"
5
5
  require "logstash/inputs/base"
6
6
  require "logstash/util/socket_peer"
7
7
  require "logstash-input-tcp_jars"
8
- require "logstash/inputs/tcp/decoder_impl"
9
- require "logstash/inputs/tcp/compat_ssl_options"
8
+ require 'logstash/plugin_mixins/ecs_compatibility_support'
10
9
 
11
10
  require "socket"
12
11
  require "openssl"
@@ -61,7 +60,13 @@ require "openssl"
61
60
  # }
62
61
  class LogStash::Inputs::Tcp < LogStash::Inputs::Base
63
62
 
64
- java_import org.logstash.tcp.InputLoop
63
+ java_import 'org.logstash.tcp.InputLoop'
64
+ java_import 'org.logstash.tcp.SslContextBuilder'
65
+
66
+ require_relative "tcp/decoder_impl"
67
+
68
+ # ecs_compatibility option, provided by Logstash core or the support adapter.
69
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
65
70
 
66
71
  config_name "tcp"
67
72
 
@@ -103,7 +108,8 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
103
108
  # Useful when the CA chain is not necessary in the system store.
104
109
  config :ssl_extra_chain_certs, :validate => :array, :default => []
105
110
 
106
- # Validate client certificates against these authorities. You can define multiple files or paths. All the certificates will be read and added to the trust store.
111
+ # Validate client certificates against these authorities. You can define multiple files or paths.
112
+ # All the certificates will be read and added to the trust store.
107
113
  config :ssl_certificate_authorities, :validate => :array, :default => []
108
114
 
109
115
  # Instruct the socket to use TCP keep alives. Uses OS defaults for keep alive settings.
@@ -112,13 +118,6 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
112
118
  # Option to allow users to avoid DNS Reverse Lookup.
113
119
  config :dns_reverse_lookup_enabled, :validate => :boolean, :default => true
114
120
 
115
- HOST_FIELD = "host".freeze
116
- HOST_IP_FIELD = "[@metadata][ip_address]".freeze
117
- PORT_FIELD = "port".freeze
118
- PROXY_HOST_FIELD = "proxy_host".freeze
119
- PROXY_PORT_FIELD = "proxy_port".freeze
120
- SSLSUBJECT_FIELD = "sslsubject".freeze
121
-
122
121
  # Monkey patch TCPSocket and SSLSocket to include socket peer
123
122
  # @private
124
123
  def self.patch_socket_peer!
@@ -133,6 +132,8 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
133
132
  def initialize(*args)
134
133
  super(*args)
135
134
 
135
+ setup_fields!
136
+
136
137
  self.class.patch_socket_peer!
137
138
 
138
139
  # threadsafe socket bookkeeping
@@ -148,10 +149,7 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
148
149
  fix_streaming_codecs
149
150
 
150
151
  if server?
151
- ssl_context = get_ssl_context(SslOptions)
152
-
153
-
154
- @loop = InputLoop.new(@host, @port, DecoderImpl.new(@codec, self), @tcp_keep_alive, ssl_context)
152
+ @loop = InputLoop.new(@host, @port, DecoderImpl.new(@codec, self), @tcp_keep_alive, java_ssl_context)
155
153
  end
156
154
  end
157
155
 
@@ -188,8 +186,8 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
188
186
  proxy_port, tbuf, socket)
189
187
  codec.decode(tbuf) do |event|
190
188
  if @proxy_protocol
191
- event.set(PROXY_HOST_FIELD, proxy_address) unless event.get(PROXY_HOST_FIELD)
192
- event.set(PROXY_PORT_FIELD, proxy_port) unless event.get(PROXY_PORT_FIELD)
189
+ event.set(@field_proxy_host, proxy_address) unless event.get(@field_proxy_host)
190
+ event.set(@field_proxy_port, proxy_port) unless event.get(@field_proxy_port)
193
191
  end
194
192
  enqueue_decorated(event, client_ip_address, client_address, client_port, socket)
195
193
  end
@@ -262,14 +260,24 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
262
260
  end
263
261
 
264
262
  def enqueue_decorated(event, client_ip_address, client_address, client_port, socket)
265
- event.set(HOST_FIELD, client_address) unless event.get(HOST_FIELD)
266
- event.set(HOST_IP_FIELD, client_ip_address) unless event.get(HOST_IP_FIELD)
267
- event.set(PORT_FIELD, client_port) unless event.get(PORT_FIELD)
268
- event.set(SSLSUBJECT_FIELD, socket.peer_cert.subject.to_s) if socket && @ssl_enable && @ssl_verify && event.get(SSLSUBJECT_FIELD).nil?
263
+ event.set(@field_host, client_address) unless event.get(@field_host)
264
+ event.set(@field_host_ip, client_ip_address) unless event.get(@field_host_ip)
265
+ event.set(@field_port, client_port) unless event.get(@field_port)
266
+ event.set(@field_sslsubject, socket.peer_cert.subject.to_s) if socket && @ssl_enable && @ssl_verify && event.get(@field_sslsubject).nil?
269
267
  decorate(event)
270
268
  @output_queue << event
271
269
  end
272
270
 
271
+ # setup the field names, with respect to ECS compatibility.
272
+ def setup_fields!
273
+ @field_host = ecs_select[disabled: "host", v1: "[@metadata][input][tcp][source][name]" ].freeze
274
+ @field_host_ip = ecs_select[disabled: "[@metadata][ip_address]", v1: "[@metadata][input][tcp][source][ip]" ].freeze
275
+ @field_port = ecs_select[disabled: "port", v1: "[@metadata][input][tcp][source][port]" ].freeze
276
+ @field_proxy_host = ecs_select[disabled: "proxy_host", v1: "[@metadata][input][tcp][proxy][ip]" ].freeze
277
+ @field_proxy_port = ecs_select[disabled: "proxy_port", v1: "[@metadata][input][tcp][proxy][port]" ].freeze
278
+ @field_sslsubject = ecs_select[disabled: "sslsubject", v1: "[@metadata][input][tcp][tls][client][subject]"].freeze
279
+ end
280
+
273
281
  def server?
274
282
  @mode == "server"
275
283
  end
@@ -320,7 +328,7 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
320
328
 
321
329
  socket
322
330
  rescue OpenSSL::SSL::SSLError => e
323
- @logger.error("SSL Error", :exception => e, :backtrace => e.backtrace)
331
+ @logger.error("SSL Error", :message => e.message, :exception => e.class, :backtrace => e.backtrace)
324
332
  # catch all rescue nil on close to discard any close errors or invalid socket
325
333
  socket.close rescue nil
326
334
  sleep(1) # prevent hammering peer
@@ -362,15 +370,33 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
362
370
  @socket_mutex.synchronize{@connection_sockets.keys.dup}
363
371
  end
364
372
 
365
- def get_ssl_context(options_class)
366
- ssl_context = options_class.builder
367
- .set_is_ssl_enabled(@ssl_enable)
373
+ def java_ssl_context
374
+ SslContextBuilder.new
375
+ .set_ssl_enabled(@ssl_enable)
368
376
  .set_should_verify(@ssl_verify)
369
377
  .set_ssl_cert(@ssl_cert)
370
378
  .set_ssl_key(@ssl_key)
371
- .set_ssl_key_passphrase(@ssl_key_passphrase.value)
379
+ .set_ssl_key_password(@ssl_key_passphrase.value)
372
380
  .set_ssl_extra_chain_certs(@ssl_extra_chain_certs.to_java(:string))
373
381
  .set_ssl_certificate_authorities(@ssl_certificate_authorities.to_java(:string))
374
- .build.toSslContext()
382
+ .build_context
383
+ rescue java.lang.IllegalArgumentException => e
384
+ @logger.error("SSL configuration invalid", error_details(e))
385
+ raise LogStash::ConfigurationError, e
386
+ rescue java.lang.Exception => e
387
+ @logger.error("SSL configuration failed", error_details(e, true))
388
+ raise e
389
+ end
390
+
391
+ def error_details(e, trace = false)
392
+ error_details = { :exception => e.class, :message => e.message }
393
+ error_details[:backtrace] = e.backtrace if trace || @logger.debug?
394
+ cause = e.cause
395
+ if cause && e != cause
396
+ error_details[:cause] = { :exception => cause.class, :message => cause.message }
397
+ error_details[:cause][:backtrace] = cause.backtrace if trace || @logger.debug?
398
+ end
399
+ error_details
375
400
  end
401
+
376
402
  end
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require 'java'
3
3
 
4
- class DecoderImpl
4
+ class LogStash::Inputs::Tcp::DecoderImpl
5
5
 
6
6
  include org.logstash.tcp.Decoder
7
7
 
@@ -24,7 +24,7 @@ class DecoderImpl
24
24
  end
25
25
 
26
26
  def copy
27
- DecoderImpl.new(@codec.clone, @tcp)
27
+ self.class.new(@codec.clone, @tcp)
28
28
  end
29
29
 
30
30
  def flush
@@ -41,16 +41,17 @@ class DecoderImpl
41
41
  @tcp.logger.error("Invalid proxy protocol header label", :header => pp_hdr)
42
42
  raise IOError.new("Invalid proxy protocol header label #{pp_hdr.inspect}")
43
43
  else
44
- @proxy_address = pp_info[3]
45
- @proxy_port = pp_info[5]
46
- @address = pp_info[2]
47
- @port = pp_info[4]
44
+ @proxy_address = pp_info[3] # layer 3 destination address (proxy's receiving address)
45
+ @proxy_port = pp_info[5] # TCP destination port (proxy's receiving port)
46
+ @ip_address = pp_info[2] # layer 3 source address (outgoing ip of sender)
47
+ @address = extract_host_name(@ip_address)
48
+ @port = pp_info[4] # TCP source port (outgoing port on sender [probably random])
48
49
  end
49
50
  else
50
51
  filtered = received
51
- @ip_address = channel_addr.get_address.get_host_address
52
- @address = extract_host_name(channel_addr)
53
- @port = channel_addr.get_port
52
+ @ip_address = channel_addr.get_address.get_host_address # ip address of sender
53
+ @address = extract_host_name(channel_addr) # name _or_ address of sender
54
+ @port = channel_addr.get_port # outgoing port of sender (probably random)
54
55
  end
55
56
  @first_read = false
56
57
  filtered
@@ -58,6 +59,8 @@ class DecoderImpl
58
59
 
59
60
  private
60
61
  def extract_host_name(channel_addr)
62
+ channel_addr = java.net.InetSocketAddress.new(channel_addr, 0) if channel_addr.kind_of?(String)
63
+
61
64
  return channel_addr.get_host_string unless @tcp.dns_reverse_lookup_enabled?
62
65
 
63
66
  channel_addr.get_host_name
@@ -21,6 +21,12 @@ 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
+ s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.2'
25
+
26
+ s.add_runtime_dependency 'logstash-core', '>= 6.7.0'
27
+
28
+ # we depend on bouncycastle's bcpkix-jdk15on being on the class-path
29
+ s.add_runtime_dependency 'jruby-openssl', '>= 0.10.2', '< 0.12'
24
30
 
25
31
  # line vs streaming codecs required for fix_streaming_codecs
26
32
  # TODO: fix_streaming_codecs should be refactored to not
@@ -15,9 +15,11 @@ java_import "io.netty.handler.ssl.util.SelfSignedCertificate"
15
15
 
16
16
  require_relative "../spec_helper"
17
17
 
18
+ require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
19
+
18
20
  #Cabin::Channel.get(LogStash).subscribe(STDOUT)
19
21
  #Cabin::Channel.get(LogStash).level = :debug
20
- describe LogStash::Inputs::Tcp do
22
+ describe LogStash::Inputs::Tcp, :ecs_compatibility_support do
21
23
 
22
24
  def get_port
23
25
  begin
@@ -52,160 +54,176 @@ describe LogStash::Inputs::Tcp do
52
54
  end
53
55
  end
54
56
 
55
- it "should read plain with unicode" do
56
- event_count = 10
57
- conf = <<-CONFIG
58
- input {
59
- tcp {
60
- port => #{port}
57
+ ecs_compatibility_matrix(:disabled,:v1, :v8 => :v1) do |ecs_select|
58
+ before(:each) do
59
+ allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility)
60
+ end
61
+
62
+ it "should read plain with unicode" do
63
+ event_count = 10
64
+ conf = <<-CONFIG
65
+ input {
66
+ tcp {
67
+ port => #{port}
68
+ }
61
69
  }
62
- }
63
- CONFIG
70
+ CONFIG
71
+
72
+ host = 'localhost'
73
+ events = input(conf) do |pipeline, queue|
74
+ socket = Stud::try(5.times) { TCPSocket.new(host, port) }
75
+ event_count.times do |i|
76
+ # unicode smiley for testing unicode support!
77
+ socket.puts("#{i} ☹")
78
+ socket.flush
79
+ end
80
+ socket.close
64
81
 
65
- host = 'localhost'
66
- events = input(conf) do |pipeline, queue|
67
- socket = Stud::try(5.times) { TCPSocket.new(host, port) }
68
- event_count.times do |i|
69
- # unicode smiley for testing unicode support!
70
- socket.puts("#{i} ☹")
71
- socket.flush
82
+ event_count.times.collect {queue.pop}
72
83
  end
73
- socket.close
74
84
 
75
- event_count.times.collect {queue.pop}
76
- end
85
+ expect(events.length).to eq(event_count)
86
+ events = events.sort_by {|e| e.get("message")} # the ordering of events in the queue is highly timing-dependent
87
+ event_count.times do |i|
88
+ event = events[i]
77
89
 
78
- insist { events.length } == event_count
79
- events = events.sort_by {|e| e.get("message")} # the ordering of events in the queue is highly timing-dependent
80
- event_count.times do |i|
81
- event = events[i]
82
- insist { event.get("message") } == "#{i} ☹"
83
- insist { ["localhost","ip6-localhost"].includes? event.get("host") }
84
- insist { event.get("[@metadata][ip_address]") } == '127.0.0.1'
90
+ aggregate_failures("event #{i}") do
91
+ expect(event.get("message")).to eq("#{i} ☹")
92
+ expect(event.get(ecs_select[disabled: "host", v1: "[@metadata][input][tcp][source][name]"])).to eq("localhost").or eq("ip6-localhost")
93
+ expect(event.get(ecs_select[disabled: "[@metadata][ip_address]", v1: "[@metadata][input][tcp][source][ip]"])).to eq('127.0.0.1')
94
+ end
95
+ end
85
96
  end
86
- end
87
97
 
88
- it "should handle PROXY protocol v1 connections" do
89
- event_count = 10
90
- conf = <<-CONFIG
91
- input {
92
- tcp {
93
- proxy_protocol => true
94
- port => '#{port}'
98
+ it "should handle PROXY protocol v1 connections" do
99
+ event_count = 10
100
+ conf = <<-CONFIG
101
+ input {
102
+ tcp {
103
+ proxy_protocol => true
104
+ port => '#{port}'
105
+ }
95
106
  }
96
- }
97
- CONFIG
107
+ CONFIG
98
108
 
99
- events = input(conf) do |pipeline, queue|
100
- socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
101
- socket.puts("PROXY TCP4 1.2.3.4 5.6.7.8 1234 5678\r");
102
- socket.flush
103
- event_count.times do |i|
104
- # unicode smiley for testing unicode support!
105
- socket.puts("#{i} ☹")
109
+ events = input(conf) do |pipeline, queue|
110
+ socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
111
+ socket.puts("PROXY TCP4 1.2.3.4 5.6.7.8 1234 5678\r");
106
112
  socket.flush
107
- end
108
- socket.close
113
+ event_count.times do |i|
114
+ # unicode smiley for testing unicode support!
115
+ socket.puts("#{i} ☹")
116
+ socket.flush
117
+ end
118
+ socket.close
109
119
 
110
- event_count.times.collect {queue.pop}
111
- end
120
+ event_count.times.collect {queue.pop}
121
+ end
112
122
 
113
- insist { events.length } == event_count
114
- events = events.sort_by {|e| e.get("message")} # the ordering of events in the queue is highly timing-dependent
115
- event_count.times do |i|
116
- insist { events[i].get("message") } == "#{i} ☹"
117
- insist { events[i].get("host") } == "1.2.3.4"
118
- insist { events[i].get("port") } == "1234"
119
- insist { events[i].get("proxy_host") } == "5.6.7.8"
120
- insist { events[i].get("proxy_port") } == "5678"
123
+ expect(events.length).to eq(event_count)
124
+ events = events.sort_by {|e| e.get("message")} # the ordering of events in the queue is highly timing-dependent
125
+ events.each_with_index do |event, i|
126
+ aggregate_failures("event #{i}") do
127
+ expect(event.get("message")).to eq("#{i} ")
128
+ expect(event.get(ecs_select[disabled: "host", v1: "[@metadata][input][tcp][source][name]"])).to eq('1.2.3.4')
129
+ expect(event.get(ecs_select[disabled: "[@metadata][ip_address]", v1: "[@metadata][input][tcp][source][ip]" ])).to eq('1.2.3.4')
130
+ expect(event.get(ecs_select[disabled: "port", v1: "[@metadata][input][tcp][source][port]"])).to eq('1234')
131
+ expect(event.get(ecs_select[disabled: "proxy_host", v1: "[@metadata][input][tcp][proxy][ip]" ])).to eq('5.6.7.8')
132
+ expect(event.get(ecs_select[disabled: "proxy_port", v1: "[@metadata][input][tcp][proxy][port]" ])).to eq('5678')
133
+ end
134
+ end
121
135
  end
122
- end
123
136
 
124
- it "should read events with plain codec and ISO-8859-1 charset" do
125
- charset = "ISO-8859-1"
126
- conf = <<-CONFIG
127
- input {
128
- tcp {
129
- port => #{port}
130
- codec => plain { charset => "#{charset}" }
137
+ it "should read events with json codec" do
138
+ conf = <<-CONFIG
139
+ input {
140
+ tcp {
141
+ port => #{port}
142
+ codec => json
143
+ }
131
144
  }
145
+ CONFIG
146
+
147
+ data = {
148
+ "hello" => "world",
149
+ "foo" => [1,2,3],
150
+ "baz" => { "1" => "2" },
151
+ "host" => "example host"
132
152
  }
133
- CONFIG
134
153
 
135
- event = input(conf) do |pipeline, queue|
136
- socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
137
- text = "\xA3" # the £ symbol in ISO-8859-1 aka Latin-1
138
- text.force_encoding("ISO-8859-1")
139
- socket.puts(text)
140
- socket.close
154
+ event = input(conf) do |pipeline, queue|
155
+ socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
156
+ socket.puts(LogStash::Json.dump(data))
157
+ socket.close
141
158
 
142
- queue.pop
143
- end
159
+ queue.pop
160
+ end
144
161
 
145
- # Make sure the 0xA3 latin-1 code converts correctly to UTF-8.
146
- insist { event.get("message").size } == 1
147
- insist { event.get("message").bytesize } == 2
148
- insist { event.get("message") } == "£"
149
- end
162
+ insist { event.get("hello") } == data["hello"]
163
+ insist { event.get("foo").to_a } == data["foo"] # to_a to cast Java ArrayList produced by JrJackson
164
+ insist { event.get("baz") } == data["baz"]
150
165
 
151
- it "should read events with json codec" do
152
- conf = <<-CONFIG
153
- input {
154
- tcp {
155
- port => #{port}
156
- codec => json
166
+ # Make sure the tcp input, w/ json codec, uses the event's 'host' value,
167
+ # if present, instead of providing its own
168
+ insist { event.get("host") } == data["host"]
169
+ end
170
+
171
+ it "should read events with json codec (testing 'host' handling)" do
172
+ conf = <<-CONFIG
173
+ input {
174
+ tcp {
175
+ port => #{port}
176
+ codec => json
177
+ }
157
178
  }
179
+ CONFIG
180
+
181
+ data = {
182
+ "hello" => "world"
158
183
  }
159
- CONFIG
160
184
 
161
- data = {
162
- "hello" => "world",
163
- "foo" => [1,2,3],
164
- "baz" => { "1" => "2" },
165
- "host" => "example host"
166
- }
185
+ event = input(conf) do |pipeline, queue|
186
+ socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
187
+ socket.puts(LogStash::Json.dump(data))
188
+ socket.close
167
189
 
168
- event = input(conf) do |pipeline, queue|
169
- socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
170
- socket.puts(LogStash::Json.dump(data))
171
- socket.close
190
+ queue.pop
191
+ end
172
192
 
173
- queue.pop
193
+ aggregate_failures("event") do
194
+ expect(event.get("hello")).to eq(data["hello"])
195
+ expect(event).to include(ecs_select[disabled: "host", v1: "[@metadata][input][tcp][source][name]"])
196
+ expect(event).to include(ecs_select[disabled: "[@metadata][ip_address]", v1: "[@metadata][input][tcp][source][ip]" ])
197
+ end
174
198
  end
175
-
176
- insist { event.get("hello") } == data["hello"]
177
- insist { event.get("foo").to_a } == data["foo"] # to_a to cast Java ArrayList produced by JrJackson
178
- insist { event.get("baz") } == data["baz"]
179
-
180
- # Make sure the tcp input, w/ json codec, uses the event's 'host' value,
181
- # if present, instead of providing its own
182
- insist { event.get("host") } == data["host"]
183
199
  end
184
200
 
185
- it "should read events with json codec (testing 'host' handling)" do
201
+ it "should read events with plain codec and ISO-8859-1 charset" do
202
+ charset = "ISO-8859-1"
186
203
  conf = <<-CONFIG
187
- input {
188
- tcp {
189
- port => #{port}
190
- codec => json
204
+ input {
205
+ tcp {
206
+ port => #{port}
207
+ codec => plain { charset => "#{charset}" }
208
+ }
191
209
  }
192
- }
193
210
  CONFIG
194
211
 
195
- data = {
196
- "hello" => "world"
197
- }
198
-
199
212
  event = input(conf) do |pipeline, queue|
200
213
  socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
201
- socket.puts(LogStash::Json.dump(data))
214
+ text = "\xA3" # the £ symbol in ISO-8859-1 aka Latin-1
215
+ text.force_encoding("ISO-8859-1")
216
+ socket.puts(text)
202
217
  socket.close
203
218
 
204
219
  queue.pop
205
220
  end
206
221
 
207
- insist { event.get("hello") } == data["hello"]
208
- insist { event }.include?("host")
222
+ # Make sure the 0xA3 latin-1 code converts correctly to UTF-8.
223
+ aggregate_failures("event") do
224
+ expect(event.get("message")).to have_attributes(size: 1, bytesize: 2, encoding: Encoding.find("UTF-8"))
225
+ expect(event.get("message")).to eq("£")
226
+ end
209
227
  end
210
228
 
211
229
  it "should read events with json_lines codec" do
data/version CHANGED
@@ -1 +1 @@
1
- 6.0.8
1
+ 6.2.0
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-tcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.8
4
+ version: 6.2.0
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-23 00:00:00.000000000 Z
11
+ date: 2021-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +30,54 @@ 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: '1.2'
39
+ name: logstash-mixin-ecs_compatibility_support
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.2'
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 6.7.0
53
+ name: logstash-core
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 6.7.0
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.10.2
67
+ - - "<"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.12'
70
+ name: jruby-openssl
71
+ prerelease: false
72
+ type: :runtime
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 0.10.2
78
+ - - "<"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.12'
33
81
  - !ruby/object:Gem::Dependency
34
82
  requirement: !ruby/object:Gem::Requirement
35
83
  requirements:
@@ -171,15 +219,13 @@ files:
171
219
  - NOTICE.TXT
172
220
  - README.md
173
221
  - docs/index.asciidoc
174
- - docs/testfile.asciidoc
175
222
  - lib/logstash-input-tcp_jars.rb
176
223
  - lib/logstash/inputs/tcp.rb
177
- - lib/logstash/inputs/tcp/compat_ssl_options.rb
178
224
  - lib/logstash/inputs/tcp/decoder_impl.rb
179
225
  - logstash-input-tcp.gemspec
180
226
  - spec/inputs/tcp_spec.rb
181
227
  - spec/spec_helper.rb
182
- - vendor/jar-dependencies/org/logstash/inputs/logstash-input-tcp/6.0.8/logstash-input-tcp-6.0.8.jar
228
+ - vendor/jar-dependencies/org/logstash/inputs/logstash-input-tcp/6.2.0/logstash-input-tcp-6.2.0.jar
183
229
  - version
184
230
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
185
231
  licenses:
@@ -1,30 +0,0 @@
1
- :plugin: tcp-test
2
- :type: input
3
- :default_codec: line
4
-
5
- ///////////////////////////////////////////
6
- START - GENERATED VARIABLES, DO NOT EDIT!
7
- ///////////////////////////////////////////
8
- :version: %VERSION%
9
- :release_date: %RELEASE_DATE%
10
- :changelog_url: %CHANGELOG_URL%
11
- :include_path: ../../../../logstash/docs/include
12
- ///////////////////////////////////////////
13
- END - GENERATED VARIABLES, DO NOT EDIT!
14
- ///////////////////////////////////////////
15
-
16
- [id="plugins-{type}s-{plugin}"]
17
-
18
- === Tcp-test input plugin
19
-
20
- include::{include_path}/plugin_header.asciidoc[]
21
-
22
- ==== Description
23
-
24
- This is only a test.
25
-
26
-
27
- [id="plugins-{type}s-{plugin}-common-options"]
28
- include::{include_path}/{type}.asciidoc[]
29
-
30
- :default_codec!:
@@ -1,147 +0,0 @@
1
- require 'openssl'
2
- require "logstash/util/loggable"
3
-
4
- # Simulate a normal SslOptions builder:
5
- #
6
- # ssl_context = SslOptions.builder
7
- # .set_is_ssl_enabled(@ssl_enable)
8
- # .set_should_verify(@ssl_verify)
9
- # .set_ssl_cert(@ssl_cert)
10
- # .set_ssl_key(@ssl_key)
11
- # .set_ssl_key_passphrase(@ssl_key_passphrase.value)
12
- # .set_ssl_extra_chain_certs(@ssl_extra_chain_certs.to_java(:string))
13
- # .set_ssl_certificate_authorities(@ssl_certificate_authorities.to_java(:string))
14
- # .build.toSslContext()
15
- class SslOptions
16
- include LogStash::Util::Loggable
17
-
18
- java_import 'io.netty.handler.ssl.ClientAuth'
19
- java_import 'io.netty.handler.ssl.SslContextBuilder'
20
- java_import 'java.security.cert.X509Certificate'
21
- java_import 'javax.crypto.Cipher'
22
- java_import 'org.bouncycastle.asn1.pkcs.PrivateKeyInfo'
23
- java_import 'org.bouncycastle.jce.provider.BouncyCastleProvider'
24
- java_import 'org.bouncycastle.openssl.PEMKeyPair'
25
- java_import 'org.bouncycastle.openssl.PEMParser'
26
- java_import 'org.bouncycastle.openssl.PEMEncryptedKeyPair'
27
- java_import 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter'
28
- java_import 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder'
29
- java_import 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder'
30
- java_import 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo'
31
-
32
- def self.builder
33
- new
34
- end
35
-
36
- def set_is_ssl_enabled(boolean)
37
- @ssl_enabled = boolean
38
- self
39
- end
40
-
41
- def set_should_verify(boolean)
42
- @ssl_verify = boolean
43
- self
44
- end
45
-
46
- def set_ssl_cert(path)
47
- @ssl_cert_path = path
48
- self
49
- end
50
-
51
- def set_ssl_key(path)
52
- @ssl_key_path = path
53
- self
54
- end
55
-
56
- def set_ssl_key_passphrase(passphrase)
57
- @ssl_key_passphrase = passphrase
58
- self
59
- end
60
-
61
- def set_ssl_extra_chain_certs(certs)
62
- @ssl_extra_chain_certs = certs
63
- self
64
- end
65
-
66
- def set_ssl_certificate_authorities(certs)
67
- @ssl_certificate_authorities = certs
68
- self
69
- end
70
-
71
- def build; self; end
72
-
73
- def toSslContext
74
- return nil unless @ssl_enabled
75
-
76
- # Check key strength
77
- logger.warn("JCE Unlimited Strength Jurisdiction Policy not installed - max key length is 128 bits") unless Cipher.getMaxAllowedKeyLength("AES") > 128
78
- # create certificate object
79
- cf = java.security.cert.CertificateFactory.getInstance("X.509")
80
- cert_chain = []
81
- fetch_certificates_from_file(@ssl_cert_path, cf) do |cert|
82
- cert_chain << cert
83
- end
84
-
85
- # convert key from pkcs1 to pkcs8 and get PrivateKey object
86
- pem_parser = PEMParser.new(java.io.FileReader.new(@ssl_key_path))
87
- java.security.Security.addProvider(BouncyCastleProvider.new)
88
- converter = JcaPEMKeyConverter.new
89
- case obj = pem_parser.readObject
90
- when PEMKeyPair # unencrypted pkcs#1
91
- private_key = converter.getKeyPair(obj).private
92
- when PrivateKeyInfo # unencrypted pkcs#8
93
- private_key = converter.getPrivateKey(obj)
94
- when PEMEncryptedKeyPair # encrypted pkcs#1
95
- key_char_array = @ssl_key_passphrase.to_java.toCharArray
96
- decryptor = JcePEMDecryptorProviderBuilder.new.build(key_char_array)
97
- key_pair = obj.decryptKeyPair(decryptor)
98
- private_key = converter.getKeyPair(key_pair).private
99
- when PKCS8EncryptedPrivateKeyInfo # encrypted pkcs#8
100
- key_char_array = @ssl_key_passphrase.to_java.toCharArray
101
- key = JceOpenSSLPKCS8DecryptorProviderBuilder.new.build(key_char_array)
102
- private_key = converter.getPrivateKey(obj.decryptPrivateKeyInfo(key))
103
- else
104
- raise "Could not recognize 'ssl_key' format. Class: #{obj.class}"
105
- end
106
-
107
- @ssl_extra_chain_certs.each do |file|
108
- fetch_certificates_from_file(file, cf) do |cert|
109
- cert_chain << cert
110
- end
111
- end
112
- sslContextBuilder = SslContextBuilder.forServer(private_key, @ssl_key_passphrase, cert_chain.to_java(X509Certificate))
113
-
114
- trust_certs = []
115
-
116
- @ssl_certificate_authorities.each do |file|
117
- fetch_certificates_from_file(file, cf) do |cert|
118
- trust_certs << cert
119
- end
120
- end
121
-
122
- if trust_certs.any?
123
- sslContextBuilder.trustManager(trust_certs.to_java(X509Certificate))
124
- end
125
-
126
- sslContextBuilder.clientAuth(@ssl_verify ? ClientAuth::REQUIRE : ClientAuth::NONE)
127
- sslContextBuilder.build()
128
- end
129
-
130
- private
131
- def fetch_certificates_from_file(file, cf)
132
- fis = java.io.FileInputStream.new(file)
133
-
134
- while (fis.available > 0) do
135
- cert = generate_certificate(cf, fis)
136
- yield cert if cert
137
- end
138
- ensure
139
- fis.close if fis
140
- end
141
-
142
- def generate_certificate(cf, fis)
143
- cf.generateCertificate(fis)
144
- rescue Java::JavaSecurityCert::CertificateException => e
145
- raise e unless e.cause.message == "Empty input"
146
- end
147
- end