signet_planet's avatar

Laravel incorrectly handling EHLO for SMTP from artisan/cli : issues with Gmail's SMTP-RELAY (resolved)

Did you have SMTP relay with Google working but then CLI or Queue instances suddenly stop working earlier this year? Well this is for you and you can jump to the end to see the fix but if your interested in why this happened, read on.

Technically when computers communicate over a network it is bad practice for them to introduce themselves as localhost. In the mid 90's there was a huge debate whether this mattered for SMTP EHLO/HELO handshakes as it did not authenticate on that name. Because of this it has been mostly ignored and most SMTP servers today still use by default 'localhost' or 'localdomain' during the initial handshake.

however it was also known most spam servers never took the time to change most of the defaults in the SMTP servers and Google noticed that most spam came from servers announcing they were localhost during the EHLO handshake. So much so that Google announced back in 2010 that starting in January 2021 their SMTP servers would start dropping connections to any of their SMTP servers identifying themselves as localhost, localdomain, 127.0.0.1, etc. They gave us 10 years to get our act together but as expected little changed until January of this year.

Even though I never noticed any new announcements or reminders of their old announcement; starting in January of 2021 Google true to their word, started dropping connections to their SMTP servers that used any common default (and improper) EHLO handshake. My laravel scripts appeared to work fine but 99% of my Laravel initiated emails come from an HTTP process and I only noticed the 1% this week when I manually ran a php artisan script I expected an emailed report from.

Digging in I found during the Laravel bootstrap process if you are using a web server, the domain name gets magically passed down the the Swiftmail transport and it is used during the ELHO handshake so it was working fine. However if the email is initiated via a CLI artisan command (and I suspect the Queue) it is left blank. The Swiftmail package is designed to use [127.0.0.1] by default thereby breaking this RFC requirement that is being enforced by Gmail.

It wouldn't make sense to have Swiftmail figure out a better default as the bootstrap happens in Laravel and it really should be passing a domain name to use when bootstrapped from the CLI.

Because Laravel (via Swiftmail) is using 127.0.0.1 during the EHLO handshake, google doesn't give an error but drops the connection. You will typically see an error something like "Swift_TransportException: Expected response code 250 but got an empty response". I dug into the Swiftmail package and worked my way backwards to Laravel to see where it was failing to pass on a valid domain. The fix ended up being very simple.

the fix

This fix is to add 'local_domain' to your smtp settings in the config/mail.php file specifying any fully qualified domain as that is the value Laravel will forward onto Swiftmail. You could set that to use any name as long as it is not similar to any commonly default localhost domains or IP addresses (or gmail.com - they protect their name too obviously).

For me in the config/mail.php file, I added the local_domain variable using this:

'mailers' => [
  'smtp' => [
    'transport' => 'smtp',
    'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
    'port' => env('MAIL_PORT', 587),
    'encryption' => env('MAIL_ENCRYPTION', 'tls'),
    'username' => env('MAIL_USERNAME'),
    'password' => env('MAIL_PASSWORD'),
    'timeout' => null,
    'auth_mode' => null,
    // add the following
    'local_domain' => env('MAIL_EHLO_DOMAIN',parse_url(env('APP_URL'))['host']),

This way if I do not define a MAIL_EHLO_DOMAIN in my .env file it will use the host name portion of the domain defined in APP_URL for my app. Before you ask, I already tested it on my development box using my docker version of valet and so all my APP_URL's end with .test (such as blob.test) and those have all worked with Gmails SMTP as well.

But with that extra line, if I want I can also use a different name for the SMTP handshake by defining MAIL_EHLO_DOMAIN in my .env file.

Hope this is of help.

0 likes
4 replies
patrickkellar's avatar

This is perfect, I've been wracking my brain on this for a while and stumbled across this post. Thank you very much for the detailed explanation and the fix.

1 like
misakstvanu's avatar

I love you. My app stopped working and I spent last few days talking to Google support and trying to find a solution. Even they assured me this should not be the case and until now, I didn't really know how to test this. Thanks! :)

1 like

Please or to participate in this conversation.