NLB配下のPostfixでGmailのSMTP認証をしてメールを送信してみた

SMTPサーバーを冗長構成にしたい時ってありますよね。
2021.06.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

SMTPサーバーを冗長構成したい

こんにちは、のんピ です。

皆さんはSMTPサーバーを冗長構成にしたいと思ったことはありますか? 私はあります。
メールという複数システムが使用するであろう機能が単一障害点(SPOF)になっているのはとても怖いです。

AWS上でEC2インスタンスの冗長構成を実装する際、HTTP/HTTPS以外のプロトコルを使用するのであれば、NLBを使うことが多いと思います。意外にもDevelopersIOでは

  • NLB + SMTP(Postfix)

の組み合わせの記事がなかったので、今回チャレンジしてみたいと思います。

通常メールを送信する際には、tcp/25を使用します。 しかし、以下記事にある通り、AWSではtcp/25を使用して外部にメールを送信する際には、申請が必要になります。

 初期状態の EC2 インスタンスでは Eメールを送信する際に利用する SMTP ポート 25番の通信に制限が設けられています。これは主にスパムメール送信対策としての制限で、悪意のあるユーザーがいくつも EC2 インスタンスを起動して大量のスパムメールを世にばらまくということが簡単に出来ないようにしてくれています。

わざわざ申請するのは面倒臭いので、tcp/25ではなく、サブミッションポートであるtcp/587を使用して外部にメールを送信してみようと思います。

tcp/587を使用する際には、SMTP認証が必要です。AWS上でSMTP認証を行う際には、以下記事の通り、Amazon SESやSendGridを使うパターンが多いと思います。

しかし、既出の認証方法を紹介するのも面白くないので、GmailのSMTP認証を使ってみようと思います。(なんとなく)

いきなりまとめ

  • NLB + SMTP(Postfix) でメール配送の負荷分散はできる
  • GmailでSMTP認証すると、送信元メールアドレスは認証に使用したGmailのメールアドレスになる

検証環境

今回の検証の構成図は以下の通りです。 PostfixをインストールしたEC2インスタンスをMulti-AZ構成にしてNLBにぶら下がるように構築します。

また、今回の検証シナリオはメールの送信のみで、受信は考慮していないため、PublicサブネットにMTAは作成はしておらず、DNSでMXレコードを登録する必要はありません。

環境構築

AWS CDKの説明

いつもの通り、AWS CDKで環境構築していきます。上述の環境を1つのスタックでまとめて作成しちゃいます。

./lib/app-stack.ts

import * as cdk from "@aws-cdk/core";
import * as s3 from "@aws-cdk/aws-s3";
import * as ec2 from "@aws-cdk/aws-ec2";
import * as logs from "@aws-cdk/aws-logs";
import * as iam from "@aws-cdk/aws-iam";
import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2";
import * as fs from "fs";

