logstash-input-log4j 3.0.3-java → 3.0.4-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
  SHA1:
3
- metadata.gz: a15d7c497e785b7e09b93e6f176bea3cf52a1936
4
- data.tar.gz: 3daa1c9000748d6a21f91551ccb15866444c1efa
3
+ metadata.gz: 8332669c67e8a314ad2bdbeacb8d73ce517f6164
4
+ data.tar.gz: b08d5eecf241d5205f521ed95cd3aad87b96681d
5
5
  SHA512:
6
- metadata.gz: 195c966ddab0dff4b8077a26496586584f8e333702247a38924e270213ae991243c56e77b53485409671c6af6b11d92f4a766f52583ad04360f8c23965746173
7
- data.tar.gz: e06e17f5bb17535ea3870ab98dec0e0f0b25153a70f3ad0b29048f728c360dd4b709f6cc8c8c8ddbcd7eba4f1606dd95d699ec70d38868cb1ddd3da36c19aa53
6
+ metadata.gz: d1228c327c49ebc80c2d1a3551cff839750ec6ed6c1785f766fa36378fff8287eb4642e044cf51eebaf8a01d22a1c3d130815be7e4ec8b7202e5703f33d212ad
7
+ data.tar.gz: 481d227075c191bf9c4f0d05da2cf6f319c99338cab7251c846f6a3d28a325d90e0d8b729c7cdc5412e1f7dc1f5e4c1378b2c1ddd1e528ed530cddeb21c928e6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 3.0.4
2
+ - This input will now reject any non-log4j log objects sent as input.
3
+
1
4
  ## 3.0.3
2
5
  - `data_timeout` is now marked as obsolete, since this option is no longer necessary
3
6
 
@@ -41,6 +41,10 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
41
41
  # When mode is `client`, the port to connect to.
42
42
  config :port, :validate => :number, :default => 4560
43
43
 
44
+ # Proxy protocol support, only v1 is supported at this time
45
+ # http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
46
+ config :proxy_protocol, :validate => :boolean, :default => false
47
+
44
48
  # Read timeout in seconds. If a particular TCP connection is
45
49
  # idle for more than this timeout period, we will assume
46
50
  # it is dead and close it.
@@ -57,8 +61,6 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
57
61
 
58
62
  public
59
63
  def register
60
- require "java"
61
- require "jruby/serialization"
62
64
 
63
65
  begin
64
66
  Java::OrgApacheLog4jSpi.const_get("LoggingEvent")
@@ -100,10 +102,25 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
100
102
  private
101
103
  def handle_socket(socket, output_queue)
102
104
  begin
105
+ peer = socket.peer
106
+ if @proxy_protocol
107
+ pp_hdr = socket.readline
108
+ pp_info = pp_hdr.split(/\s/)
109
+
110
+ # PROXY proto clientip proxyip clientport proxyport
111
+ if pp_info[0] != "PROXY"
112
+ @logger.error("invalid proxy protocol header label", :hdr => pp_hdr)
113
+ return
114
+ else
115
+ # would be nice to log the proxy host and port data as well, but minimizing changes
116
+ peer = pp_info[2]
117
+ end
118
+ end
103
119
  ois = socket_to_inputstream(socket)
120
+
104
121
  while !stop?
105
122
  event = create_event(ois.readObject)
106
- event.set("host", socket.peer)
123
+ event.set("host", peer)
107
124
  decorate(event)
108
125
  output_queue << event
109
126
  end # loop do
@@ -124,8 +141,7 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
124
141
 
125
142
  private
126
143
  def socket_to_inputstream(socket)
127
- # JRubyObjectInputStream uses JRuby class path to find the class to de-serialize to
128
- JRubyObjectInputStream.new(java.io.BufferedInputStream.new(socket.to_inputstream))
144
+ Log4jInputStream.new(java.io.BufferedInputStream.new(socket.to_inputstream))
129
145
  end
130
146
 
131
147
  private
@@ -178,4 +194,23 @@ class LogStash::Inputs::Log4j < LogStash::Inputs::Base
178
194
  def add_socket_mixin(socket)
179
195
  socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
180
196
  end
197
+
198
+ class Log4jInputStream < java.io.ObjectInputStream
199
+ ALLOWED_CLASSES = ["org.apache.log4j.spi.LoggingEvent"]
200
+
201
+ def initialize(*args)
202
+ super
203
+ @class_loader = org.jruby.Ruby.getGlobalRuntime.getJRubyClassLoader
204
+ end
205
+
206
+ def safety_check(name)
207
+ raise java.io.InvalidObjectException.new("Object type #{name} is not allowed.") if !ALLOWED_CLASSES.include?(name)
208
+ end
209
+
210
+ def resolveClass(desc)
211
+ name = desc.getName
212
+ safety_check(name)
213
+ java.lang.Class.forName(name, false, @class_loader)
214
+ end
215
+ end
181
216
  end # class LogStash::Inputs::Log4j
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-log4j'
4
- s.version = '3.0.3'
4
+ s.version = '3.0.4'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Read events over a TCP socket from a Log4j SocketAppender"
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"
@@ -26,5 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
27
27
 
