Cannot connect to AWS SES SMTP endpoint - node.js

I'm building an application that will use AWS SES to send email via SMTP. I've properly configured a domain and confirmed I can send email from that domain using the AWS SDK. I've created SMTP credentials and ensured the IAM user is properly configured with the right permissions. I've written a test script that uses Nodemailer to send the email.
The test script runs successfully using my personal development machine on my home network, but the script will NOT work when using the development laptop issued by my corporate client on my home network. The corporate laptop runs many security tools, including ZScaler. I also know that, due to the ZScaler service, NPM must be set to use a self-signed certificate (the command is npm config set cafile {CA_FILEPATH}).
I don't know why the script won't work on the corporate laptop, and I would appreciate some help in figuring what to try next.
Here's the script:
'use strict';
const nodemailer = require('nodemailer');
const runtest = async function() {
console.debug('Creating the SMTP transport');
const transporter = nodemailer.createTransport({
host: 'email-smtp.us-west-2.amazonaws.com',
port: 465,
secure: true,
auth: {
user: 'myusername',
pass: 'mypassword',
},
});
console.debug('Building mail options');
const mailOptions = {
from: 'me#example.com',
to: 'you#example.com',
subject: 'subject',
text: 'body',
html: '<h1>Hi</h1',
};
console.debug('Sending mail...');
const info = await transporter.sendMail(mailOptions);
console.debug(`Sent mail. Info: ${JSON.stringify(info, null, 2)}`);
console.info('Message sent!');
};
runtest().catch(console.error);
Here's the result when run from the corporate laptop:
Creating the SMTP transport
Building mail options
Sending mail...
Error: read ECONNRESET
at TLSWrap.onStreamRead (internal/stream_base_commons.js:200:27) {
errno: 'ECONNRESET',
code: 'ESOCKET',
syscall: 'read',
command: 'CONN'
}
Things I've tried:
Playing with TLS settings such as rejectUnauthorized: false and specifying the TLS version
Connecting to or disconnecting from the corporate VPN
I found a reference on this Github issue that suggested testing the connection with openssl. I ran this command openssl s_client -connect email-smtp.us-west-2.amazonaws.com:465, and this was the result (seems ok):
CONNECTED(0000021C)
write:errno=10054
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 336 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

In my case, it was the node version that caused this error. I have upgraded my node version to v12.x to fix the issue.

Related

NodeMailer is blocked on Azure App Service