export class AppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create S3 Bucket for NLB access log
    const nlbAccessLogBucket = new s3.Bucket(this, "NlbAccessLogBucket", {
      encryption: s3.BucketEncryption.S3_MANAGED,
      blockPublicAccess: new s3.BlockPublicAccess({
        blockPublicAcls: true,
        blockPublicPolicy: true,
        ignorePublicAcls: true,
        restrictPublicBuckets: true,
      }),
    });
    console.log(nlbAccessLogBucket.bucketRegionalDomainName);

    // Create CloudWatch Logs for VPC Flow Logs
    const flowLogsLogGroup = new logs.LogGroup(this, "FlowLogsLogGroup", {
      retention: logs.RetentionDays.ONE_WEEK,
    });

    // Create VPC Flow Logs IAM role
    const flowLogsIamrole = new iam.Role(this, "FlowLogsIamrole", {
      assumedBy: new iam.ServicePrincipal("vpc-flow-logs.amazonaws.com"),
    });

    // Create SSM IAM role
    const ssmIamRole = new iam.Role(this, "SsmIamRole", {
      assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "AmazonSSMManagedInstanceCore"
        ),
      ],
    });

    // Create VPC Flow Logs IAM Policy
    const flowLogsIamPolicy = new iam.Policy(this, "FlowLogsIamPolicy", {
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ["iam:PassRole"],
          resources: [flowLogsIamrole.roleArn],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: [
            "logs:CreateLogStream",
            "logs:PutLogEvents",
            "logs:DescribeLogStreams",
          ],
          resources: [flowLogsLogGroup.logGroupArn],
        }),
      ],
    });

    // Atach VPC Flow Logs IAM Policy
    flowLogsIamrole.attachInlinePolicy(flowLogsIamPolicy);

    // Create VPC
    const vpc = new ec2.Vpc(this, "Vpc", {
      cidr: "10.0.0.0/16",
      enableDnsHostnames: true,
      enableDnsSupport: true,
      natGateways: 2,
      maxAzs: 2,
      subnetConfiguration: [
        { name: "Public", subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 },
        { name: "Private", subnetType: ec2.SubnetType.PRIVATE, cidrMask: 24 },
      ],
    });

    // Setting VPC Flow Logs
    new ec2.CfnFlowLog(this, "FlowLogToLogs", {
      resourceId: vpc.vpcId,
      resourceType: "VPC",
      trafficType: "ALL",
      deliverLogsPermissionArn: flowLogsIamrole.roleArn,
      logDestination: flowLogsLogGroup.logGroupArn,
      logDestinationType: "cloud-watch-logs",
      logFormat:
        "${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status} ${vpc-id} ${subnet-id} ${instance-id} ${tcp-flags} ${type} ${pkt-srcaddr} ${pkt-dstaddr} ${region} ${az-id} ${sublocation-type} ${sublocation-id} ${pkt-src-aws-service} ${pkt-dst-aws-service} ${flow-direction} ${traffic-path}",
      maxAggregationInterval: 60,
    });

    // Security Group for Internal MTA
    const internalMtaSg = new ec2.SecurityGroup(this, "InternalMtaSg", {
      allowAllOutbound: true,
      vpc: vpc,
    });
    internalMtaSg.addIngressRule(
      ec2.Peer.ipv4(vpc.vpcCidrBlock),
      ec2.Port.tcp(25),
      "Allow SMTP"
    );

    // Create NLB
    const nlb = new elbv2.NetworkLoadBalancer(this, "Nlb", {
      vpc: vpc,
      vpcSubnets: vpc.selectSubnets({ subnetGroupName: "Private" }),
      crossZoneEnabled: true,
      internetFacing: false,
    });
    nlb.logAccessLogs(nlbAccessLogBucket);

    // Create NLB Target group
    const targetGroup = new elbv2.NetworkTargetGroup(this, "TargetGroup", {
      vpc: vpc,
      port: 25,
      targetType: elbv2.TargetType.INSTANCE,
    });

    // Create NLB listener
    const listener = nlb.addListener("Listener", {
      port: 25,
      defaultTargetGroups: [targetGroup],
    });

    // User data for Amazon Linux
    const userDataParameter = fs.readFileSync(
      "./src/ec2/userDataSettingPostfix.sh",
      "utf8"
    );
    const userDataSettingPostfix = ec2.UserData.forLinux({
      shebang: "#!/bin/bash",
    });
    userDataSettingPostfix.addCommands(userDataParameter);

    // Create EC2 instance
    // Internal MTA
    vpc
      .selectSubnets({ subnetGroupName: "Private" })
      .subnets.forEach((subnet, index) => {
        const ec2Instance = new ec2.Instance(
          this,
          `InternalMtaEc2Instance${index}`,
          {
            machineImage: ec2.MachineImage.latestAmazonLinux({
              generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
            }),
            instanceType: new ec2.InstanceType("t3.micro"),
            vpc: vpc,
            keyName: this.node.tryGetContext("key-pair"),
            role: ssmIamRole,
            vpcSubnets: vpc.selectSubnets({
              subnetGroupName: "Private",
              availabilityZones: [vpc.availabilityZones[index]],
            }),
            securityGroup: internalMtaSg,
            userData: userDataSettingPostfix,
          }
        );

        targetGroup.addTarget(
          new elbv2.InstanceTarget(ec2Instance.instanceId, 25)
        );
      });

    // MUA
    new ec2.Instance(this, `MuaEc2Instance`, {
      machineImage: ec2.MachineImage.latestAmazonLinux({
        generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
      }),
      instanceType: new ec2.InstanceType("t3.micro"),
      vpc: vpc,
      keyName: this.node.tryGetContext("key-pair"),
      role: ssmIamRole,
      vpcSubnets: vpc.selectSubnets({
        subnetGroupName: "Private",
      }),
    });
  }
}

IntenalMTAにインストールするPostfixの設定は以下の通り、UserDataを使って行います。

./src/ec2/userDataSettingPostfix.sh

