I want to send emails from my Rails web application, and I do not want to disable TLS certificate verification. However for some reason, it always fails with "SSLv3 read server certificate B: certificate verify failed", even though the server certificate is valid.
I doubled checked with openssl s_client
(using /etc/ssl/certs/ca-certificates.crt
), and running the following in the rails console also works, delivering successfully.
smtp = Net::SMTP.new(host, port)
smtp.enable_tls
smtp.start("localhost", username, password, :login) do |smtp|
smtp.send_message msgstr, from, to
end
The server has Rails 4.2.6 and Ruby 2.3.0
config.action_mailer.smtp_setting = {
address:
port: 465,
user_name:
password:
authentication: :login,
openssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
enable_starttls_auto: false,
ssl: true
}
From the described behavior I am quite sure that peer verification has not been done in the console and that you need to explicitly set the certificate store for verifying peer certificates in your Rails configuration.
Why it "works" in the console and how to actually verify peers there:
The observation that it works from the console but does not from Rails code is caused by the fact that
smtp.enable_tls
in your console code does not force peer verification whereas your Rails configuration apparently does. Indeed, when you write the command to the console, you get theSSLContext
printed out:Note that
@verify_mode
isnil
so there is no peer verification enabled by default on theSSLContext
.To force peer verification in console, so that you can play with the SSL settings manually, you need to use a custom
SSLContext
and pass it toenable_tls
:Watch closely the differences: the
SSLContext
now hasverify_mode
set to 1 and has a certificate store for the verifications defined. This is (among other things) what theset_params
method inSSLContext
does.How to configure the certificate store in Rails
Now, Rails does not call the
set_params
methods when constructing theSSLContext
for SMTP connection. Instead, it sets the individual attributes on it according to the options (see here and here in the source code). You have properly configured Rails that you want to verify peer certificates but you have not configured a certificate store to verify peers against.This can be done using the
ca_file
orca_path
options, so the following Rails configuration should work for you:I have no idea why this is not properly documented in the Rails Guides...
This Rails configuration works for me (using Ruby 2.2.2 and Rails 5):