From 759fb5fe5d5d9587428160694a80e9def005eb7a Mon Sep 17 00:00:00 2001 From: Pier-Hugues Pellerin Date: Thu, 30 Jul 2015 10:15:48 -0400 Subject: [PATCH] Enforcing `VERIFY_PEER` on the client The ruby client did not enforce the validation of the certificate, opening possible man in the middle attacks on the client. This PR make sure that the `verify_mode` is set to `VERIFY_PEER` and add the certificate to the store for this specific connection. An integration test was added to validate this change. This change make the ruby client handling of connection closer to the `logstash-forwarder` behavior, which does the verify peer per default. Fixes #4 --- Gemfile | 9 +------ jls-lumberjack.gemspec | 15 +++++------ lib/lumberjack/client.rb | 14 +++++++++- spec/integration_spec.rb | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 spec/integration_spec.rb diff --git a/Gemfile b/Gemfile index 8be2464..851fabc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,2 @@ source 'https://rubygems.org' - -gem "rspec" -gem "insist" -gem "stud" -gem "fpm" -gem "pleaserun" - -gem "jruby-openssl", :platform => :jruby +gemspec diff --git a/jls-lumberjack.gemspec b/jls-lumberjack.gemspec index e275714..9c6fc05 100644 --- a/jls-lumberjack.gemspec +++ b/jls-lumberjack.gemspec @@ -5,17 +5,14 @@ Gem::Specification.new do |gem| gem.summary = gem.description gem.homepage = "https://github.com/jordansissel/lumberjack" - gem.files = %w{ - lib/lumberjack/server.rb - lib/lumberjack/client.rb - } - #lib/lumberjack/server2.rb + gem.files = Dir.glob("lib/**/*.rb") - gem.test_files = [] + gem.test_files = Dir.glob("spec/**/*.rb") gem.name = "jls-lumberjack" gem.require_paths = ["lib"] - gem.version = "0.0.22" + gem.version = "0.0.23" - # This isn't used yet because the new protocol isn't ready - #gem.add_runtime_dependency "ffi-rzmq", "~> 1.0.0" + gem.add_development_dependency "flores", "0.0.5" + gem.add_development_dependency "rspec" + gem.add_development_dependency "stud" end diff --git a/lib/lumberjack/client.rb b/lib/lumberjack/client.rb index b258a0d..634323b 100644 --- a/lib/lumberjack/client.rb +++ b/lib/lumberjack/client.rb @@ -80,7 +80,19 @@ def initialize(opts={}) private def connection_start(opts) tcp_socket = TCPSocket.new(opts[:address], opts[:port]) - @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket) + + certificate = OpenSSL::X509::Certificate.new(File.read(opts[:ssl_certificate])) + + certificate_store = OpenSSL::X509::Store.new + certificate_store.add_cert(certificate) + + ssl_context = OpenSSL::SSL::SSLContext.new + ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER + ssl_context.cert_store = certificate_store + + # ssl_context.ca_file = opts[:ssl_certificate] + + @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context) @socket.connect @socket.syswrite(["1", "W", @window_size].pack("AAN")) end diff --git a/spec/integration_spec.rb b/spec/integration_spec.rb new file mode 100644 index 0000000..80683a4 --- /dev/null +++ b/spec/integration_spec.rb @@ -0,0 +1,57 @@ +# encoding: utf-8 +require "lib/lumberjack/client" +require "lib/lumberjack/server" +require "stud/temporary" +require "flores/pki" +require "fileutils" +require "thread" +require "spec_helper" + +describe "A client" do + let(:port) { Flores::Random.integer(1024..65335) } + let(:csr) { Flores::PKI::CertificateSigningRequest.new } + let(:key_bits) { 1024 } + let(:key) { OpenSSL::PKey::RSA.generate(key_bits, 65537) } + let(:certificate_duration) { Flores::Random.number(1..86400) } + let(:certificate_subject) { "CN=server.example.com" } + let(:certificate) { csr.create } + let(:certificate_file_crt) { "certificate.crt" } + let(:certificate_file_key) { "certificate.key" } + let(:host) { "127.0.0.1" } + let(:queue) { Queue.new } + let(:payload) { {"line" => "hello world" } } + + before do + csr.subject = certificate_subject + csr.public_key = key.public_key + csr.start_time = Time.now + csr.expire_time = csr.start_time + certificate_duration + csr.signing_key = key + csr.want_signature_ability = true + + expect(File).to receive(:read).twice.with(certificate_file_crt) { certificate.to_s } + expect(File).to receive(:read).with(certificate_file_key) { key.to_s } + + server = Lumberjack::Server.new(:port => port, + :address => host, + :ssl_certificate => certificate_file_crt, + :ssl_key => certificate_file_key) + + Thread.new do + server.run { |data| queue.push(data) } + end + end + + it "should require a certificate" do + expect { + client = Lumberjack::Client.new(:port => port, + :host => host, + :addresses => host, + :ssl_certificate => certificate_file_crt) + + client.write(payload) + }.not_to raise_error + + expect(queue.pop).to eq(payload) + end +end