#!/bin/bash -xe

# Redirect /var/log/user-data.log and /dev/console
exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1

# Check if postfix is installed.
yum list installed | grep postfix

# Install the necessary packages.
sudo yum install cyrus-sasl-md5 jq mailx -y

# Check the current configuration of postfix.
postconf -n

# Back up the postfix configuration file.
sudo cp -a /etc/postfix/main.cf /etc/postfix/main.cf.`date +"%Y%m%d"`

# Edit the postfix configuration file.
sudo postconf -e "myhostname = internal-mta.non-97.private" \
"mydomain = non-97.private" \
"myorigin = \$mydomain" \
"inet_interfaces = all" \
"inet_protocols = ipv4" \
"mydestination = \$myhostname, localhost.\$mydomain, localhost" \
"mynetworks = 10.0.0.0/16, 127.0.0.0/8" \
"home_mailbox = Maildir/" \
"masquerade_domains = \$mydomain" \
"smtpd_banner = \$myhostname ESMTP unknown" \
"relayhost = [smtp.gmail.com]:587" \
"smtp_sasl_auth_enable = yes" \
"smtp_sasl_security_options = noanonymous" \
"smtp_sasl_tls_security_options = noanonymous" \
"smtp_sasl_password_maps = hash:/etc/postfix/smtp-auth-passwd" \
"smtp_sasl_mechanism_filter = digest-md5, plain" \
"smtp_use_tls = yes" \
"smtp_tls_security_level = encrypt" \
"smtp_tls_loglevel = 1" \
"smtp_tls_note_starttls_offer = yes"

# Check the differences of postfix configuration files before and after editing.
sudo diff -u /etc/postfix/main.cf.`date +"%Y%m%d"` /etc/postfix/main.cf

# Check the postfix configuration file for incorrect descriptions.
sudo postfix check

# Start postfix.
sudo systemctl start postfix

# Check the status of postfix.
systemctl status postfix

# Enable postfix auto-start.
sudo systemctl enable postfix

# Check the postfix auto-start setting.
systemctl is-enabled postfix

# Check the ports
netstat -ant |grep "LISTEN "

Postfixの設定値の説明

postfixの設定が大量にあるので、各項目について補足します。

# 構築するSMTPサーバーのホスト名をFQDNで設定します。
myhostname = internal-mta.non-97.private

# メールアドレスで使用するドメイン名を設定します。
mydomain = non-97.private 

# ローカル(Internal MTA)から配送されたメールの送信元アドレスに付加するドメイン名を設定します。
# $mydomainに設定することで non-97.private のメールだと認識させます。
myorigin = $mydomain 

# SMTPサーバで待ち受けるネットワークアドレスを設定します。
# デフォルトでは localhost になっており、ローカル(Internal MTA)から以外のメールを配送することができません。
# allを指定することで、ローカル以外から配信されたメールも配信することが可能になります。
inet_interfaces = all 

# IPv4のみ許可するよう設定します。
inet_protocols = ipv4 

# SMTPサーバーが受信(ローカルに配送)するドメインを設定します。
# 今回の場合、 以下のドメイン宛のメールについて受信します。
# - @internal-mta.non-97.private
# - @localhost.non-97.private
# - @localhost
mydestination = $myhostname, localhost.$mydomain, localhost 

# 信頼できるネットワークを設定します。
# 指定したネットワークに属しているクラインアントからは認証なしでメール送信が可能になるので、不正中継に利用されないよう信頼できるネットワークのみ設定します。
# ここでは、VPC全体を信頼するネットワークとして指定しています。
# 0.0.0.0/0 とするとオープンリレーな設定になるので、要注意です。
mynetworks = 10.0.0.0/16, 127.0.0.0/8 

# メールの保存形式を指定します。
# Maildir/を指定することでMaildir形式となりメールごとにファイルが生成されます。
# メールは宛先ユーザのホームディレクトリ配下のMaildirディレクトリに保存されます。
# 例) testmailman@internal-mta.non-97.private宛にメールを送信した場合、以下ディレクトリにメールが保存される。
#         /home/testmailman/Maildir/new/
home_mailbox = Maildir/ 

# SMTPサーバーのホスト名を隠します。
# 設定をしなければ、送信元のメールのドメインが @internal-mta.non-97.private になります。
# $mydomain を指定することで、送信元のメールドメインを @non-97.private にすることができます。
masquerade_domains = $mydomain 

