ActionMailer SMTP“证书验证失败”

Fir*_*cer 0 ruby ssl smtp ruby-on-rails

我想从我的Rails Web应用程序发送电子邮件,并且我不想禁用TLS证书验证。但是,由于某种原因,即使服务器证书有效,它也始终会失败并显示“ SSLv3读取服务器证书B:证书验证失败”。

我再次检查了openssl s_client(使用/etc/ssl/certs/ca-certificates.crt),并且在rails控制台中运行以下命令也可以成功交付。

smtp = Net::SMTP.new(host, port)
smtp.enable_tls
smtp.start("localhost", username, password, :login) do |smtp|
  smtp.send_message msgstr, from, to
end
Run Code Online (Sandbox Code Playgroud)

该服务器具有Rails 4.2.6和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
}
Run Code Online (Sandbox Code Playgroud)

Bor*_*aMa 5

根据所描述的行为,我非常确定控制台中尚未完成对等验证,并且您需要在Rails配置中显式设置证书存储以验证对等证书

它为何在控制台中“起作用”,以及如何在其中实际验证对等方:

观察到它可以在控制台中运行,但不能在Rails代码中运行,是由于smtp.enable_tls您的控制台代码中没有强制对等验证,而Rails配置显然可以进行对等验证。确实,当您将命令写入控制台时,就可以SSLContext打印出:

smtp.enable_tls
# => #<OpenSSL::SSL::SSLContext:0x000000064043d0 @cert=nil, @key=nil,
       @client_ca=nil, @ca_file=nil, @ca_path=nil, @timeout=nil,
       @verify_mode=nil, @verify_depth=nil, @renegotiation_cb=nil,
       @verify_callback=nil, @cert_store=nil, @extra_chain_cert=nil, 
       @client_cert_cb=nil, @session_id_context=nil, @tmp_dh_callback=nil, 
       @session_get_cb=nil, @session_new_cb=nil, @session_remove_cb=nil, 
       @tmp_ecdh_callback=nil, @servername_cb=nil, @npn_protocols=nil, 
       @alpn_protocols=nil, @alpn_select_cb=nil, @npn_select_cb=nil>
Run Code Online (Sandbox Code Playgroud)

请注意,@verify_modenil所以默认情况下启用无对等认证SSLContext

要在控制台中强制进行对等验证,以便可以手动使用SSL设置,您需要使用自定义SSLContext并将其传递给enable_tls

ssl_context = Net::SMTP.default_ssl_context
ssl_context.set_params
smtp.enable_tls(ssl_context)
# => #<OpenSSL::SSL::SSLContext:0x000000063c27c8 @cert=nil, @key=nil, 
       @client_ca=nil, @ca_file=nil, @ca_path=nil, @timeout=nil, 
       @verify_mode=1, @verify_depth=nil, @renegotiation_cb=nil, 
       @verify_callback=nil, @cert_store=#<OpenSSL::X509::Store:0x00000002894408 @verify_callback=nil, @error=nil, @error_string=nil, @chain=nil, @time=nil>, @extra_chain_cert=nil, 
       @client_cert_cb=nil, @session_id_context=nil, @tmp_dh_callback=nil, 
       @session_get_cb=nil, @session_new_cb=nil, @session_remove_cb=nil, 
       @tmp_ecdh_callback=nil, @servername_cb=nil, @npn_protocols=nil, 
       @alpn_protocols=nil, @alpn_select_cb=nil, @npn_select_cb=nil>
Run Code Online (Sandbox Code Playgroud)

仔细观察它们之间的差异:SSLContext现在将其verify_mode设置为1,并具有用于定义验证的证书存储。这(除其他事项外)是该set_params方法SSLContext作用。

如何在Rails中配置证书存储

现在,Rails set_params在构造SSLContextfor SMTP连接时不会调用这些方法。而是根据选项在其上设置各个属性(请参见源代码中的此处此处)。您已经正确配置了要验证对等方证书的Rails,但尚未配置要对等方进行验证的证书存储

可以使用ca_fileca_path选项完成此操作,因此以下Rails配置应该适合您:

config.action_mailer.smtp_setting = {
    ...
    ssl: true
    enable_starttls_auto: false,
    openssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
    ca_file: "/etc/ssl/certs/ca-certificates.crt",
    ...
} 
Run Code Online (Sandbox Code Playgroud)

我不知道为什么在Rails指南中没有正确记录...