Skip to content

Commit 00cd021

Browse files
committed
Merge pull request #622 from estolfo/RUBY-886-ssl-verify
RUBY-886 verify server certificates by default when ssl is true
2 parents 5c60606 + b77b0ca commit 00cd021

File tree

7 files changed

+172
-48
lines changed

7 files changed

+172
-48
lines changed

lib/mongo/socket/ssl.rb

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,34 @@ def readbyte
100100

101101
def create_context(options)
102102
context = OpenSSL::SSL::SSLContext.new
103-
if options[:ssl_cert]
104-
context.cert = OpenSSL::X509::Certificate.new(File.open(options[:ssl_cert]))
105-
end
106-
if options[:ssl_key]
107-
if options[:ssl_key_pass_phrase]
108-
context.key = OpenSSL::PKey::RSA.new(File.open(options[:ssl_key]),
109-
options[:ssl_key_pass_phrase])
110-
else
111-
context.key = OpenSSL::PKey::RSA.new(File.open(options[:ssl_key]))
112-
end
103+
set_cert(context, options) if options[:ssl_cert]
104+
set_key(context, options) if options[:ssl_key]
105+
set_cert_verification(context, options) unless options[:ssl_verify] == false
106+
context
107+
end
108+
109+
def set_cert(context, options)
110+
context.cert = OpenSSL::X509::Certificate.new(File.open(options[:ssl_cert]))
111+
end
112+
113+
def set_key(context, options)
114+
if options[:ssl_key_pass_phrase]
115+
context.key = OpenSSL::PKey::RSA.new(File.open(options[:ssl_key]),
116+
options[:ssl_key_pass_phrase])
117+
else
118+
context.key = OpenSSL::PKey::RSA.new(File.open(options[:ssl_key]))
113119
end
114-
if options[:ssl_verify] || options[:ssl_ca_cert]
115-
context.ca_file = options[:ssl_ca_cert]
116-
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
120+
end
121+
122+
def set_cert_verification(context, options)
123+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
124+
cert_store = OpenSSL::X509::Store.new
125+
if options[:ssl_ca_cert]
126+
cert_store.add_file(options[:ssl_ca_cert])
127+
else
128+
cert_store.set_default_paths
117129
end
118-
context
130+
context.cert_store = cert_store
119131
end
120132

121133
def verify_certificate!(socket)

spec/mongo/server/connection_pool_spec.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
describe Mongo::Server::ConnectionPool do
44

5+
let(:options) do
6+
TEST_OPTIONS.merge(max_pool_size: 2)
7+
end
8+
59
let(:address) do
610
Mongo::Address.new('127.0.0.1:27017')
711
end
@@ -17,7 +21,7 @@
1721
describe '#checkin' do
1822

1923
let(:server) do
20-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
24+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, options)
2125
end
2226

2327
let!(:pool) do
@@ -47,7 +51,7 @@
4751
describe '#checkout' do
4852

4953
let(:server) do
50-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
54+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, options)
5155
end
5256

5357
let!(:pool) do
@@ -95,7 +99,7 @@
9599
describe '.get' do
96100

97101
let(:server) do
98-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
102+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, options)
99103
end
100104

101105
let!(:pool) do
@@ -110,7 +114,7 @@
110114
describe '#inspect' do
111115

112116
let(:server) do
113-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
117+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, options)
114118
end
115119

116120
let!(:pool) do
@@ -129,7 +133,7 @@
129133
describe '#with_connection' do
130134

131135
let(:server) do
132-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
136+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, options)
133137
end
134138

135139
let!(:pool) do

spec/mongo/server/connection_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
end
1616

1717
let(:server) do
18-
Mongo::Server.new(address, double('cluster'), monitoring, listeners, ssl: SSL)
18+
Mongo::Server.new(address, double('cluster'), monitoring, listeners, TEST_OPTIONS)
1919
end
2020

2121
describe '#connect!' do

spec/mongo/socket/ssl_spec.rb

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212