# バナー情報(ソフトウェア名など)の出力を指定します。
# ここでは非表示にしています。
smtpd_banner = $myhostname ESMTP unknown 

# リレーするSMTPサーバーを指定します。
# 今回はGmailのSMTPサーバーのサブミッションポートを指定します。
relayhost = [smtp.gmail.com]:587 

# SMTP認証方式としてSASLを使用します。
smtp_sasl_auth_enable = yes 

# SMTP認証をする際、匿名認証を拒否するように設定します。
smtp_sasl_security_options = noanonymous 

# TLSを使ったSMTP認証をする際、匿名認証を拒否するように設定します。
smtp_sasl_tls_security_options = noanonymous 

# リレー先の認証情報を記載するファイルを指定します。
# hash:<ファイル名>とすることで、<ファイル名>の内容をハッシュしたファイルを読み取ります。
smtp_sasl_password_maps = hash:/etc/postfix/smtp-auth-passwd 

# SASL認証方式の指定をします。
# 記述した順番にに認証方式の優先順位が高くなります。
# digest-md5は、md5によるメッセージダイジェストで認証し、パスワード自体は送信しません。
# plainは、その名の通り、平文で認証情報を送信します。
smtp_sasl_mechanism_filter = digest-md5, plain 

# SMTP認証時にTLSによる暗号化を有効化します。
smtp_use_tls = yes 

# SMTP認証時にTLSによる暗号化の強制します。
smtp_tls_security_level = encrypt 

# TLSハンドシェイクと証明書の情報をログに記録します。
smtp_tls_loglevel = 1 

# TLSがまだ有効になっていなければ認証先のSMTPサーバーのホスト名をログに記録します。
smtp_tls_note_starttls_offer = yes

AWS CDKでリソースを払い出す

cdk deployをして、リソースを払い出します。
払い出されたリソースを確認すると、以下の通り、EC2インスタンスとNLBが払い出されています。NLBのDNS名はMUAからメールを送信する際にSMTPサーバーとして指定するので控えておきます。

また、ターゲットグループを確認すると、2台ともhealthyになっています。

SMTP認証で使用する認証情報の取得

GmailでSMTP認証を行う場合は、普段使用しているGoogleのパスワードではなく、アプリパスワードを利用します。このアプリパスワードは、AWSで言うところのシークレットアクセスキーをイメージすると分かりやすいかと思います。

アプリパスワードを取得するためにまず、Googleアカウント管理に入ります。

Googleアカウント管理に入った後、アプリパスワードをクリックします。

アプリパスワードを生成するアプリとデバイスを選択するように指示されるため、メールと、その他(名前を入力)を指定します。

名前を適当に入力して生成をクリックすると、16桁のアプリパスワードが表示されます。表示されたアプリパスワードは後程使用するので控えておきます。

取得したアプリパスワードをSMTP認証の認証情報をSSM パラメーターストアにアップロードします。認証情報は[smtp.gmail.com]:587 <Gmailのアドレス>:<アプリパスワード>というフォーマットで入力します。

認証情報をSSM パラメーターストアにアップロードした後、SSM RunCommandで認証情報をEC2インスタンスに読み込ませます。

SSM RunCommandで実行するコマンドは以下の通りです。

# Get smtp credentials from the SSM Parameter Store.
aws ssm get-parameter --name "smtp-auth-passwd" --with-decryption --region "us-east-1" --output json | jq -r '.Parameter.Value' > /etc/postfix/smtp-auth-passwd

# Generate a hash value from smtp credentials.
postmap /etc/postfix/smtp-auth-passwd

# Delete the file with the original smtp credentials.
rm -rf /etc/postfix/smtp-auth-passwd

# Restart postfix and check its status.
systemctl restart postfix
systemctl status postfix

SSM RunCommandで上述したコマンドを実行します。

実行が完了したことを確認します。

メール送信の確認

外部のメールアドレス宛に送信

MTAでSMTP認証情報の読み込みが完了したら、実際にメールを送信してみます。

MUAのEC2インスタンスより、私の社用メールアドレスに送信します。メールログを確認したいので、MTAのEC2インスタンス上でそれぞれで以下のコマンドを実行しておきます。egrepでNLBからの疎通確認のログは除外するようにしています。

$ sudo tail -f /var/log/maillog | egrep -v 'disconnect|connect'

メールを送信するために、mailxをインストールして、3通ほどメールを送信してみます。
mailx自体の説明については以下の記事をご参照ください。