28
28
  s.add_development_dependency 'logstash-devutils'
29
+ s.add_development_dependency 'flores'
29
30
  end
30
31
 
Binary file
@@ -1,16 +1,23 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/devutils/rspec/spec_helper"
3
+ require "socket"
3
4
  require "logstash/inputs/log4j"
4
5
  require "logstash/plugin"
6
+ require "stud/try"
7
+ require "stud/task"
8
+ require 'timeout'
9
+ require "flores/random"
10
+
11
+ # Uncomment to enable higher level logging if needed during testing.
12
+ #LogStash::Logging::Logger::configure_logging("DEBUG")
5
13
 
6
14
  describe LogStash::Inputs::Log4j do
7
15
 
8
16
  it "should register" do
9
- input = LogStash::Plugin.lookup("input", "log4j").new("mode" => "client")
10
-
17
+ plugin = LogStash::Plugin.lookup("input", "log4j").new("mode" => "client")
11
18
 
12
19
  # register will try to load jars and raise if it cannot find jars or if org.apache.log4j.spi.LoggingEvent class is not present
13
- expect {input.register}.to_not raise_error
20
+ expect {plugin.register}.to_not raise_error
14
21
  end
15
22
 
16
23
  context "when interrupting the plugin in server mode" do
@@ -35,7 +42,7 @@ describe LogStash::Inputs::Log4j do
35
42
  end
36
43
 
37
44
  context "reading general information from a org.apache.log4j.spi.LoggingEvent" do
