logstash-input-log4j 3.0.3-java → 3.0.4-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
  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