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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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