sh-4.2$ sudo yum install mailx -y
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                                                                                         | 3.7 kB  00:00:00
Resolving Dependencies
--> Running transaction check
---> Package mailx.x86_64 0:12.5-19.amzn2 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

==========================================================================================================================================================
 Package                          Arch                              Version                                   Repository                             Size
==========================================================================================================================================================
Installing:
 mailx                            x86_64                            12.5-19.amzn2                             amzn2-core                            247 k

Transaction Summary
==========================================================================================================================================================
Install  1 Package

Total download size: 247 k
Installed size: 466 k
Downloading packages:
mailx-12.5-19.amzn2.x86_64.rpm                                                                                                     | 247 kB  00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : mailx-12.5-19.amzn2.x86_64                                                                                                             1/1
  Verifying  : mailx-12.5-19.amzn2.x86_64                                                                                                             1/1

Installed:
  mailx.x86_64 0:12.5-19.amzn2

Complete!
sh-4.2$
sh-4.2$ vi ~/.mailrc
sh-4.2$
sh-4.2$ cat ~/.mailrc
# Specify the DNS of the NLB as the SMTP server.
set smtp=smtp://AppSt-NlbBC-PSK8IRBL516L-84d15b2c9a3a9c0a.elb.us-east-1.amazonaws.com
sh-4.2$
sh-4.2$
sh-4.2$ mail <のん のメールアドレス>@classmethod.jp
Subject: test subject
test contents
.
EOT
sh-4.2$
sh-4.2$ mail <のん のメールアドレス>@classmethod.jp
Subject: test subject 2
test contents 2
.
EOT
sh-4.2$
sh-4.2$ mail <のん のメールアドレス>@classmethod.jp
Subject: test subject 3
test contents 3
.
EOT
sh-4.2$

それでは、それぞれのメールログも確認してみます。

InternalMtaEc2Instance0

sh-4.2$ sudo tail -f /var/log/maillog | egrep -v 'disconnect|connect'
May 31 07:12:06 ip-10-0-2-18 postfix/smtpd[32058]: 917AA2C4C6: client=ip-10-0-2-15.ec2.internal[10.0.2.15]
May 31 07:12:06 ip-10-0-2-18 postfix/cleanup[32060]: 917AA2C4C6: message-id=<60b48c46.QBchESFx2KepC28f%ssm-user@ip-10-0-2-15.ec2.internal>
May 31 07:12:06 ip-10-0-2-18 postfix/qmgr[3211]: 917AA2C4C6: from=<ssm-user@ip-10-0-2-15.ec2.internal>, size=598, nrcpt=1 (queue active)
May 31 07:12:07 ip-10-0-2-18 postfix/smtp[32061]: 917AA2C4C6: to=<<のん のメールアドレス>@classmethod.jp>, relay=smtp.gmail.com[209.85.144.108]:587, delay=0.75, delays=0.05/0.02/0.22/0.46, dsn=2.0.0, status=sent (250 2.0.0 OK  1622445127 g18sm1618173qtk.34 - gsmtp)
May 31 07:12:07 ip-10-0-2-18 postfix/qmgr[3211]: 917AA2C4C6: removed

InternalMtaEc2Instance1

sh-4.2$ sudo tail -f /var/log/maillog | egrep -v 'disconnect|connect'
May 31 07:11:13 ip-10-0-3-7 postfix/smtpd[3390]: 37B6866146: client=ip-10-0-2-15.ec2.internal[10.0.2.15]
May 31 07:11:13 ip-10-0-3-7 postfix/cleanup[3393]: 37B6866146: message-id=<60b48c11.9HSVFtCVanNdP2mx%ssm-user@ip-10-0-2-15.ec2.internal>
May 31 07:11:13 ip-10-0-3-7 postfix/qmgr[2987]: 37B6866146: from=<ssm-user@ip-10-0-2-15.ec2.internal>, size=594, nrcpt=1 (queue active)
May 31 07:11:14 ip-10-0-3-7 postfix/smtp[3394]: 37B6866146: to=<<のん のメールアドレス>@classmethod.jp>, relay=smtp.gmail.com[209.85.232.109]:587, delay=1, delays=0.06/0.02/0.24/0.69, dsn=2.0.0, status=sent (250 2.0.0 OK  1622445074 i14sm9141743qkn.99 - gsmtp)
May 31 07:11:14 ip-10-0-3-7 postfix/qmgr[2987]: 37B6866146: removed

