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