spec/integration_spec.rb in logstash-input-beats-2.1.4 vs spec/integration_spec.rb in logstash-input-beats-2.2.0
- old
+ new
@@ -4,32 +4,52 @@
require "stud/temporary"
require "flores/pki"
require "fileutils"
require "thread"
require "spec_helper"
+require_relative "./support/file_helpers"
+require_relative "./support/flores_extensions"
Thread.abort_on_exception = true
describe "A client" do
+ include FileHelpers
+
let(:certificate) { Flores::PKI.generate }
- let(:certificate_file_crt) { "certificate.crt" }
- let(:certificate_file_key) { "certificate.key" }
+ let(:certificate_file_crt) do
+ p = Stud::Temporary.file
+ p.write(certificate.first.to_s)
+ p.close
+ p.path
+ end
+
+ let(:certificate_file_key) do
+ p = Stud::Temporary.file
+ p.write(certificate.last.to_s)
+ p.close
+ p.path
+ end
let(:port) { Flores::Random.integer(1024..65335) }
let(:tcp_port) { port + 1 }
let(:host) { "127.0.0.1" }
let(:queue) { [] }
+ let(:config_ssl) do
+ {
+ :port => port,
+ :address => host,
+ :ssl_certificate => certificate_file_crt,
+ :ssl_key => certificate_file_key
+ }
+ end
+ let(:config_tcp) do
+ { :port => tcp_port, :address => host, :ssl => false }
+ end
+
before :each do
- expect(File).to receive(:read).at_least(1).with(certificate_file_crt) { certificate.first.to_s }
- expect(File).to receive(:read).at_least(1).with(certificate_file_key) { certificate.last.to_s }
+ tcp_server = Lumberjack::Beats::Server.new(config_tcp)
+ ssl_server = Lumberjack::Beats::Server.new(config_ssl)
- tcp_server = Lumberjack::Beats::Server.new(:port => tcp_port, :address => host, :ssl => false)
-
- ssl_server = Lumberjack::Beats::Server.new(:port => port,
- :address => host,
- :ssl_certificate => certificate_file_crt,
- :ssl_key => certificate_file_key)
-
@tcp_server = Thread.new do
tcp_server.run { |data, identity_stream| queue << [data, identity_stream] }
while true
tcp_server.accept do |socket|
next if socket.nil?
@@ -40,11 +60,11 @@
rescue
# Close connection on failure. For example SSL client will make
# parser for TCP based server trip.
# Connection is closed by Server connection object
end
- end
+ end
end
end
@ssl_server = Thread.new do
ssl_server.run { |data, identity_stream| queue << [data, identity_stream] }
@@ -158,64 +178,336 @@
include_examples "transmit payloads"
end
end
end
- context "using ssl encrypted connection" do
- context "with a valid certificate" do
- it "successfully connect to the server" do
- expect {
- Lumberjack::Beats::Client.new(:port => port,
- :host => host,
- :addresses => host,
- :ssl_certificate => certificate_file_crt)
- }.not_to raise_error
- end
+ context "using TLS/SSL encrypted connection" do
+ context "without a ca chain" do
+ context "with a valid certificate" do
+ it "successfully connect to the server" do
+ expect {
+ Lumberjack::Beats::Client.new(:port => port,
+ :host => host,
+ :addresses => host,
+ :ssl_certificate_authorities => certificate_file_crt)
+ }.not_to raise_error
+ end
- it "should fail connecting to plain tcp server" do
- expect {
- Lumberjack::Beats::Client.new(:port => tcp_port,
- :host => host,
- :addresses => host,
- :ssl_certificate => certificate_file_crt)
- }.to raise_error(OpenSSL::SSL::SSLError)
+ it "should fail connecting to plain tcp server" do
+ expect {
+ Lumberjack::Beats::Client.new(:port => tcp_port,
+ :host => host,
+ :addresses => host,
+ :ssl_certificate_authorities => certificate_file_crt)
+ }.to raise_error(OpenSSL::SSL::SSLError)
+ end
end
- end
- context "with an invalid certificate" do
- let(:invalid_certificate) { Flores::PKI.generate }
- let(:invalid_certificate_file) { "invalid.crt" }
+ context "with an invalid certificate" do
+ let(:invalid_certificate) { Flores::PKI.generate }
+ let(:invalid_certificate_file) { Stud::Temporary.file.path }
- before do
- expect(File).to receive(:read).with(invalid_certificate_file) { invalid_certificate.first.to_s }
+ it "should refuse to connect" do
+ expect {
+ Lumberjack::Beats::Client.new(:port => port,
+ :host => host,
+ :addresses => host,
+ :ssl_certificate_authorities => invalid_certificate_file)
+
+ }.to raise_error(OpenSSL::SSL::SSLError, /certificate verify failed/)
+ end
end
- it "should refuse to connect" do
- expect {
+ context "When transmitting a payload" do
+ let(:client) do
Lumberjack::Beats::Client.new(:port => port,
:host => host,
:addresses => host,
- :ssl_certificate => invalid_certificate_file)
+ :ssl_certificate_authorities => certificate_file_crt)
+ end
- }.to raise_error(OpenSSL::SSL::SSLError, /certificate verify failed/)
+ context "json" do
+ let(:options) { super.merge({ :json => true }) }
+ include_examples "transmit payloads"
+ end
+
+ context "v1 frame" do
+ include_examples "transmit payloads"
+ end
end
end
+ end
- context "When transmitting a payload" do
- let(:client) do
- Lumberjack::Beats::Client.new(:port => port,
- :host => host,
- :addresses => host,
- :ssl_certificate => certificate_file_crt)
+ def write_to_tmp_file(content)
+ file = Stud::Temporary.file
+ file.write(content.to_s)
+ file.close
+ file.path
+ end
+
+ context "Mutual validation" do
+ let(:host) { "localhost" }
+ let(:certificate_duration) { Flores::Random.number(1000..2000) }
+ let(:client) { Lumberjack::Beats::Client.new(options) }
+ let(:options) { { :ssl => true, :port => port, :addresses => host } }
+ let(:config_ssl) do
+ {
+ :port => port,
+ :address => host,
+ :ssl => true,
+ :ssl_verify_mode => "force_peer"
+ }
+ end
+
+ context "with a self signed certificate" do
+ let(:certificate) { Flores::PKI.generate }
+ let_tmp_file(:certificate_file) { certificate.first }
+ let_tmp_file(:certificate_key_file) { certificate.last }
+ let(:options) do
+ super.merge({ :ssl_certificate => certificate_file,
+ :ssl_certificate_key => certificate_key_file,
+ :ssl_certificate_authorities => certificate_file })
end
- context "json" do
- let(:options) { super.merge({ :json => true }) }
+ let(:config_ssl) do
+ super.merge({ :ssl_certificate_authorities => certificate_file,
+ :ssl_certificate => certificate_file,
+ :ssl_key => certificate_key_file })
+ end
+
+ include_examples "transmit payloads"
+ end
+
+ context "with certificate authorities" do
+ let(:keysize) { 1024 }
+ let(:exponent) { 65537 }
+ let(:root_ca) { Flores::PKI.generate("CN=root.logstash") }
+ let(:root_ca_certificate) { root_ca.first }
+ let(:root_ca_key) { root_ca.last }
+ let_tmp_file(:root_ca_certificate_file) { root_ca_certificate }
+
+ context "directly signing a certificate" do
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
+ let(:client_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = client_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = root_ca_key
+ csr.signing_certificate = root_ca_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:client_certificate_file) { client_certificate }
+
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let(:server_certificate_key_file) { write_to_tmp_file(server_certificate_key) }
+ let(:server_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = server_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = root_ca_key
+ csr.signing_certificate = root_ca_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:server_certificate_file) { server_certificate }
+
+ let(:options) do
+ super.merge({ :ssl_certificate => client_certificate_file,
+ :ssl_certificate_key => client_certificate_key_file,
+ :ssl_certificate_authorities => root_ca_certificate_file })
+ end
+
+ let(:config_ssl) do
+ super.merge({ :ssl_certificate_authorities => root_ca_certificate_file,
+ :ssl_certificate => server_certificate_file,
+ :ssl_key => server_certificate_key_file,
+ :verify_mode => :force_peer })
+ end
+
include_examples "transmit payloads"
end
- context "v1 frame" do
+ # Doesnt work because of this issues in `jruby-openssl`
+ # https://github.com/jruby/jruby-openssl/issues/84
+ xcontext "CA given with client providing intermediate and client certificate" do
+ let(:intermediate_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:intermediate_certificate_key_file) { intermediate_certificate_key }
+ let(:intermediate_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = intermediate_certificate_key.public_key
+ csr.subject = "CN=intermediate 1"
+ csr.signing_key = root_ca_key
+ csr.signing_certificate = root_ca_certificate
+ csr.want_signature_ability = true
+ csr.create
+ end
+ let_tmp_file(:intermediate_certificate_file) { intermediate_certificate }
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
+ let(:client_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = client_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = intermediate_certificate_key
+ csr.signing_certificate = intermediate_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:client_certificate_file) { client_certificate }
+
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:server_certificate_key_file) { server_certificate_key }
+ let(:server_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = server_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = intermediate_certificate_key
+ csr.signing_certificate = intermediate_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:server_certificate_file) { server_certificate }
+ let_tmp_file(:certificate_chain_file) { Flores::PKI.chain_certificates(root_ca_certificate, intermediate_certificate) }
+
+ let(:options) do
+ super.merge({ :ssl_certificate => client_certificate_file,
+ :ssl_certificate_key => client_certificate_key_file,
+ :ssl_certificate_authorities => certificate_chain_file })
+ end
+
+ let(:config_ssl) do
+ super.merge({ :ssl_certificate_authorities => certificate_chain_file,
+ :ssl_certificate => server_certificate_file,
+ :ssl_key => server_certificate_key_file,
+ :verify_mode => :force_peer })
+
+
+ end
+
include_examples "transmit payloads"
+ end
+
+ context "Mutiple CA different clients" do
+ let(:secondary_root_ca) { Flores::PKI.generate("CN=secondary.root.logstash") }
+ let(:secondary_root_ca_certificate) { root_ca.first }
+ let(:secondary_root_ca_key) { root_ca.last }
+ let_tmp_file(:secondary_root_ca_certificate_file) { root_ca_certificate }
+
+ let(:secondary_client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let(:secondary_client_certificate_key_file) { write_to_tmp_file(secondary_client_certificate_key) }
+ let(:secondary_client_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = secondary_client_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = secondary_root_ca_key
+ csr.signing_certificate = secondary_root_ca_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:secondary_client_certificate_file) { secondary_client_certificate }
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
+ let(:client_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = client_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = root_ca_key
+ csr.signing_certificate = root_ca_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:client_certificate_file) { client_certificate }
+
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
+ let_tmp_file(:server_certificate_key_file) { server_certificate_key }
+ let(:server_certificate) do
+ csr = Flores::PKI::CertificateSigningRequest.new
+ csr.start_time = Time.now
+ csr.expire_time = csr.start_time + certificate_duration
+ csr.public_key = server_certificate_key.public_key
+ csr.subject = "CN=localhost"
+ csr.signing_key = root_ca_key
+ csr.signing_certificate = root_ca_certificate
+ csr.want_signature_ability = false
+ csr.create
+ end
+ let_tmp_file(:server_certificate_file) { server_certificate }
+
+ shared_examples "multiples client" do
+ context "client from primary CA" do
+ let(:options) do
+ super.merge({ :ssl_certificate => client_certificate_file,
+ :ssl_certificate_key => client_certificate_key_file,
+ :ssl_certificate_authorities => [root_ca_certificate_file] })
+ end
+
+ include_examples "transmit payloads"
+ end
+
+ context "client from secondary CA" do
+ let(:options) do
+ super.merge({ :ssl_certificate => secondary_client_certificate_file,
+ :ssl_certificate_key => secondary_client_certificate_key_file,
+ :ssl_certificate_authorities => [root_ca_certificate_file] })
+ end
+
+ include_examples "transmit payloads"
+ end
+ end
+
+ context "when the CA are defined individually in the configuration" do
+ let(:config_ssl) do
+ super.merge({ :ssl_certificate_authorities => [secondary_root_ca_certificate_file, root_ca_certificate_file],
+ :ssl_certificate => server_certificate_file,
+ :ssl_key => server_certificate_key_file,
+ :verify_mode => "force_peer" })
+ end
+
+ include_examples "multiples client"
+ end
+
+ context "when the CA are defined by a path in the configuration" do
+ let(:ca_path) do
+ p = Stud::Temporary.directory
+
+ # Doing this here make sure the path is populated when the server
+ # is started in the main before block
+ FileUtils.mkdir_p(p)
+ FileUtils.cp(secondary_root_ca_certificate_file, p)
+ FileUtils.cp(root_ca_certificate_file, p)
+
+ p
+ end
+
+ after :each do
+ FileUtils.rm_rf(ca_path)
+ end
+
+ let(:config_ssl) do
+ super.merge({ :ssl_certificate_authorities => ca_path,
+ :ssl_certificate => server_certificate_file,
+ :ssl_key => server_certificate_key_file,
+ :verify_mode => "force_peer" })
+ end
+
+ include_examples "multiples client"
+ end
end
end
end
end