I have a NestJS app deployed to a Windows Azure App Service. Like everything else in Azure, it works fine on my machine until I deploy it. Getting a 500 error. It looks like some kind of port restriction.
Error message:
DEBUG Creating transport: nodemailer (6.7.2; +https://nodemailer.com/; SMTP/6.7.2[client:6.7.2])
DEBUG Sending mail using SMTP/6.7.2 DEBUG [DJemKiOruKY]
Resolved localhost as 127.0.0.1 [cache hit]
ERROR [DJemKiOruKY] connect EACCES 127.0.0.1:587
DEBUG [DJemKiOruKY] Closing connection to the server using 'destroy'
Here's the code that configures the mailer. Our noreply email has a custom domain (noreply#our_company.com)
const transporter = nodemailer.createTransport({
host: 'smtp.office365.com',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: process.env.NOREPLY_EMAIL_ADDRESS,
pass: process.env.NOREPLY_EMAIL_PASSWORD
},
logger: true
});
const info = await transporter.sendMail({
from: `Sender Name <${process.env.NOREPLY_EMAIL_ADDRESS}>`,
to: process.env.NOREPLY_EMAIL_RECIPIENT,
...
You have to specifically allow outbound traffic on port 587.
How you do this depends on your setup.
To verify that the port is open, go to the app service console:
tcpping smtp.office365.com:587

Heroku Redis Add-On Error error:1408F10B:SSL routines:ssl3_get_record:wrong version number

After upgrading my Heroku Redis add-on to v6.2.3 from v4, Heroku papertrail logs display this error: Error accepting a client connection: error:1408F10B:SSL routines:ssl3_get_record:wrong version number I am connecting to Redis using NodeJs and the bull npm package (https://www.npmjs.com/package/bull). I found similar questions related to this error, along with Heroku documentation, and based on that I have set my bull redis options to the following:
redis: {
host: redisURL.hostname,
port: Number(redisURL.port),
password: redisURL.password,
tls: {
rejectUnauthorized: false,
},
},
Note the tls parameter. I have set it to Heroku's recommendations here: https://devcenter.heroku.com/articles/heroku-redis#connecting-in-node-js After getting stuck for a while, I attempted to simply comment out any client code that connects to Redis, delete the add-on, and re-provision the add-on. I expected to see no redis logs in papertrail when I did this, but I still see the same error, even when no code that connects to redis is being run... This leads me to believe maybe it's a setting on the actual Redis add-on instance, rather than an issue with my code, but I am at a loss.
Updates:
I logged into the redis:cli and did some investigation. client list reveals 2 client connections. 1 is the instance of the redis:cli I am running in my terminal, and another is the a client with a flag that means "the client is a replica node connection to this instance" (see https://redis.io/commands/client-list). What is interesting is the error that is being logged in papertrail shows the file descriptor for the client connection that is having the SSL error fd=12, while the 2 clients shown in client list have the file descriptors fd=10 and fd=11. So there must be another client connection with fd=12 that isn't appearing in client list command causing the error shown above.
Jasper Kennis' answer is correct. Adding tls: {rejectUnauthorized: false} fixed this issue for me. Unfortunately, Heroku only gives you a full REDIS_URL connection string, so you need to parse the password/host/port yourself (you can't specify both a URL and tls settings). Here's my BullModule.forRoot() config object if it helps:
redis: {
password: process.env.REDIS_URL.split('#')[0].split(':')[2],
host: process.env.REDIS_URL.split('#')[1].split(':')[0],
port: parseInt(process.env.REDIS_URL.split('#')[1].split(':')[1]),
tls: { rejectUnauthorized: false },
}
Using:
#nestjs/bull: 0.6.0,
Heroku redis: 6.2.3
Ran into the same problem. In addition to rejectUnauthorized: false, adding requestCert: true, solved it for me. In addition, some clients need agent: false, (but the version of Bull I'm using doesn't recognise that argument)
redis: {
host: redisURL.hostname,
port: Number(redisURL.port),
password: redisURL.password,
tls: {
rejectUnauthorized: false,
requestCert: true,
// agent: false, (not all clients accept this)
},
},

Nodemailer cannot send email from within Docker container

I've been searching/reading/trying everywhere on the Internet for about 3 weeks before posting here ...
Context:
developing little website app
technologies:
Next JS (ReactJs, HTML, CSS) for both frontend and backend (Node)
Linux as host (Ubuntu 20.04 LTS)
Docker's container to encapsulate app (based on node:alpine image) (Docker version 20.10.6)
Nodemailer Node's module to send email
this is the code using Nodemailer to send the e-mail message:
import type { NextApiRequest, NextApiResponse } from "next";
import * as nodemailer from "nodemailer";
export default async (req: NextApiRequest, res: NextApiResponse) => {
res.statusCode = 200;
let transporter = nodemailer.createTransport({
host: process.env.NM_HOST,
port: parseInt(process.env.NM_PORT),
secure: true,
auth: {
user: process.env.NM_USER,
pass: process.env.NM_PASS,
},
tls: {
rejectUnauthorized: false,
},
});
// console.log("User:");
// console.log(process.env.NM_USER);
let info = await transporter.sendMail({
from: "Website <xxx#xxx.com>",
to: "Website <xxx#xxx.com>",
subject: "New contact",
text: "NAME:\n" + req.body.data.name + "\n----------\nEMAIL:\n" + req.body.data.email + "\n----------\nBODY:\n" + req.body.data.body,
}, function (err, info) {
if (err) {
console.log(err)
} else {
console.log(info);
}
});
console.log("Message sent: %s", info);
res.json({
a: req.body.data.name,
b: req.body.data.email,
c: req.body.data.body,
});
};
Issue:
when I try to send e-mail using Nodemailer launching my app from Linux host as "npm run start" or "npm run dev", mails get delivered
when I try to send e-mail using Nodemailer launching my app from Docker's container, i get following error (from app's output itself)
Error: connect ECONNREFUSED 127.0.0.1:465
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1133:16) {
errno: -111,
code: 'ESOCKET',
syscall: 'connect',
address: '127.0.0.1',
port: 465,
command: 'CONN'
}
What I already tried and what I observed:
ping google.com (and many others) works from within container (using docker exec -ti container-name sh command)
starting container with docker run --dns 8.8.8.8 ... -> same result (error above)
container's and host' /etc/resolv.conf are different (but I think that this might not be the point, as ping command correctly resolves, but feel free to say me wrong if I am)
I am not a sys admin (i am a developer), so I don't know if iptables or ufw (firewall) may be implied in this thing (btw, it's difficult to install non pre-installed packages on node:alpine)
Email server authentication is correct (both username, hostname, password) as it works correctly when i launch my app as npm run start or npm run dev
switch container's network between bridge (default) bridge (custom with docker-compose) and host ... same issue (error above)
Anyone willing to help is really appreciated.
Found out what wasn't working: I was using docker-compose WITHOUT --env-file option.
That way all the environment variables (e.g. PORT, HOST, PSWD, USR) I was trying to access within my app, were left undefined (this was because those environment variables weren't already built in during the building step - design choice, but rather accessed at runtime with process.env)
SOLUTION (change .env file part as suits your situation):
docker-compose --env-file ./.env.production
Useful official resource (docker-compose)
Docker-compose using --env-file option

Error: read ECONNRESET when connecting from mqtt node.js

I am facing an issue when trying to connect to mqtt broker, I have installed the mosquitto and given two ports(1883,8883) as a listener in the mosquitto.conf file. When I try to run the code for connect from node.js for mqtts on port 8883 I get below error
Error: read ECONNRESET
and on mosquitto
socket error on client <unknown> disconnecting
When I try to publish a message using MQTT.fx it successfully publish the mesasge on port 8883
Below is my code
const config = {
endpoint: 'mqtts://192.168.0.0',
topic: 'test/topic/local',
payload: {message: 'HelloWorld'},
}
mqtt.connect(config.endpoint, {
clientId: 'some id',
})
Are there some configurations needs to be done?
mqtts is the tls version of mqtt , to use it you will need to either supply certs to match the backend or configure it not to validate if they are self signed.
to disable validation add this to your connect
rejectUnauthorized: false
be aware that doing this will prevent your client from being able to validate the backend safely, you should never do this in production.

How to catch email from local app server. Nodejs

I have lite nodejs server. I am trying to implement password reset flow. I use nodemailer to send email with reset password link. I want to test if I send email properly. I don't want to use any remote smpt.
What I can use on my local environment to catch emails and check if they are good?
Try this command:
python -m smtpd -n -c DebuggingServer localhost:1025
I used mailcatcher. It is a super simple SMTP server which catches any message sent to it to display in a web interface.
I have following configuration:
let smtpConfig = {
host: '127.0.0.1',
port: 1025,
secure: false, // upgrade later with STARTTLS
auth: {
user: 'user',
pass: 'password'
}
};
Do not forget to add user and password in auth section. It can be even white space. But it can't be empty string in this case you'll get error.
const smtpTransport = nodemailer.createTransport(smtpConfig);
And now you can see all sent emails here http://127.0.0.1:1080

Resources