0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

こんなメールサーバーが欲しい

普通に使うメールはOutlook+ExchangeOnlineだけど、
自動システムとか内部からのメール送信の一部はオンプレ環境にSMTPサーバーを置き送信を行う必要がある。

必要になったのはこんな機能のあるSMTPサーバー

  • ExchangeOnlineでの送信
    SMTPで受け取ったメールをExchangeOnlineの非SMTPで送信する。
    SMTPでのログインユーザーでどのアカウントを使うかは決まる
  • SMTPリレーとして送信
    その場合に、 STARTTLS対応・ DKIM署名付与
  • どちらになるかはSMTP認証時のユーザーで決定する

こんなの欲しいが、少し探した限りでは見つからなかった。

つくる

ExchangeOnlineでの送信に関しては

SMTPに関しては
nodemailerはSMTPサーバーが必要なのが基本、
一応direct-transportもあるが、使ってみたらちょっと問題があったので
transporterを作ることにした。
動作作るならとMTA-STSにも対応させた。

nodemailerはdkim署名とSMTP接続の処理に使う。
SMTP接続前の処理は自作。

内容

全部コードはのせられないけど一部の処理を載せる、

  • メールアドレスからMXサーバーの特定
    アドレスのホスト部分のDNSからMXの候補を探し出す。
    CNAME,MX,A,AAAAを見て、優先度設定して配列に入れていく
getMX = async (domain:string,cnameList?:Array<string>):Promise<undefined|Array<MxRecord>> => {
    if(!domain){
        return;
    }
    const cnames:Array<string> = cnameList||[];
    const lookupLimit = 4;
    //CNAME
    const cnameRR = await this._dnsResolve("CNAME",domain);
    if(cnameRR.record && typeof cnameRR.record[0] === "string"){
        if(cnames.includes(domain)){
            return;
        }
        cnames.push(cnameRR.record[0]);
        if(cnames.length > lookupLimit){
            return;
        }
        const mxList = await this._getMX(cnameRR.record[0],cnames);
        if(!mxList){
            return;
        }
        return [...mxList];
    }
    if(cnameRR.err?.temporary){
        return;
    }
    //MX
    const mxRR = await this._dnsResolve("MX",domain);
    if(mxRR.mxrecord && mxRR.mxrecord.length > 0){
        return [...mxRR.mxrecord];
    }
    if(cnameRR.err?.temporary){
        return;
    }
    //A
    const aRR = await this._dnsResolve("A",domain);
    if(aRR.record){
        return [{"exchange":domain,"priority":0}];
    }
    if(cnameRR.err?.temporary){
        return;
    }
    //AAAA
    const aaaaRR = await this._dnsResolve("AAAA",domain);
    if(aaaaRR.record){
        return [{"exchange":domain,"priority":0}];
    }
    if(cnameRR.err?.temporary){
        return;
    }
}
  • MXのリスト順に送信を実行する
    その際にMXサーバーに対してMTA-STAの検証をする
    MTA-STSでTLS必須なら、SMTP接続でTLS検証必須にする。
//略
    for(const mx of mxList){
        const mtasts = await this.verifyMTASTS(sendAddressDomain.domain,mx.exchange);
        if(mtasts){
            TLS.DEFAULT_MIN_VERSION = "TLSv1.2";
        }
        const smtpCon = new SMTPConnectionSync({
            "host":mx.exchange,
            "port":25,
            "secure":false,
            "requireTLS":mtasts,
            "tls": {
                "rejectUnauthorized": mtasts,
                "servername":mx.exchange,
            }
        });
        try{
            if(await smtpCon.connect()){
                continue;
            }
            const sendResult = await smtpCon.send({"to":sendAddressDomain.sendAddress,"from":currentQueue.envelopFrom},currentQueue.messege);
            await smtpCon.quit();
            if(sendResult.info && sendResult.info.rejected.length > 0){
                this.pushRetryQueue(currentQueue,{"domain":sendAddressDomain.domain,"sendAddress":sendResult.info.rejected});
            }
            log.debug("Done.");
            //送信成功、MXリスト処理から抜ける
            sendMX = true;
            break;
        }catch(err){
            log.error(err);
            continue;
        }finally{
        //略
        }
    }
    //略
    if(!sendMX){
    //全MXへの処理に失敗、再送キューに入れる
        this.pushRetryQueue(currentQueue,sendAddressDomain);
    }
//略
  • 送信失敗時の再送処理

これは送信内容をキューに入れて、最初は待機時間=0で即処理、成功したらそのまま終わり、
失敗したら待機時間をセットしてまたキューに入れる。
といった感で処理するようにした。

どうせ、内部でしか使わないし、頻度低いし、で結構適当に作ったけど
それなりに上手く動いている。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?