1313
let(:options) do
1414
{
15-
:ssl => true,
16-
:ssl_cert => CLIENT_PEM,
17-
:ssl_key => CLIENT_PEM
15+
:ssl => true,
16+
:ssl_cert => CLIENT_PEM,
17+
:ssl_key => CLIENT_PEM,
18+
:ssl_verify => false
1819
}
1920
end
2021

@@ -31,9 +32,10 @@
3132

3233
let(:options) do
3334
{
34-
:ssl => true,
35-
:ssl_cert => CLIENT_PEM,
36-
:ssl_key => CRL_PEM
35+
:ssl => true,
36+
:ssl_cert => CLIENT_PEM,
37+
:ssl_key => CRL_PEM,
38+
:ssl_verify => false
3739
}
3840
end
3941

@@ -51,7 +53,8 @@
5153
:ssl => true,
5254
:ssl_cert => CLIENT_PEM,
5355
:ssl_key => CLIENT_PEM,
54-
:ssl_ca_cert => CA_PEM
56+
:ssl_ca_cert => CA_PEM,
57+
:ssl_verify => true
5558
}
5659
end
5760

@@ -63,5 +66,88 @@
6366
expect(socket).to be_alive
6467
end
6568
end
69+
70+
context 'when a CA certificate is not provided', if: running_ssl? && testing_locally? do
71+
72+
let(:options) do
73+
{
74+
:ssl => true,
75+
:ssl_cert => CLIENT_PEM,
76+
:ssl_key => CLIENT_PEM,
77+
:ssl_verify => true
78+
}
79+
end
80+
81+
before do
82+
ENV['SSL_CERT_FILE']= CA_PEM
83+
socket.connect!
84+
end
85+
86+
it 'uses the default cert store' do
87+
expect(socket).to be_alive
88+
end
89+
end
90+
91+
context 'when ssl_verify is not specified' do
92+
93+
let(:options) do
94+
{
95+
:ssl => true,
96+
:ssl_cert => CLIENT_PEM,
97+
:ssl_key => CLIENT_PEM,
98+
:ssl_ca_cert => CA_PEM
99+
}
100+
end
101+
102+
before do
103+
socket.connect!
104+
end
105+
106+
it 'verifies the server certificate' do
107+
expect(socket).to be_alive
108+
end
109+
end
110+
111+
context 'when ssl_verify is true' do
112+
113+
let(:options) do
114+
{
115+
:ssl => true,
116+
:ssl_cert => CLIENT_PEM,
117+
:ssl_key => CLIENT_PEM,
118+
:ssl_ca_cert => CA_PEM,
119+
:ssl_verify => true
120+
}
121+
end
122+
123+
before do
124+
socket.connect!
125+
end
126+
127+
it 'verifies the server certificate' do
128+
expect(socket).to be_alive
129+
end
130+
end
131+
132+
context 'when ssl_verify is false' do
133+
134+
let(:options) do
135+
{
136+
:ssl => true,
137+
:ssl_cert => CLIENT_PEM,
138+
:ssl_key => CLIENT_PEM,
139+
:ssl_ca_cert => 'invalid',
140+
:ssl_verify => false
141+
}
142+
end
143+
144+
before do
145+
socket.connect!
146+
end
147+
148+
it 'does not verify the server certificate' do
149+
expect(socket).to be_alive
150+
end
151+
end
66152
end
67153
end

spec/spec_helper.rb

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@
1717
end
1818
end
1919

20+
TEST_SET = 'ruby-driver-rs'
21+
COVERAGE_MIN = 90
22+
CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
23+
SERVER_DISCOVERY_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam/**/*.yml")
24+
SERVER_SELECTION_RTT_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/rtt/*.yml")
25+
SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/selection/**/*.yml")
26+
CRUD_TESTS = Dir.glob("#{CURRENT_PATH}/support/crud_tests/**/*.yml")
27+
28+
SSL_CERTS_DIR = "#{CURRENT_PATH}/support/certificates"
29+
CLIENT_PEM = "#{SSL_CERTS_DIR}/client.pem"
30+
CLIENT_PASSWORD_PEM = "#{SSL_CERTS_DIR}/password_protected.pem"
31+
CA_PEM = "#{SSL_CERTS_DIR}/ca.pem"
32+
CRL_PEM = "#{SSL_CERTS_DIR}/crl.pem"
33+
2034
require 'mongo'
2135

