I was trying to setup my Office 365 account as the SMTP mail server for a Ghost CMS instance and kept running into some pretty fun errors. While instructions for a number of other mail hosts exist, I wasn't able to find any information on O365 specifically and nothing I tried seemed to be working.

After a little research, I found that GHost integrates with Nodemailer, a node.js module, to send or relay mail. Using this as a starting point, I was able to find a lot more documentation on all of the various settings that are possible under mail in the config.production.json file.

After hitting my first brick wall, my first though was that the O365 server was enforcing TLS and the SMTP module simply wasn't trying to handshake with the encryption protocol it was looking for. This was definitely a component of the initial problem as the error thrown was:

Failed to send email. Reason: routines:ssl3 get record:wrong version number [blah, blah, blah]

According to the documentation, setting the connection to "secure": false should effectively disable SSL and default to TLS. Contrary to the immediate success message I had hoped for, the error only transitioned to a new one about how the server had denied me the joy of sending my test message:

Error sending email: Failed to send email. Reason: Message delivery failed: 554 5.2.0 STOREDRV Submission Exception: SendAsDeniedException MapiExceptionSendAsDenied; Failed to process message due to a permanent exception with message Cannot submit message. [more blah, blah, blah]

From there, I tried a bunch of setting variations like explicitly setting the connection to TLS using "requireTLS": true and setting an explicit TLS connection with SSL turned off. I even attempted to connected by specifying cipher versions 1, 2, and 3 with "tls": {"ciphers:'SSLv3'"} - and yeah, none of that got me any further than a refused connection error.

Still convinced this was related to TLS, I thought I'd try create a non-TLS connection by creating a send connector in O365. The result was the exact same server send as denied error.

After all this troubleshooting and reading through error logs, I had a light bulb moment/realized there was literally only one thing left I hadn't tried.  Since the error was specific to "SendAs" I did some searching and located a "from": " " statement...and...viola! My test email came through instantly.

I strongly suspect that ghost was passing a blank send as value to the O365 Exchange server and that was the source of the issue as well as the reason that adding a from statement resolved it.

Here's what I did from start to finish that worked:

Open up the config.production.json file with your preferred text editor, in this case I'll use nano and we'll pretend the file is at the default location:

sudo nano /var/www/ghost/config.production.json

Delete everything in the file from "mail": { down to }, and stop at "process": "systemd" - do not erase this or anything below it.

Replace [email protected] with the UPN of the user you actually want to use and replace [email protected]$$Word with a real password, then, in place of the void you created in the previous step, append the following code:

"mail": {
    "from": "[email protected]",
    "transport": "SMTP",
    "options": {
        "host": "smtp.office365.com",
        "port": 587,
        "service": "O365",
        "secure": false,
        "requireTLS": true,
        "auth": {
            "user": "[email protected]",
            "pass": "[email protected]$$Word"
        }
    }
},

Helpful tip: After editing, be sure to save the file.

Now you'll need to restart Ghost in order for these changes to be reflected. To do this, navigate to the directory that it's installed in. If you're not sure where ghost is located you can just run ghost ls to get the directory location. In this case I'll again assume it's default:

cd /var/www/ghost/

Now run the restart command:

ghost restart

That's it, you're done!