You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AL2023虚拟机Node.js邮件服务器无法发送邮件求助

Node.js SMTP 服务器无法发送邮件(接收功能正常)

我在AL2023 Linux虚拟机上使用smtp-server npm包搭建了Node.js邮件服务器,目前能正常接收邮件,但无法发送邮件。已在安全组配置25和587端口,且通过netstat -tuln命令确认端口已正常开放。相同的发件客户端代码对接Gmail服务器时可正常工作,因此问题肯定出在邮件服务器的配置环节。

服务器端代码

import path from "node:path";
import dotenv from "dotenv";
dotenv.config({path: path.resolve(__dirname + './../../.env')});

import fs from 'fs';
import connectDB from "./db";
import logger from "./logger";

import { SMTPServer } from "smtp-server";
import { simpleParser } from "mailparser";

import MailRepository from "./repositories/mail.repository";
const repo = new MailRepository();

const server = new SMTPServer({
  onConnect(session, cb) {
    cb();
  },
  onMailFrom(address, session, cb) {
    cb();
  },
  onRcptTo(address, session, cb) {
    cb();
  },
  onData(stream, session, callback) {
    simpleParser(stream)
      .then((parsed) => {
        const obj = {
          mail: JSON.parse(JSON.stringify(parsed)),
        };

        repo.create(obj);

        callback(null);
      })
      .catch((err) => {
        console.error("Error parsing email:", err);
        callback(err); 
      });
  },
  disabledCommands: ["AUTH"],
  disableReverseLookup: true,
  logger: false,
  secure: false,
  hideSTARTTLS: true,
  hide8BITMIME: true,
  hidePIPELINING: true,
  hideSMTPUTF8: true,
});

const serverFor587 = new SMTPServer({
  authOptional: false,
  onAuth(auth, session, cb){
    try{
      logger.info('587 on auth : username - '+  auth.username + ' password - ' + auth.password);
      if (auth.username === 'demo' && auth.password === 'Demo@123') {
          cb(null, { user: 'demo' });
      } else {
          cb(new Error('Authentication failed'));
      }
    }
    catch(err){
      logger.error('on auth error :' + err);
      cb(new Error("Authentication Failed"));
    }
  },
  onConnect(session, callback) {
    logger.info('Client connected, sending greeting...');
    callback();
  },
  onData(stream, session, callback) {
    logger.info('ondata');
    stream.pipe(process.stdout);
    stream.on('end', callback);
  },
  onClose(session) {
    console.log('Connection closed from:', session.remoteAddress);
  },
  secure: false,  
  needsUpgrade: true,  // Enable STARTTLS
  key: fs.readFileSync('/etc/letsencrypt/live/mail.demo.in/privkey.pem'),
  cert: fs.readFileSync('/etc/letsencrypt/live/mail.demo.in/fullchain.pem') 
});

serverFor587.on('error', (err) => {
  logger.error('Error:', err);
});

connectDB()
  .then(() => {
    server.listen(25, () => logger.info("mail server running on port 25 for receiving mails"));
    serverFor587.listen(587, () => logger.info("mail server running on port 587 for sending mails"));
  })
  .catch((err) => {
    logger.error("db connection failed !!! ", err);
  });

发件客户端代码

import nodemailer from 'nodemailer';
import logger from "./../logger/log.js";

export class SendMails{

  private transporter = nodemailer.createTransport({
    host: 'mail.demo.in',
    port: 587,
    secure: false,
    auth: {
      user: 'demo',
      pass: 'Demo@123'
  },
  tls: {
      rejectUnauthorized: false
  }
  });

  constructor(){
    this.transporter
        .verify()
        .then(result => logger.info("mailer connected "+ result))
        .catch(err => logger.error("error in mail connection " + err));
  }

  async sendMail(frm: string, to: string, cc:string, bcc:string, sbjct: string, text: string, html: string){
    await this.transporter.sendMail(
      {
        from: frm,
        to: to,
        subject: sbjct,
        text: text,
        html: html,
        cc:cc,
        bcc:bcc,

      },
      function (error, info) {
        if (error) {
          logger.error(error);
        } else {
          logger.info("Email sent: " + info.response);
        }
      }
    );

  }
}

问题分析与修复方案

核心问题

smtp-server包本身仅负责接收邮件,不具备邮件转发/发送到外部邮箱的能力。你当前587端口的服务器配置只是将客户端发来的邮件内容输出到控制台,没有执行实际的邮件转发操作,这是无法发送邮件的根本原因。

具体修复步骤

1. 为587端口服务器添加邮件转发逻辑

引入nodemailer作为出站邮件的处理工具,在onData回调中解析邮件后,将其转发到目标收件人的邮件服务器:

import nodemailer from 'nodemailer';

// 创建用于转发邮件的transporter(可配置中继服务器或直接使用目标邮箱SMTP)
const forwardTransporter = nodemailer.createTransport({
  host: 'smtp.your-relay-server.com', // 若无中继,可使用目标邮箱SMTP(如Gmail的smtp.gmail.com)
  port: 587,
  secure: false,
  auth: {
    user: 'your-relay-account',
    pass: 'your-relay-password'
  }
});

const serverFor587 = new SMTPServer({
  // 保留原有配置...
  onData(stream, session, callback) {
    logger.info('开始处理待发送邮件');
    simpleParser(stream)
      .then(async (parsed) => {
        const forwardOptions = {
          from: parsed.from.text,
          to: parsed.to.text,
          cc: parsed.cc?.text || '',
          bcc: parsed.bcc?.text || '',
          subject: parsed.subject,
          text: parsed.text,
          html: parsed.html
        };

        try {
          const info = await forwardTransporter.sendMail(forwardOptions);
          logger.info(`邮件转发成功: ${info.response}`);
          callback(null);
        } catch (err) {
          logger.error(`邮件转发失败: ${err}`);
          callback(err);
        }
      })
      .catch((err) => {
        logger.error(`解析邮件失败: ${err}`);
        callback(err);
      });
  },
  // 保留原有配置...
});

2. 验证STARTTLS配置有效性

确认证书文件路径正确且证书有效,生产环境建议移除客户端的rejectUnauthorized: false配置,确保证书可信。

3. 配置域名邮件认证记录

外部邮件服务器会拒绝来自无认证记录的邮件,需完成以下配置:

  • 为mail.demo.in设置反向DNS解析(PTR记录)
  • 添加SPF记录,授权你的服务器发送邮件
  • 配置DKIM签名,提升邮件可信度

4. 检查服务器出站限制与黑名单状态

部分云服务商(如AWS)默认限制25端口出站流量,需申请解除限制;同时可通过工具检查服务器IP是否被列入邮件黑名单。

内容的提问来源于stack exchange,提问作者Samarth Srivastava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 21:33:19