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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e842feb71aa235b190d3ecc62267f82250d66bd1
4
- data.tar.gz: 82c27027f6f482b7959a8b2c8db6641a99830dab
3
+ metadata.gz: 2b44b745b034e4879863dafa18e2a47f6c5077cb
4
+ data.tar.gz: be9744dfb679a87d2c62d9c70d0556c5a4101e91
5
5
  SHA512:
6
- metadata.gz: 5f1a5d58c37d7e4ea6adb24fbed54a85ce486257b3ab827b01ae9da11dfb644598d0e37301f8dd0c14aef4aaa81ed8229bab865bcd2b25aca012c9bbd8959768
7
- data.tar.gz: b34f6a828c699c17aa4b7f00b6028c79104076369e5d48e3e1bd1a0afcb1ee38fd4232af34c3f2bc40b37830a1dfd90a5f14270cda482676a1b3adef5cd7561c
6
+ metadata.gz: c9a92bec0e4ae62083feb22c6736c474b31fe49b3853f6ae48c9dcd5d71259ac38da73fc340853f171f5fc3f4167bcf71a5e99071e5aee4d1fb8c8667ad94514
7
+ data.tar.gz: aadca9c26ab53386668cd0e4544178d0485cdd0d2e29546dc8bd261593a1711930a3ff598ab7c9c57f8a672cee34d82fab31a2ba25c4edad6e945349df55cc17
@@ -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
- @lumberjack = Lumberjack::Beats::Server.new(:address => @host, :port => @port,
91
- :ssl => @ssl, :ssl_certificate => @ssl_certificate, :ssl_key => @ssl_key,
92
- :ssl_key_passphrase => @ssl_key_passphrase)
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] = [@opts[:addresses]] if @opts[:addresses].class == String
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
- raise "Must set a ssl certificate or path" if @opts[:ssl_certificate].nil? && @opts[:ssl]
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(opts)
86
+ connection_start
77
87
  end
78
88
 
79
89
  private
80
- def connection_start(opts)
81
- tcp_socket = TCPSocket.new(opts[:address], opts[:port])
82
- if !opts[:ssl]
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
- certificate_store = OpenSSL::X509::Store.new
88
- certificate_store.add_cert(certificate)
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
- ssl_context = OpenSSL::SSL::SSLContext.new
91
- ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
92
- ssl_context.cert_store = certificate_store
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
- @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
95
- @socket.connect
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
- [:ssl_certificate, :ssl_key].each do |k|
37
- if @options[k].nil?
38
- raise "You must specify #{k} in Lumberjack::Server.new(...)"
39
- end
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
- # Query the port in case the port number is '0'
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
- end # class Server
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
- SUPPORTED_PROTOCOLS.include?(version)
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.1.4"
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