May 31 07:11:42 ip-10-0-3-7 postfix/smtpd[3390]: 1B08566146: client=ip-10-0-2-15.ec2.internal[10.0.2.15]
May 31 07:11:42 ip-10-0-3-7 postfix/cleanup[3393]: 1B08566146: message-id=<60b48c2e.mlacZA+SZbDEIUC7%ssm-user@ip-10-0-2-15.ec2.internal>
May 31 07:11:42 ip-10-0-3-7 postfix/qmgr[2987]: 1B08566146: from=<ssm-user@ip-10-0-2-15.ec2.internal>, size=598, nrcpt=1 (queue active)
May 31 07:11:42 ip-10-0-3-7 postfix/smtp[3394]: 1B08566146: to=<<のん のメールアドレス>@classmethod.jp>, relay=smtp.gmail.com[209.85.232.109]:587, delay=0.74, delays=0.05/0/0.23/0.47, dsn=2.0.0, status=sent (250 2.0.0 OK  1622445102 a21sm8100997qtm.54 - gsmtp)
May 31 07:11:42 ip-10-0-3-7 postfix/qmgr[2987]: 1B08566146: removed

dsn=2.0.0となっていることから正しくメールの配送が出来ていそうです。

それでは、メールボックスを確認してみます。

メールボックスを確認すると、以下の通り、3通メールを受信できています。なお、メールの送信元はSMTP認証をしたGmailのメールアドレスになっています。

メールのソースも確認してみます。
ソースを確認すると、X-Google-Original-From: ssm-user@ip-10-0-2-15.ec2.internalとなっていることから本来の送信元メールアドレスも表示されていますね。

クリップボードにコピーした内容

Delivered-To: <のん のメールアドレス>@classmethod.jp
Received: by 2002:a17:90a:6b4e:0:0:0:0 with SMTP id x14csp2365322pjl;
        Mon, 31 May 2021 00:11:14 -0700 (PDT)
X-Received: by 2002:ac8:7ed2:: with SMTP id x18mr13578452qtj.26.1622445074669;
        Mon, 31 May 2021 00:11:14 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1622445074; cv=none;
        d=google.com; s=arc-20160816;
        b=NsMgI1pryySGlHhC85NWuPVMRWHbwKhpPCbr1Q8jlf8YYFzyUATdNJvYsK5JIhWOHU
         TlrwEZlProx8FPg41ecgcw7rtyIOEEkb3lm8tcbDTjU39D7z4j9OUtZ8xtSxaVHh2077
         arF5fM0mrHCIvuS2PqNpNjXZv17JMYD6cUZv5S0MxZ9LwVW4kl0boEqXWx0/cwyldm9C
         X8ZT0LFCAzR74tNFWLnNQlq/Xb3team0qx7xnX92/xyUTAk+aWFWC/CA5/9deBuoAZce
         LErgi/sJTRYrZGAgJCAEm+g/meXOoHZYpK/6rdado7DN215Zbu4JQkl3eVOwrlIlkebo
         dldg==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
        h=content-transfer-encoding:mime-version:user-agent:message-id
         :subject:to:date:from:dkim-signature;
        bh=kcsbJeKOSZwRituv/SRW2f+Z/P1mKqKgFdpy9y19cWc=;
        b=RvdfUAKMa+hkQF8XywZLZplRik+5LIrMEHA2UGHN/Oiw9va0hN7tgGnYuj7RGFnHvt
         Kb+KSACPwAQ24WvnWT6i1YcEkf6RYId6WstEiDNceRmYIQzxUfDtrJiUArRMOOm8xlOX
         aYTZNmxgsfQkPRQn/HvtcG7i73cJrV7+ceAQlbdnH8Db+3bGjXppBXEYCBR1Ebl3YRql
         p06tRemdbIJ2UJnjHGZaC0RNaAgfXDZORIPANPwNQThJ+PNEIFpn1NcKML1kXRnIJ46C
         zKNjGj++5nzi+3NFwSQ+ahxBBsNfeepdmuAJ3zb6yakHVpEI/bOv9FV6y55Fip5HEZ6B
         p77w==
ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@gmail.com header.s=20161025 header.b=LwtUHOa+;
       spf=pass (google.com: domain of <SMTP認証に使ったメールアドレス>@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=<SMTP認証に使ったメールアドレス>@gmail.com;
       dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com
Return-Path: <<SMTP認証に使ったメールアドレス>@gmail.com>
Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41])
        by mx.google.com with SMTPS id m4sor5044414qkk.27.2021.05.31.00.11.14
        for <<のん のメールアドレス>@classmethod.jp>
        (Google Transport Security);
        Mon, 31 May 2021 00:11:14 -0700 (PDT)
