logstash-input-beats 2.1.4 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/logstash/inputs/beats.rb +38 -10
- data/lib/lumberjack/beats/client.rb +65 -16
- data/lib/lumberjack/beats/server.rb +99 -26
- data/logstash-input-beats.gemspec +2 -1
- data/spec/integration/filebeat_spec.rb +235 -0
- data/spec/integration/logstash_forwarder_spec.rb +102 -0
- data/spec/integration_spec.rb +340 -48
- data/spec/support/client_process_helpers.rb +27 -0
- data/spec/support/file_helpers.rb +61 -0
- data/spec/support/flores_extensions.rb +42 -0
- data/spec/support/integration_shared_context.rb +55 -0
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b44b745b034e4879863dafa18e2a47f6c5077cb
|
4
|
+
data.tar.gz: be9744dfb679a87d2c62d9c70d0556c5a4101e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9a92bec0e4ae62083feb22c6736c474b31fe49b3853f6ae48c9dcd5d71259ac38da73fc340853f171f5fc3f4167bcf71a5e99071e5aee4d1fb8c8667ad94514
|
7
|
+
data.tar.gz: aadca9c26ab53386668cd0e4544178d0485cdd0d2e29546dc8bd261593a1711930a3ff598ab7c9c57f8a672cee34d82fab31a2ba25c4edad6e945349df55cc17
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# 2.2.0
|
2
|
+
- The server can now do client side verification by providing a list of certificate authorities and configuring the `ssl_verify_mode`,
|
3
|
+
the server can use `peer`, if the client send a certificate it will be validated. Using `force_peer` will make sure the client provide a certificate
|
4
|
+
and it will be validated with the know CA. #8
|
1
5
|
# 2.1.4
|
2
6
|
- Change the `logger#warn` for `logger.debug` when a peer get disconnected, keep alive check from proxy can generate a lot of logs #46
|
3
7
|
# 2.1.3
|
@@ -68,6 +68,27 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
|
|
68
68
|
# SSL key passphrase to use.
|
69
69
|
config :ssl_key_passphrase, :validate => :password
|
70
70
|
|
71
|
+
# Validate client certificates against theses authorities
|
72
|
+
# You can defined multiples files or path, all the certificates will
|
73
|
+
# be read and added to the trust store. You need to configure the `ssl_verify_mode`
|
74
|
+
# to `peer` or `force_peer` to enable the verification.
|
75
|
+
#
|
76
|
+
# This feature only support certificate directly signed by your root ca.
|
77
|
+
# Intermediate CA are currently not supported.
|
78
|
+
#
|
79
|
+
config :ssl_certificate_authorities, :validate => :array, :default => []
|
80
|
+
|
81
|
+
# By default the server dont do any client verification,
|
82
|
+
#
|
83
|
+
# `peer` will make the server ask the client to provide a certificate,
|
84
|
+
# if the client provide the certificate it will be validated.
|
85
|
+
#
|
86
|
+
# `force_peer` will make the server ask the client for their certificate, if the clients
|
87
|
+
# doesn't provide it the connection will be closed.
|
88
|
+
#
|
89
|
+
# This option need to be used with `ssl_certificate_authorities` and a defined list of CA.
|
90
|
+
config :ssl_verify_mode, :validate => ["none", "peer", "force_peer"], :default => "none"
|
91
|
+
|
71
92
|
# The number of seconds before we raise a timeout,
|
72
93
|
# this option is useful to control how much time to wait if something is blocking the pipeline.
|
73
94
|
config :congestion_threshold, :validate => :number, :default => 5
|
@@ -87,9 +108,16 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
|
|
87
108
|
end
|
88
109
|
|
89
110
|
@logger.info("Beats inputs: Starting input listener", :address => "#{@host}:#{@port}")
|
90
|
-
|
91
|
-
|
92
|
-
|
111
|
+
|
112
|
+
|
113
|
+
@lumberjack = Lumberjack::Beats::Server.new(:address => @host,
|
114
|
+
:port => @port,
|
115
|
+
:ssl => @ssl,
|
116
|
+
:ssl_certificate => @ssl_certificate,
|
117
|
+
:ssl_key => @ssl_key,
|
118
|
+
:ssl_key_passphrase => @ssl_key_passphrase,
|
119
|
+
:ssl_certificate_authorities => @ssl_certificate_authorities,
|
120
|
+
:ssl_verify_mode => @ssl_verify_mode)
|
93
121
|
|
94
122
|
# in 1.5 the main SizeQueue doesnt have the concept of timeout
|
95
123
|
# We are using a small plugin buffer to move events to the internal queue
|
@@ -103,8 +131,8 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
|
|
103
131
|
@codec = LogStash::Codecs::IdentityMapCodec.new(@codec)
|
104
132
|
|
105
133
|
# Keep a list of active connections so we can flush their codec on shutdown
|
106
|
-
|
107
|
-
# Use threadsafe gem, since we have a strict dependency on concurrent-ruby 0.9.2
|
134
|
+
|
135
|
+
# Use threadsafe gem, since we have a strict dependency on concurrent-ruby 0.9.2
|
108
136
|
# in the core
|
109
137
|
@connections_list = ThreadSafe::Hash.new
|
110
138
|
end # def register
|
@@ -127,13 +155,13 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
|
|
127
155
|
connection = @lumberjack.accept # call that creates a new connection
|
128
156
|
# if the connection is nil the connection was closed upstream,
|
129
157
|
# so we will try in another iteration to recover or stop.
|
130
|
-
next if connection.nil?
|
158
|
+
next if connection.nil?
|
131
159
|
|
132
|
-
Thread.new do
|
160
|
+
Thread.new do
|
133
161
|
handle_new_connection(connection)
|
134
162
|
end
|
135
163
|
else
|
136
|
-
@logger.warn("Beats input: the pipeline is blocked, temporary refusing new connection.",
|
164
|
+
@logger.warn("Beats input: the pipeline is blocked, temporary refusing new connection.",
|
137
165
|
:reconnect_backoff_sleep => RECONNECT_BACKOFF_SLEEP)
|
138
166
|
sleep(RECONNECT_BACKOFF_SLEEP)
|
139
167
|
end
|
@@ -186,9 +214,9 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
|
|
186
214
|
@logger.warn("Beats input: The circuit breaker has detected a slowdown or stall in the pipeline, the input is closing the current connection and rejecting new connection until the pipeline recover.",
|
187
215
|
:exception => e.class)
|
188
216
|
rescue Exception => e # If we have a malformed packet we should handle that so the input doesn't crash completely.
|
189
|
-
@logger.error("Beats input: unhandled exception",
|
217
|
+
@logger.error("Beats input: unhandled exception",
|
190
218
|
:exception => e,
|
191
|
-
:backtrace => e.backtrace)
|
219
|
+
:backtrace => e.backtrace)
|
192
220
|
ensure
|
193
221
|
transformer = LogStash::Inputs::BeatsSupport::EventTransformCommon.new(self)
|
194
222
|
|
@@ -12,14 +12,21 @@ module Lumberjack module Beats
|
|
12
12
|
:port => 0,
|
13
13
|
:addresses => [],
|
14
14
|
:ssl_certificate => nil,
|
15
|
+
:ssl_certificate_key => nil,
|
16
|
+
:ssl_certificate_authorities => nil,
|
15
17
|
:ssl => true,
|
16
18
|
:json => false,
|
17
19
|
}.merge(opts)
|
18
20
|
|
19
|
-
@opts[:addresses] =
|
21
|
+
@opts[:addresses] = Array(@opts[:addresses])
|
20
22
|
raise "Must set a port." if @opts[:port] == 0
|
21
23
|
raise "Must set atleast one address" if @opts[:addresses].empty? == 0
|
22
|
-
|
24
|
+
|
25
|
+
if @opts[:ssl]
|
26
|
+
if @opts[:ssl_certificate_authorities].nil? && (@opts[:ssl_certificate].nil? || @opts[:ssl_certificate_key].nil?)
|
27
|
+
raise "Must set a ssl certificate or path"
|
28
|
+
end
|
29
|
+
end
|
23
30
|
|
24
31
|
@socket = connect
|
25
32
|
end
|
@@ -67,36 +74,78 @@ module Lumberjack module Beats
|
|
67
74
|
@opts = {
|
68
75
|
:port => 0,
|
69
76
|
:address => "127.0.0.1",
|
77
|
+
:ssl_certificate_authorities => [], # use the same naming as beats' TLS options
|
70
78
|
:ssl_certificate => nil,
|
79
|
+
:ssl_certificate_key => nil,
|
80
|
+
:ssl_certificate_password => nil,
|
71
81
|
:ssl => true,
|
72
82
|
:json => false,
|
73
83
|
}.merge(opts)
|
74
84
|
@host = @opts[:address]
|
75
85
|
|
76
|
-
connection_start
|
86
|
+
connection_start
|
77
87
|
end
|
78
88
|
|
79
89
|
private
|
80
|
-
def connection_start
|
81
|
-
tcp_socket = TCPSocket.new(opts[:address], opts[:port])
|
82
|
-
|
90
|
+
def connection_start
|
91
|
+
tcp_socket = TCPSocket.new(@opts[:address], @opts[:port])
|
92
|
+
|
93
|
+
if !@opts[:ssl]
|
83
94
|
@socket = tcp_socket
|
84
95
|
else
|
85
|
-
certificate = OpenSSL::X509::Certificate.new(File.read(opts[:ssl_certificate]))
|
86
96
|
|
87
|
-
|
88
|
-
|
97
|
+
@socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, setup_ssl)
|
98
|
+
@socket.connect
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def setup_ssl
|
104
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
89
105
|
|
90
|
-
|
91
|
-
|
92
|
-
|
106
|
+
ssl_context.cert = certificate
|
107
|
+
ssl_context.key = private_key
|
108
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
109
|
+
ssl_context.cert_store = trust_store
|
110
|
+
ssl_context
|
111
|
+
end
|
93
112
|
|
94
|
-
|
95
|
-
|
113
|
+
private
|
114
|
+
def certificate
|
115
|
+
if @opts[:ssl_certificate]
|
116
|
+
OpenSSL::X509::Certificate.new(File.open(@opts[:ssl_certificate]))
|
96
117
|
end
|
97
118
|
end
|
98
119
|
|
99
|
-
private
|
120
|
+
private
|
121
|
+
def private_key
|
122
|
+
OpenSSL::PKey::RSA.new(File.read(@opts[:ssl_certificate_key]), @opts[:ssl_certificate_password]) if @opts[:ssl_certificate_key]
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def trust_store
|
127
|
+
store = OpenSSL::X509::Store.new
|
128
|
+
|
129
|
+
Array(@opts[:ssl_certificate_authorities]).each do |certificate_authority|
|
130
|
+
if File.file?(certificate_authority)
|
131
|
+
store.add_file(certificate_authority)
|
132
|
+
else
|
133
|
+
# add_path is no implemented under jruby
|
134
|
+
# so recursively try to load all the certificate from this directory
|
135
|
+
# https://github.com/jruby/jruby-openssl/blob/master/src/main/java/org/jruby/ext/openssl/X509Store.java#L159
|
136
|
+
if !!(RUBY_PLATFORM == "java")
|
137
|
+
Dir.glob(File.join(certificate_authority, "**", "*")).each { |f| store.add_file(f) }
|
138
|
+
else
|
139
|
+
store.add_path(certificate_authority)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
store
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
private
|
100
149
|
def inc
|
101
150
|
@sequence = 0 if @sequence + 1 > Lumberjack::Beats::SEQUENCE_MAX
|
102
151
|
@sequence = @sequence + 1
|
@@ -134,7 +183,7 @@ module Lumberjack module Beats
|
|
134
183
|
ack(elements.size)
|
135
184
|
end
|
136
185
|
|
137
|
-
private
|
186
|
+
private
|
138
187
|
def compress_payload(payload)
|
139
188
|
compress = Zlib::Deflate.deflate(payload)
|
140
189
|
["1", "C", compress.bytesize, compress].pack("AANA*")
|
@@ -29,35 +29,33 @@ module Lumberjack module Beats
|
|
29
29
|
:ssl => true,
|
30
30
|
:ssl_certificate => nil,
|
31
31
|
:ssl_key => nil,
|
32
|
-
:ssl_key_passphrase => nil
|
32
|
+
:ssl_key_passphrase => nil,
|
33
|
+
:ssl_certificate_authorities => nil,
|
34
|
+
:ssl_verify_mode => :none # By default we dont verify client
|
33
35
|
}.merge(options)
|
34
36
|
|
35
37
|
if @options[:ssl]
|
36
|
-
[:
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
if verify_client?(@options[:ssl_verify_mode]) && certificate_authorities.empty?
|
39
|
+
raise "When `ssl_verify_mode` is set to `peer` OR `force_peer` you need to specify the `ssl_certificate_authorities`"
|
40
|
+
end
|
41
|
+
|
42
|
+
if !verify_client?(@options[:ssl_verify_mode]) && certificate_authorities.size > 0
|
43
|
+
raise "When `ssl_certificate_authorities` is configured you need to set `ssl_verify_mode` to either `peer` or `force_peer`"
|
44
|
+
end
|
45
|
+
|
46
|
+
if @options[:ssl_certificate].nil? || @options[:ssl_key].nil?
|
47
|
+
raise "You must specify `ssl_certificate` AND `ssl_key`"
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
43
51
|
@server = TCPServer.new(@options[:address], @options[:port])
|
44
|
-
|
45
52
|
@close = Concurrent::AtomicBoolean.new
|
53
|
+
@port = retrieve_current_port
|
46
54
|
|
47
|
-
|
48
|
-
# TCPServer#addr == [ address_family, port, address, address ]
|
49
|
-
@port = @server.addr[1]
|
50
|
-
|
51
|
-
if @options[:ssl]
|
52
|
-
# load SSL certificate
|
53
|
-
@ssl = OpenSSL::SSL::SSLContext.new
|
54
|
-
@ssl.cert = OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
|
55
|
-
@ssl.key = OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]),
|
56
|
-
@options[:ssl_key_passphrase])
|
57
|
-
end
|
55
|
+
setup_ssl if ssl?
|
58
56
|
end # def initialize
|
59
57
|
|
60
|
-
# Server#run method, allow the library to manage all the connection
|
58
|
+
# Server#run method, allow the library to manage all the connection
|
61
59
|
# threads, this handing is quite minimal and don't handler
|
62
60
|
# all the possible cases deconnection/connection.
|
63
61
|
#
|
@@ -76,8 +74,8 @@ module Lumberjack module Beats
|
|
76
74
|
Thread.new(connection) do |connection|
|
77
75
|
begin
|
78
76
|
connection.run(&block)
|
79
|
-
rescue Lumberjack::Beats::Connection::ConnectionClosed
|
80
|
-
# Connection will raise a wrapped exception upstream,
|
77
|
+
rescue Lumberjack::Beats::Connection::ConnectionClosed
|
78
|
+
# Connection will raise a wrapped exception upstream,
|
81
79
|
# but if the threads are managed by the library we can simply ignore it.
|
82
80
|
#
|
83
81
|
# Note: This follow the previous behavior of the perfect silence.
|
@@ -87,7 +85,7 @@ module Lumberjack module Beats
|
|
87
85
|
end # def run
|
88
86
|
|
89
87
|
def ssl?
|
90
|
-
@ssl
|
88
|
+
@options[:ssl]
|
91
89
|
end
|
92
90
|
|
93
91
|
def accept(&block)
|
@@ -139,13 +137,88 @@ module Lumberjack module Beats
|
|
139
137
|
@close.make_true
|
140
138
|
@server.close unless @server.closed?
|
141
139
|
end
|
142
|
-
|
140
|
+
|
141
|
+
private
|
142
|
+
def verify_client?(mode)
|
143
|
+
mode = mode.to_sym
|
144
|
+
mode == :peer || mode == :force_peer
|
145
|
+
end
|
146
|
+
|
147
|
+
def retrieve_current_port
|
148
|
+
# Query the port in case the port number is '0'
|
149
|
+
# TCPServer#addr == [ address_family, port, address, address ]
|
150
|
+
@server.addr[1]
|
151
|
+
end
|
152
|
+
|
153
|
+
def certificate_authorities
|
154
|
+
Array(@options[:ssl_certificate_authorities])
|
155
|
+
end
|
156
|
+
|
157
|
+
def server_private_key
|
158
|
+
OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]), @options[:ssl_key_passphrase])
|
159
|
+
end
|
160
|
+
|
161
|
+
def server_certificate
|
162
|
+
OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
|
163
|
+
end
|
164
|
+
|
165
|
+
def verify_mode
|
166
|
+
case @options[:ssl_verify_mode].to_sym
|
167
|
+
when :none
|
168
|
+
OpenSSL::SSL::VERIFY_NONE
|
169
|
+
when :peer
|
170
|
+
OpenSSL::SSL::VERIFY_PEER
|
171
|
+
when :force_peer
|
172
|
+
OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def jruby?
|
177
|
+
RUBY_PLATFORM == "java"
|
178
|
+
end
|
179
|
+
|
180
|
+
def trust_store
|
181
|
+
store = OpenSSL::X509::Store.new
|
182
|
+
|
183
|
+
if certificate_authorities.size > 0
|
184
|
+
certificate_authorities.each do |certificate_authority|
|
185
|
+
if File.file?(certificate_authority)
|
186
|
+
store.add_file(certificate_authority)
|
187
|
+
else
|
188
|
+
# `#add_path` is not implemented under jruby
|
189
|
+
# so recursively try to load all the certificate from this directory
|
190
|
+
# https://github.com/jruby/jruby-openssl/blob/master/src/main/java/org/jruby/ext/openssl/X509Store.java#L159
|
191
|
+
if jruby?
|
192
|
+
Dir.glob(File.join(certificate_authority, "**", "*")).each { |f| store.add_file(f) }
|
193
|
+
else
|
194
|
+
store.add_path(certificate_authority)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
store
|
201
|
+
end
|
202
|
+
|
203
|
+
def setup_ssl
|
204
|
+
@ssl = OpenSSL::SSL::SSLContext.new
|
205
|
+
|
206
|
+
# @ssl.verify_callback = lambda do |preverify_ok, context|
|
207
|
+
# require "pry"
|
208
|
+
# binding.pry
|
209
|
+
# end
|
210
|
+
@ssl.cert_store = trust_store
|
211
|
+
@ssl.verify_mode = verify_mode
|
212
|
+
# @ssl.ca_file = certificate_authorities.first
|
213
|
+
@ssl.cert = server_certificate
|
214
|
+
@ssl.key = server_private_key
|
215
|
+
end
|
216
|
+
end
|
143
217
|
|
144
218
|
class Parser
|
145
219
|
PROTOCOL_VERSION_1 = "1".ord
|
146
220
|
PROTOCOL_VERSION_2 = "2".ord
|
147
221
|
|
148
|
-
SUPPORTED_PROTOCOLS = [PROTOCOL_VERSION_1, PROTOCOL_VERSION_2]
|
149
222
|
class UnsupportedProtocol < StandardError; end
|
150
223
|
|
151
224
|
def initialize
|
@@ -242,7 +315,7 @@ module Lumberjack module Beats
|
|
242
315
|
end
|
243
316
|
|
244
317
|
def supported_protocol?(version)
|
245
|
-
|
318
|
+
PROTOCOL_VERSION_2 == version || PROTOCOL_VERSION_1 == version
|
246
319
|
end
|
247
320
|
|
248
321
|
def window_size(&block)
|
@@ -313,7 +386,7 @@ module Lumberjack module Beats
|
|
313
386
|
end # class Parser
|
314
387
|
|
315
388
|
class Connection
|
316
|
-
# Wrap the original exception into a common one,
|
389
|
+
# Wrap the original exception into a common one,
|
317
390
|
# to make upstream managing and reporting easier
|
318
391
|
# But lets make sure we keep the meaning of the original exception.
|
319
392
|
class ConnectionClosed < StandardError
|
@@ -351,7 +424,7 @@ module Lumberjack module Beats
|
|
351
424
|
|
352
425
|
@server = server
|
353
426
|
@ack_handler = nil
|
354
|
-
|
427
|
+
|
355
428
|
# Fetch the details of the host before reading anything from the socket
|
356
429
|
# se we can use that information when debugging connection issues with
|
357
430
|
# remote hosts.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "logstash-input-beats"
|
3
|
-
s.version = "2.
|
3
|
+
s.version = "2.2.0"
|
4
4
|
s.licenses = ["Apache License (2.0)"]
|
5
5
|
s.summary = "Receive events using the lumberjack protocol."
|
6
6
|
s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
|
@@ -33,5 +33,6 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.add_development_dependency "rspec-wait"
|
34
34
|
s.add_development_dependency "logstash-devutils", "~> 0.0.18"
|
35
35
|
s.add_development_dependency "logstash-codec-json"
|
36
|
+
s.add_development_dependency "childprocess" # To make filebeat/LSF integration test easier to write.
|
36
37
|
end
|
37
38
|
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/beats"
|
3
|
+
require "stud/temporary"
|
4
|
+
require "flores/pki"
|
5
|
+
require "fileutils"
|
6
|
+
require "thread"
|
7
|
+
require "spec_helper"
|
8
|
+
require "yaml"
|
9
|
+
require "fileutils"
|
10
|
+
require "flores/pki"
|
11
|
+
require_relative "../support/flores_extensions"
|
12
|
+
require_relative "../support/file_helpers"
|
13
|
+
require_relative "../support/integration_shared_context"
|
14
|
+
require_relative "../support/client_process_helpers"
|
15
|
+
|
16
|
+
FILEBEAT_BINARY = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor", "filebeat", "filebeat"))
|
17
|
+
|
18
|
+
describe "Filebeat", :integration => true do
|
19
|
+
include ClientProcessHelpers
|
20
|
+
include FileHelpers
|
21
|
+
|
22
|
+
before :all do
|
23
|
+
unless File.exist?(FILEBEAT_BINARY)
|
24
|
+
raise "Cannot find `Filebeat` binary in `vendor/filebeat`. Did you run `bundle exec rake test:integration:setup` before running the integration suite?"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
include_context "beats configuration"
|
29
|
+
|
30
|
+
# Filebeat related variables
|
31
|
+
let(:cmd) { [filebeat_exec, "-c", filebeat_config_path, "-e", "-v"] }
|
32
|
+
|
33
|
+
let(:filebeat_exec) { FILEBEAT_BINARY }
|
34
|
+
|
35
|
+
let_empty_tmp_file(:registry_file)
|
36
|
+
let(:filebeat_config) do
|
37
|
+
{
|
38
|
+
"filebeat" => {
|
39
|
+
"prospectors" => [{ "paths" => [log_file], "input_type" => "log" }],
|
40
|
+
"registry_file" => registry_file,
|
41
|
+
"scan_frequency" => "1s"
|
42
|
+
},
|
43
|
+
"output" => {
|
44
|
+
"logstash" => { "hosts" => ["#{host}:#{port}"] },
|
45
|
+
"logging" => { "level" => "debug" }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
let_tmp_file(:filebeat_config_path) { YAML.dump(filebeat_config) }
|
51
|
+
before :each do
|
52
|
+
start_client
|
53
|
+
sleep(2) # give some time to FB to send somthing
|
54
|
+
stop_client
|
55
|
+
end
|
56
|
+
|
57
|
+
###########################################################
|
58
|
+
shared_context "Root CA" do
|
59
|
+
let(:root_ca) { Flores::PKI.generate("CN=root.localhost") }
|
60
|
+
let(:root_ca_certificate) { root_ca.first }
|
61
|
+
let(:root_ca_key) { root_ca.last }
|
62
|
+
let_tmp_file(:root_ca_certificate_file) { root_ca_certificate }
|
63
|
+
end
|
64
|
+
|
65
|
+
shared_context "Intermediate CA" do
|
66
|
+
let(:intermediate_ca) { Flores::PKI.create_intermediate_certificate("CN=intermediate.localhost", root_ca_certificate, root_ca_key) }
|
67
|
+
let(:intermediate_ca_certificate) { intermediate_ca.first }
|
68
|
+
let(:intermediate_ca_key) { intermediate_ca.last }
|
69
|
+
let_tmp_file(:certificate_authorities_chain) { Flores::PKI.chain_certificates(root_ca_certificate, intermediate_ca_certificate) }
|
70
|
+
end
|
71
|
+
|
72
|
+
############################################################
|
73
|
+
# Actuals tests
|
74
|
+
context "Plain TCP" do
|
75
|
+
include_examples "send events"
|
76
|
+
end
|
77
|
+
|
78
|
+
context "TLS" do
|
79
|
+
context "Server verification" do
|
80
|
+
let(:filebeat_config) do
|
81
|
+
super.merge({
|
82
|
+
"output" => {
|
83
|
+
"logstash" => {
|
84
|
+
"hosts" => ["#{host}:#{port}"],
|
85
|
+
"tls" => { "certificate_authorities" => certificate_authorities }
|
86
|
+
},
|
87
|
+
"logging" => { "level" => "debug" }
|
88
|
+
}})
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:input_config) do
|
92
|
+
super.merge({
|
93
|
+
"ssl" => true,
|
94
|
+
"ssl_certificate" => certificate_file,
|
95
|
+
"ssl_key" => certificate_key_file
|
96
|
+
})
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:certificate_authorities) { [certificate_file] }
|
100
|
+
let(:certificate_data) { Flores::PKI.generate }
|
101
|
+
let_tmp_file(:certificate_key_file) { certificate_data.last }
|
102
|
+
let_tmp_file(:certificate_file) { certificate_data.first }
|
103
|
+
|
104
|
+
context "self signed certificate" do
|
105
|
+
include_examples "send events"
|
106
|
+
end
|
107
|
+
|
108
|
+
context "CA root" do
|
109
|
+
include_context "Root CA"
|
110
|
+
|
111
|
+
context "directly signed client certificate" do
|
112
|
+
let(:certificate_authorities) { [root_ca_certificate_file] }
|
113
|
+
let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
|
114
|
+
|
115
|
+
include_examples "send events"
|
116
|
+
end
|
117
|
+
|
118
|
+
context "intermediate CA signs client certificate" do
|
119
|
+
include_context "Intermediate CA"
|
120
|
+
|
121
|
+
let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
|
122
|
+
let(:certificate_authorities) { [certificate_authorities_chain] }
|
123
|
+
|
124
|
+
include_examples "send events"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "Client verification / Mutual validation" do
|
129
|
+
let(:filebeat_config) do
|
130
|
+
super.merge({
|
131
|
+
"output" => {
|
132
|
+
"logstash" => {
|
133
|
+
"hosts" => ["#{host}:#{port}"],
|
134
|
+
"tls" => {
|
135
|
+
"certificate_authorities" => certificate_authorities,
|
136
|
+
"certificate" => certificate_file,
|
137
|
+
"certificate_key" => certificate_key_file
|
138
|
+
}
|
139
|
+
},
|
140
|
+
"logging" => { "level" => "debug" }
|
141
|
+
}})
|
142
|
+
end
|
143
|
+
|
144
|
+
let(:input_config) do
|
145
|
+
super.merge({
|
146
|
+
"ssl" => true,
|
147
|
+
"ssl_certificate_authorities" => certificate_authorities,
|
148
|
+
"ssl_certificate" => server_certificate_file,
|
149
|
+
"ssl_key" => server_certificate_key_file,
|
150
|
+
"ssl_verify_mode" => "force_peer"
|
151
|
+
})
|
152
|
+
end
|
153
|
+
|
154
|
+
context "with a self signed certificate" do
|
155
|
+
let(:certificate_authorities) { [certificate_file] }
|
156
|
+
let(:certificate_data) { Flores::PKI.generate }
|
157
|
+
let_tmp_file(:certificate_key_file) { certificate_data.last }
|
158
|
+
let_tmp_file(:certificate_file) { certificate_data.first }
|
159
|
+
let_tmp_file(:server_certificate_file) { certificate_data.first }
|
160
|
+
let_tmp_file(:server_certificate_key_file) { certificate_data.last }
|
161
|
+
|
162
|
+
include_examples "send events"
|
163
|
+
end
|
164
|
+
|
165
|
+
context "CA root" do
|
166
|
+
include_context "Root CA"
|
167
|
+
|
168
|
+
let_tmp_file(:server_certificate_file) { server_certificate_data.first }
|
169
|
+
let_tmp_file(:server_certificate_key_file) { server_certificate_data.last }
|
170
|
+
|
171
|
+
context "directly signed client certificate" do
|
172
|
+
let(:certificate_authorities) { [root_ca_certificate_file] }
|
173
|
+
let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
|
174
|
+
let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
|
175
|
+
|
176
|
+
include_examples "send events"
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
# Doesnt work because of this issues in `jruby-openssl`
|
181
|
+
# https://github.com/jruby/jruby-openssl/issues/84
|
182
|
+
xcontext "intermediate create server and client certificate" do
|
183
|
+
include_context "Intermediate CA"
|
184
|
+
|
185
|
+
let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
|
186
|
+
let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
|
187
|
+
let(:certificate_authorities) { [certificate_authorities_chain] }
|
188
|
+
|
189
|
+
include_examples "send events"
|
190
|
+
end
|
191
|
+
|
192
|
+
context "and Secondary CA multiples clients" do
|
193
|
+
context "with CA in different files" do
|
194
|
+
let(:secondary_ca) { Flores::PKI.generate }
|
195
|
+
let(:secondary_ca_key) { secondary_ca.last }
|
196
|
+
let(:secondary_ca_certificate) { secondary_ca.first }
|
197
|
+
let_tmp_file(:secondary_ca_certificate_file) { secondary_ca.first }
|
198
|
+
|
199
|
+
let(:secondary_client_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", secondary_ca_certificate, secondary_ca_key) }
|
200
|
+
let_tmp_file(:secondary_client_certificate_file) { secondary_client_certificate_data.first }
|
201
|
+
let_tmp_file(:secondary_client_certificate_key_file) { secondary_client_certificate_data.last }
|
202
|
+
let(:certificate_authorities) { [root_ca_certificate_file, secondary_ca_certificate_file] }
|
203
|
+
let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
|
204
|
+
|
205
|
+
let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
|
206
|
+
|
207
|
+
context "client from primary CA" do
|
208
|
+
include_examples "send events"
|
209
|
+
end
|
210
|
+
|
211
|
+
context "client from secondary CA" do
|
212
|
+
let(:filebeat_config) do
|
213
|
+
super.merge({
|
214
|
+
"output" => {
|
215
|
+
"logstash" => {
|
216
|
+
"hosts" => ["#{host}:#{port}"],
|
217
|
+
"tls" => {
|
218
|
+
"certificate_authorities" => certificate_authorities,
|
219
|
+
"certificate" => secondary_client_certificate_file,
|
220
|
+
"certificate_key" => secondary_client_certificate_key_file
|
221
|
+
}
|
222
|
+
},
|
223
|
+
"logging" => { "level" => "debug" }
|
224
|
+
}})
|
225
|
+
end
|
226
|
+
|
227
|
+
include_examples "send events"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|