38
- let (:input) { LogStash::Plugin.lookup("input", "log4j").new("mode" => "client") }
45
+ let (:plugin) { LogStash::Plugin.lookup("input", "log4j").new("mode" => "client") }
39
46
  let (:log_obj) {
40
47
  org.apache.log4j.spi.LoggingEvent.new(
41
48
  "org.apache.log4j.Logger",
@@ -59,7 +66,7 @@ describe LogStash::Inputs::Log4j do
59
66
  }
60
67
 
61
68
  it "creates event with general information" do
62
- subject = input.create_event(log_obj)
69
+ subject = plugin.create_event(log_obj)
63
70
  expect(subject.get("timestamp")).to eq(1426366971)
64
71
  expect(subject.get("path")).to eq("org.apache.log4j.LayoutTest")
65
72
  expect(subject.get("priority")).to eq("INFO")
@@ -74,14 +81,120 @@ describe LogStash::Inputs::Log4j do
74
81
  end
75
82
 
76
83
  it "creates event without stacktrace" do
77
- subject = input.create_event(log_obj)
84
+ subject = plugin.create_event(log_obj)
78
85
  expect(subject.get("stack_trace")).to be_nil
79
86
  end
80
87
 
81
88
  it "creates event with stacktrace" do
82
- subject = input.create_event(log_obj_with_stacktrace)
89
+ subject = plugin.create_event(log_obj_with_stacktrace)
83
90
  #checks stack_trace is collected, exact value is too monstruous
84
91
  expect(subject.get("stack_trace")).not_to be_empty
85
92
  end
86
93
  end
94
+
95
+ context "integration test" do
96
+ let(:host) { "127.0.0.1" }
97
+ let(:port) do
98
+ socket, address, port = Flores::Random.tcp_listener
99
+ socket.close
100
+ port
101
+ end
102
+
103
+ let(:config) do
104
+ {
105
+ "host" => host,
106
+ "port" => port
107
+ }
108
+ end
109
+
110
+ subject { LogStash::Inputs::Log4j.new(config) }
111
+
112
+ before do
113
+ subject.register
114
+ end
115
+
116
+ let(:thread) do
117
+ Thread.new { subject.run(queue) }
118
+ end
119
+
120
+ let(:queue) do
121
+ []
122
+ end
123
+
124
+ let(:client) do
125
+ Stud.try(5.times) { TCPSocket.new(host, port) }
126
+ end
127
+
128
+ after do
129
+ subject.do_stop
130
+
131
+ 10.times do
132
+ break unless thread.alive?
133
+ sleep(0.1)
134
+ end
135
+ expect(thread).not_to be_alive
136
+ end
137
+
138
+ shared_examples "accept events from the network" do |fixture|
139
+ before do
140
+ thread # make the thread run
141
+ File.open(fixture, "rb") do |payload|
142
+ IO.copy_stream(payload, client)
143
+ end
144
+ client.close
145
+
146
+ Stud.try(5.times) do
147
+ throw StandardError.new("queue was empty, no data?") if queue.empty?
148
+ end
149
+ expect(queue.size).to be == 1
150
+ end
151
+
152
+ it "should accept an event from the network" do
153
+ event = queue.first
154
+ expect(event.get("message")).to be == "Hello world"
155
+ end
156
+ end
157
+
158
+ context "default behavior" do
159
+ include_examples "accept events from the network", "spec/fixtures/log4j.payload"
160
+ end
161
+
162
+ context "with proxy enabled" do
163
+ let(:config) do
164
+ {
165
+ "host" => host,
166
+ "port" => port,
167
+ "proxy_protocol" => true
168
+ }
169
+ end
170
+
171
+ before do
172
+ client.write("PROXY TCP4 1.2.3.4 5.6.7.8 1234 5678\r\n")
173
+ end
174
+
175
+ include_examples "accept events from the network", "spec/fixtures/log4j.payload" do
176
+ it "should set proxy_host and proxy_port" do
177
+ event = queue.first
178
+ expect(event.get("host")).to be == "1.2.3.4"
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ context "for safety" do
185
+ let(:input) { java.lang.Integer.new(Flores::Random.integer(0..1000)) }
186
+ let(:baos) { java.io.ByteArrayOutputStream.new }
187
+ let(:oos) { java.io.ObjectOutputStream.new(baos) }
188
+ let(:data) {
189
+ oos.writeObject(input)
190
+ baos.toByteArray()
191
+ }
192
+
193
+ let(:bais) { java.io.ByteArrayInputStream.new(data) }
194
+ let(:ois) { LogStash::Inputs::Log4j::Log4jInputStream.new(bais) }
195
+
196
+ it "should reject non-log4j objects" do
197
+ expect { ois.readObject }.to raise_error(java.io.InvalidObjectException)
198
+ end
199
+ end
87
200
  end
metadata CHANGED
@@ -1,30 +1,39 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-log4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.3
4
+ version: 3.0.4
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-11 00:00:00.000000000 Z
11
+ date: 2017-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: logstash-codec-plain
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
14
20
  requirement: !ruby/object:Gem::Requirement
15
21
  requirements:
16
22
  - - ">="
17
23
  - !ruby/object:Gem::Version
18
24
  version: '0'
19
- name: logstash-codec-plain
20
25
  prerelease: false
21
26
  type: :runtime
27
+ - !ruby/object:Gem::Dependency
28
+ name: logstash-core-plugin-api
22
29
  version_requirements: !ruby/object:Gem::Requirement
23
30
  requirements:
24
31
  - - ">="
25
32
  - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
33
+ version: '1.60'
34
+ - - "<="
35
+ - !ruby/object:Gem::Version
36
+ version: '2.99'
28
37
  requirement: !ruby/object:Gem::Requirement
29
38
  requirements:
30
39
  - - ">="
@@ -33,31 +42,36 @@ dependencies:
33
42
  - - "<="
34
43
  - !ruby/object:Gem::Version
35
44
  version: '2.99'
36
- name: logstash-core-plugin-api
37
45
  prerelease: false
38
46
  type: :runtime
47
+ - !ruby/object:Gem::Dependency
48
+ name: logstash-devutils
39
49
  version_requirements: !ruby/object:Gem::Requirement
40
50
  requirements:
41
51
  - - ">="
42
52
  - !ruby/object:Gem::Version
43
- version: '1.60'
44
- - - "<="
45
- - !ruby/object:Gem::Version
46
- version: '2.99'
47
- - !ruby/object:Gem::Dependency
53
+ version: '0'
48
54
  requirement: !ruby/object:Gem::Requirement
49
55
  requirements:
50
56
  - - ">="
51
57
  - !ruby/object:Gem::Version
52
58
  version: '0'
53
- name: logstash-devutils
54
59
  prerelease: false
55
60
  type: :development
61
+ - !ruby/object:Gem::Dependency
62
+ name: flores
56
63
  version_requirements: !ruby/object:Gem::Requirement
57
64
  requirements:
58
65
  - - ">="
59
66
  - !ruby/object:Gem::Version
60
67
  version: '0'
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ prerelease: false
74
+ type: :development
61
75
  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
62
76
  email: info@elastic.co
63
77
  executables: []
@@ -73,6 +87,7 @@ files:
73
87
  - lib/logstash-input-log4j_jars.rb
74
88
  - lib/logstash/inputs/log4j.rb
75
89
  - logstash-input-log4j.gemspec
90
+ - spec/fixtures/log4j.payload
76
91
  - spec/inputs/log4j_spec.rb
77
92
  - vendor/jar-dependencies/runtime-jars/log4j-1.2.17.jar
78
93
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
@@ -102,4 +117,5 @@ signing_key:
102
117
  specification_version: 4
103
118
  summary: Read events over a TCP socket from a Log4j SocketAppender
104
119
  test_files:
120
+ - spec/fixtures/log4j.payload
105
121
  - spec/inputs/log4j_spec.rb