Received-SPF: pass (google.com: domain of <SMTP認証に使ったメールアドレス>@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41;
Authentication-Results: mx.google.com;
       dkim=pass header.i=@gmail.com header.s=20161025 header.b=LwtUHOa+;
       spf=pass (google.com: domain of <SMTP認証に使ったメールアドレス>@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=<SMTP認証に使ったメールアドレス>@gmail.com;
       dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20161025;
        h=from:date:to:subject:message-id:user-agent:mime-version
         :content-transfer-encoding;
        bh=kcsbJeKOSZwRituv/SRW2f+Z/P1mKqKgFdpy9y19cWc=;
        b=LwtUHOa+g+HxRJODjfIfxayNbyDqxpwN7MM0V+ZJpg3S3otez0iPszHnyutWFmM/CJ
         9SI1e25TJkPRLkkbO9EfLFCYp6VaO9TFLsivvy5PM+vInmgdP4fgcI3lp5GDT2u7iMnn
         ZvAOU4VuuN3jIQDcjB2Ma1V4idd+XJr7RIAo9yisq4IAwT0131TOOwnsCNiya8GceXoW
         AU7zEDehaaVOzpd2LW55JbboHY4UN7l4Of4AW0saLu4cWrWnwSI8lx8n1CuIRyDAnW93
         Q1xr36PKdCex808jrjrVr05JYUU5QOaND30vogoPuXB1E9DDQDKZGBubXIX4b8TD7DWW
         gVHg==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20161025;
        h=x-gm-message-state:from:date:to:subject:message-id:user-agent
         :mime-version:content-transfer-encoding;
        bh=kcsbJeKOSZwRituv/SRW2f+Z/P1mKqKgFdpy9y19cWc=;
        b=RIR+hfi41TB5YWQ0fjhC2c05goFyvOJWBFQmotJvWbBCyw8uq6y8y4Ba2FlFcY4O/7
         1sNtHNFwskCU0zcjzIXEXbdMEsfwmgCOXGIAiYQjw9DUwhJhn7pPoK2zIEyKgxOhasoD
         lTEFGwUuzRIX4b/FqV0JK8+BV+fq2fr0c86kzfEX2G9ZQjSzS4b1oXRZPGHr/uN+qQVr
         DjWn2x6s3dLCuQAmS/QQGxvMBBxZ+/c6O3fA9dpdFV1IR9gjGhph+KrcVxwyc6ZP56m9
         buIog1GaFsV7cTP778DlxgyRWVXZGxfqxK5Bs4J/UqbzOu7niH6ZCguI+cSsGvYysA6n
         653Q==
X-Gm-Message-State: AOAM532nn82npzsUZ9jOKJq/dzKvrxrLWpMXDf7xyIl/ZKMJchjBsAFp FyGsCS4cHHqmZu1SSuUYOAz+LsKIyAtpFvdg
X-Google-Smtp-Source: ABdhPJxoRNML1uGPyxY744u2YVF9LYlQN4t3NFkr2KgNg90DE2+H/rzK4PCjcMI4AF7G8hNLTl0kRQ==
X-Received: by 2002:a05:620a:2487:: with SMTP id i7mr1781310qkn.64.1622445074137;
        Mon, 31 May 2021 00:11:14 -0700 (PDT)
Return-Path: <<SMTPメールアドレス>@gmail.com>
Received: from internal-mta.non-97.private (ec2-34-236-166-203.compute-1.amazonaws.com. [34.236.166.203])
        by smtp.gmail.com with ESMTPSA id i14sm9141743qkn.99.2021.05.31.00.11.13
        for <<のん のメールアドレス>@classmethod.jp>
        (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
        Mon, 31 May 2021 00:11:13 -0700 (PDT)
From: <SMTP認証に使ったメールアドレス>@gmail.com
X-Google-Original-From: ssm-user@ip-10-0-2-15.ec2.internal
Received: from ip-10-0-2-15.ec2.internal (ip-10-0-2-15.ec2.internal [10.0.2.15]) by internal-mta.non-97.private (Postfix) with SMTP id 37B6866146 for <<のん のメールアドレス>@classmethod.jp>; Mon, 31 May 2021 07:11:13 +0000 (UTC)
Date: Mon, 31 May 2021 07:11:13 +0000
To: <のん のメールアドレス>@classmethod.jp
Subject: test subject
Message-ID: <60b48c11.9HSVFtCVanNdP2mx%ssm-user@ip-10-0-2-15.ec2.internal>
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

test contents

Internal MTA宛にメールを送信

Internal MTA宛にもメールを送信して、正しく受信できるかも確認してみます。

まず、受信をするために、Internal MTAで以下の通り、ユーザーを作成します。 その後、メールのログを出力するようにしておきます。

sh-4.2$ sudo useradd testmailman
sh-4.2$ sudo passwd testmailman
Changing password for user testmailman.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
sh-4.2$
sh-4.2$
sh-4.2$ sudo ls -l /home/testmailman/
total 0
sh-4.2$
sh-4.2$ sudo tail -f /var/log/maillog | egrep -v 'disconnect|connect'

それでは、MUAから以下の通り、Internal MTA宛にメールを送信してみます。

sh-4.2$ mail testmailman@internal-mta.non-97.private
Subject: test subject
test contents
.
EOT
sh-4.2$

Internal MTAでログを確認しみると、正常に送信されたようです。
実際に、Maildir配下を確認すると、先ほど送信したメールが保存されていることが確認できます。

sh-4.2$ sudo tail -f /var/log/maillog | egrep -v 'disconnect|connect'

May 31 07:17:31 ip-10-0-2-18 postfix/smtpd[32106]: EEAB82C4C6: client=ip-10-0-2-15.ec2.internal[10.0.2.15]
May 31 07:17:32 ip-10-0-2-18 postfix/cleanup[32109]: EEAB82C4C6: message-id=<60b48d8b.fy7z5cC9nvgaqFWg%ssm-user@ip-10-0-2-15.ec2.internal>
May 31 07:17:32 ip-10-0-2-18 postfix/qmgr[3211]: EEAB82C4C6: from=<ssm-user@ip-10-0-2-15.ec2.internal>, size=614, nrcpt=1 (queue active)
May 31 07:17:32 ip-10-0-2-18 postfix/local[32110]: EEAB82C4C6: to=<testmailman@internal-mta.non-97.private>, relay=local, delay=0.06, delays=0.05/0.01/0/0, dsn=2.0.0, status=sent (delivered to maildir)
May 31 07:17:32 ip-10-0-2-18 postfix/qmgr[3211]: EEAB82C4C6: removed

^C
sh-4.2$
sh-4.2$ sudo ls -l /home/testmailman/
total 0
drwx------ 5 testmailman testmailman 39 May 31 07:17 Maildir
sh-4.2$
sh-4.2$ sudo ls -l /home/testmailman/Maildir/new
total 4
-rw------- 1 testmailman testmailman 759 May 31 07:17 1622445452.V10301I4001ecM34183.ip-10-0-2-18.ec2.internal
sh-4.2$
sh-4.2$ sudo cat /home/testmailman/Maildir/new/1622445452.V10301I4001ecM34183.ip-10-0-2-18.ec2.internal
Return-Path: <ssm-user@ip-10-0-2-15.ec2.internal>
X-Original-To: testmailman@internal-mta.non-97.private
Delivered-To: testmailman@internal-mta.non-97.private
Received: from ip-10-0-2-15.ec2.internal (ip-10-0-2-15.ec2.internal [10.0.2.15])
        by internal-mta.non-97.private (Postfix) with SMTP id EEAB82C4C6
        for <testmailman@internal-mta.non-97.private>; Mon, 31 May 2021 07:17:31 +0000 (UTC)
Date: Mon, 31 May 2021 07:17:31 +0000
From: ssm-user@ip-10-0-2-15.ec2.internal
To: testmailman@internal-mta.non-97.private
Subject: test subject
Message-ID: <60b48d8b.fy7z5cC9nvgaqFWg%ssm-user@ip-10-0-2-15.ec2.internal>
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

test contents
sh-4.2$

NLB + SMTP(Postfix)で、配信用SMTPサーバーの冗長化は簡単にできる

特別な設定は必要なく、NLB + SMTP(Postfix)で、配信用SMTPサーバーの冗長化を行うことができました。

実はSendmail民だったので、Postfixの設定は初めてだったのですが設定変更がかなりやり易かったです。パッとSMTPサーバーを用意するならPostfixをこれから使っていこうと思います。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!