ruby-on-railsrubysslactionmailerself-signed

Why isn't a self signed SMTP Certificate ignored by Rails ActionMailer 6.1 / Ruby 3.0?


I can't get Rails ActionMailer 6.1 (with Ruby 3.0) to connect to an SMTP Mailer with a self-signed certificate.

All options that could possibly either use no TLS/SSL at all or to not verify the cert are set in config/production.rb and seem to be picked up properly by rails.

Any ideas what I might be missing?

ruby --version
  ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux-musl]

RAILS_ENV=production myapp rails c
Loading production environment (Rails 6.1.4.6)
irb(main):001:0> mailer = ActionMailer::Base.new
=> #<ActionMailer::Base:0x00000000029c48>
irb(main):002:0> ap mailer.smtp_settings
{
                 :address => "smtpout.xxxxx.com",
                    :port => 25,
         :enable_starttls => false,
    :enable_starttls_auto => false,
     :openssl_verify_mode => 0,
                     :ssl => false,
                     :tls => false
}
=> nil
irb(main):003:0> mailer.mail(from: 'user1@example.com', to: 'user2@example.com',
    subject: 'test', body: "Hello, you've got mail!").deliver
/usr/local/lib/ruby/3.0.0/net/protocol.rb:46:in `connect_nonblock': SSL_connect
    returned=1 errno=0 state=error: certificate verify failed (self signed 
    certificate) (OpenSSL::SSL::SSLError)


Solution

  • This boils down to the following: When I use the settings

    enable_starttls_auto: false,
    openssl_verify_mode: OpenSSL::SSL::VERIFY_NONE
    

    it does perform TLS transmission and fails with state=error: certificate verify failed (self signed certificate) (OpenSSL::SSL::SSLError), which is double wrong in my eyes, as it should never even start a TLS handshake and if so, should skip certificate validation.

    Rails uses the Mail-Gem to handle these options, which indeed had an incompatible change.

    As a workaround in my case, removing the enable_starttls_auto setting completely (thus keeping TLS transmission), only disabling the certificate validation with openssl_verify_mode: OpenSSL::SSL::VERIFY_NONE was helping (but will not help you if you have other reasons to avoid TLS than having a bogus certificate like me).