2236
require 'support/travis'
@@ -61,20 +75,6 @@
6175
end
6276
end
6377

64-
TEST_SET = 'ruby-driver-rs'
65-
COVERAGE_MIN = 90
66-
CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
67-
SERVER_DISCOVERY_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam/**/*.yml")
68-
SERVER_SELECTION_RTT_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/rtt/*.yml")
69-
SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/selection/**/*.yml")
70-
CRUD_TESTS = Dir.glob("#{CURRENT_PATH}/support/crud_tests/**/*.yml")
71-
72-
SSL_CERTS_DIR = "#{CURRENT_PATH}/support/certificates"
73-
CLIENT_PEM = "#{SSL_CERTS_DIR}/client.pem"
74-
CLIENT_PASSWORD_PEM = "#{SSL_CERTS_DIR}/password_protected.pem"
75-
CA_PEM = "#{SSL_CERTS_DIR}/ca.pem"
76-
CRL_PEM = "#{SSL_CERTS_DIR}/crl.pem"
77-
7878
# Determine whether the test clients are connecting to a standalone.
7979
#
8080
# @since 2.0.0
@@ -149,10 +149,16 @@ def list_command_enabled?
149149
$list_command_enabled ||= $mongo_client.cluster.servers.first.features.list_indexes_enabled?
150150
end
151151

152+
# Is the test suite running locally (not on Travis or Jenkins).
153+
#
154+
# @since 2.1.0
152155
def testing_locally?
153156
!(ENV['CI'] || ENV['JENKINS_HOME'])
154157
end
155158

159+
# Is the test suite running on SSL.
160+
#
161+
# @since 2.0.2
156162
def running_ssl?
157163
SSL
158164
end

spec/support/authorization.rb

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,28 @@
5050
# @since 2.0.3
5151
SSL = ENV['SSL_ENABLED'] == 'true'
5252

53+
# SSL options.
54+
#
55+
# @since 2.1.0
56+
SSL_OPTIONS = {
57+
ssl: SSL,
58+
ssl_verify: false,
59+
ssl_cert: CLIENT_PEM,
60+
ssl_key: CLIENT_PEM
61+
}
62+
63+
# Base test options.
64+
#
65+
# @since 2.1.0
66+
BASE_OPTIONS = {
67+
max_pool_size: 1,
68+
write: WRITE_CONCERN
69+
}
70+
5371
# Options for test suite clients.
5472
#
5573
# @since 2.0.3
56-
TEST_OPTIONS = CONNECT.merge(max_pool_size: 1,
57-
write: WRITE_CONCERN,
58-
ssl: SSL)
74+
TEST_OPTIONS = BASE_OPTIONS.merge(CONNECT).merge(SSL_OPTIONS)
5975

6076
# The root user name.
6177
#

spec/support/certificates/client.pem

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ Certificate:
3232
6e:a7
3333
Exponent: 65537 (0x10001)
3434
X509v3 extensions:
35-
X509v3 Basic Constraints:
35+
X509v3 Basic Constraints:
3636
CA:FALSE
37-
Netscape Comment:
37+
Netscape Comment:
3838
OpenSSL Generated Certificate
39-
X509v3 Subject Key Identifier:
39+
X509v3 Subject Key Identifier:
4040
4A:8B:EE:22:42:E6:F8:62:4C:86:38:8D:C5:78:95:98:C1:10:05:7C
41-
X509v3 Authority Key Identifier:
41+
X509v3 Authority Key Identifier:
4242
keyid:07:41:19:3A:9F:7E:C5:B7:22:4E:B7:BC:D5:DF:E4:FC:09:B8:64:16
4343

4444
Signature Algorithm: sha1WithRSAEncryption

0 commit comments

Comments
 (0)