1
1
Qiita×Findy記事投稿キャンペーン 「自分のエンジニアとしてのキャリアを振り返ろう!」

Google smtp-relay サービスを使って、SMTP relay しても gmail.com から迷惑メールに判定されない方法を考える

Last updated at Posted at 2024-03-02

What is this ?

最近、gmail.com で迷惑メール対策として、DMARC および SPF の義務化等の対応に追われています。
クラウドのメールサービスをそのまま使用してるドメインはいいのですが、問題なのが今まで何も考えずに MTA へメールデータを送るだけの ( ≒ 認証なしにメールを送信していた ) システムが、なりすまし対策を講じると結構なコストになる点です。

そこで、Google のサービスを利用している弊社で、もうちょっといい感じにコストを下げられないか、というのがこの記事の目的です。

最終的には色々とケースを考えて、どのように利用するかを判断すべきですが、今回は Case 1 のみ取り上げます。

Google の SMTP Relay Service

この辺に情報があります。

思えば、弊社にとって上記のサービスは「まさに空気」だったのですが、ここへ来て「えらいひと」からの注目度が俄然 UP しました。

上記手順によれば Google Vault の設定が必須ですが、となりの人がカスタマーサポートに chat して聞いたら「Gmail のアカウントに送信データが積もり、会社のストレージを圧迫するので Google Vault はやめておけ」的な話をしていたそうで。
Google Vault はどちらかというとコンプライアンス管理目的のサービスだと思っていたので、Gmail にデータが残りそう ? な点は確認する必要がありそうです。下記 URL が Google Vault のざっくりとした説明。

私の検証環境では「無効なリクエストです」と表示されたので今回は Google Vault についての検証はしていません。

検証環境

検証は実ドメインを使用しており、それを Qiita に転記する際に example.com 等に置換しています。
「なにか見た」場合には、騒ぎ立てずにこっそり教えてもらえると助かります。

共通事項

組織で保有するドメインを example.com とします。

Case 1. SMTP Client が直接 Google SMTP Relay にアクセスする

001.png

ここでは

  • example.com は Google Workspace を契約して Gmail で読み書きできる
  • 送信するメールアドレス : foo@example.com
  • foo@example.com は Google Workspace 上にある有効なアカウント
  • 受信するアドレス : var@example.com および hoge@gmail.com
  • foo@example.com から hoge@gmail.com は転送なし ( 直接送信 )

とします。

client ( sendmail ) の構築 ( ひな形 )

Windows 11 に VirtualBox をインストール、Vagrant を使用して Ubuntu 20.04 ( focal ) 上に sendmail でメールを送信する環境を構築します。

ひな形構築に関するデザインノート

  • sendmail を対象にする
    どうみても sendmail は一般的に実環境で利用されているとは考えづらいのですが、例えば「sendmail で~したことを postfix ではどうするか」といったノウハウはあると思えるので汎用性を考えて sendmail を選択
  • メール中継は SMART_HOST を使用する
    google のドキュメントにそう書いてあったから。
    また、nullclient で MASQUERADE をうまく扱えなかったから
  • vagrant で構築する
    dig などで確認する際に ssh login できると便利
  • VirtualBox を使用する
    vagrant 標準だから
  • github には公開しない
    そのまんま使ってもらうのが目的ではなく、あくまでも参考としてもらうため
  • AS IS で出す
    めんどくさくなった ( 現時点 )

ひな形の詳細

ディレクトリ構造は以下の通り

-- : root
 |- data/ # 必要に応じて vagnrantup.ps1 が作成
 |- vagrantup.ps1
 |- Vagrantfile
 |- bootstrap.sh
vagrantup.ps1
#
# vagrantup.ps1
#
$datadir = "data"
if ( -Not (Test-Path $datadir)){
  New-Item -ItemType Directory -Path $datadir
}

vagrant destroy --force

$Env:VAGRANT_DEFAULT_PROVIDER = "virtualbox"

vagrant plugin install vagrant-reload
vagrant plugin uninstall vagrant-vbguest

vagrant up

vagrant ssh-config > ssh.config

Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  config.vm.box = "ubuntu/focal64"
  config.vm.box_version = "20240118.0.0"
  config.vm.boot_timeout = 1500

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via 127.0.0.1 to disable public access
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"
  config.vm.synced_folder "data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.
  config.vm.provider "virtualbox" do |vb|
    vb.check_guest_additions = false
    vb.gui = false
    vb.name = "smtp-relay-client-tmpl"
    vb.cpus = 2
    vb.memory = 8192
    vb.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
    vb.customize [ "modifyvm", :id, "--graphicscontroller", "vmsvga" ]
    vb.customize [ "modifyvm", :id, "--ioapic", "on"]
    vb.customize [ "modifyvm", :id, "--accelerate3d", "off"]
  end
  config.vm.hostname = "smtp-relay-client"

  # Enable provisioning with a shell script. Additional provisioners such as
  # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL
  config.vm.provision "shell", path: "bootstrap.sh", args: ["foo@example.com", "example.com.", "-v"], privileged: true
  #config.trigger.after [:provision] do |t|
  #  t.name = "Reboot after provisioning"
  #  t.run = { :inline => "vagrant reload" }
  #end
end
bootstrap.sh
#!/bin/bash
#
# bootstrap.sh ( for mail client : MTA ver )
# usage : bootstrap.sh <test_mail_addr> [<domain>] [<mta host name>] [<sendmail options>]
#    domain : default is "example.com."
#    mta host name : default is "smtp-relay.gmail.com."
#    sendmail options : default is ""-bm -d -v"
#
set -x

##
## prepare
##

#
# check variables from parameter
#
test_mail_addr=$1
domain_name=${2:-"example.com."}
mta_host=${3:-"smtp-relay.gmail.com."}
sendmailopts=${4:-"-bm -d -v"}

#
# get today date
#
today=$(date +\%Y%m%d)

#
# check whether DNS name is FQDN
#
is_dotted=$(echo "${domain_name}" | grep "\.$")
if [ -z "${is_dotted}" ] ; then
  domain_name="${domain_name}."
fi

is_dotted=$(echo "${mta_host}" | grep "\.$")
if [ -z "${is_dotted}" ] ; then
  mta_host="${mta_host}."
fi

#
# update and install software
#
sudo apt-get update
sudo apt-get -yq upgrade

sudo apt-get install -yq sendmail expect jq

#
# modify /etc/hosts
#
local_short=$(hostname)
local_ip=$(ip -detail -json address list \
	| jq -r '.[] | select(.addr_info[].scope=="global") | .addr_info[].local' \
	| grep "^[0-9]" \
	| head -1)

local_fqdn="${local_short}.${domain_name}"

tmphostsfile=$(mktemp /tmp/hosts.XXXXXX)
echo "127.0.0.1	${local_fqdn}	$(hostname)" > ${tmphostsfile}
echo "${local_ip}	${local_fqdn}" >> ${tmphostsfile}
cat /etc/hosts >> ${tmphostsfile}
sudo mv /etc/hosts /etc/hosts.${today}
sudo mv ${tmphostsfile} /etc/hosts
# for debug
cat /etc/hosts

# for test
getent hosts ${local_ip}
ping -4 -c 2 ${local_ip}
ping -4 -c 2 ${local_fqdn}

##
## configure sendmail setting files
##

#
# prepare  
#
basedir="/etc/mail"

#
# make /etc/mail/local-host-names
#
local_host_name="${basedir}/local-host-names"
echo "${domain_name}"        | sudo tee    ${local_host_name}
echo "${local_fqdn}"         | sudo tee -a ${local_host_name}
echo "${local_short}."        | sudo tee -a ${local_host_name}
echo "localhost."             | sudo tee -a ${local_host_name}
echo "localhost.localdomain." | sudo tee -a ${local_host_name}
# for debug
cat ${local_host_name}

#
# make /etc/mail/masquerade-domains
#
masquerade_domains="${basedir}/masquerade-domains"
echo "${mta_host}." | sudo tee -a ${masquerade_domains}
cat ${local_host_name} | sudo tee ${masquerade_domains}
# for debug
cat ${masquerade_domains}


#
# make relay-domains file
#
relay_domains="${basedir}/relay-domains"
echo "${domain_name}" | sudo tee -a ${relay_domains}
# for debug
cat ${relay_domains}

#
# make trusted-users file
#
trusted_user_file="${basedir}/trusted-users"
if [ ! -e ${trusted_user_file} ] ; then
  touch ${trusted_user_file}
fi

has_root=$(grep ^root\$ ${trusted_user_file})
if [ -z "${has_root}" ] ; then
  echo "root" | sudo tee -a ${trusted_user_file}
fi
has_smmta=$(grep ^smmta\$ ${trusted_user_file})
if [ -z "${has_smmta}" ] ; then
  echo "smmta" | sudo tee -a ${trusted_user_file}
fi
has_smmsp=$(grep ^smmsp\$ ${trusted_user_file})
if [ -z "${has_smmsp}" ] ; then
  echo "smmsp" | sudo tee -a ${trusted_user_file}
fi
# for debug
cat ${trusted_user_file}

#
# precheck
#
if [ -z "${test_mail_addr}" ] ; then
  echo "test mail address not found."
  exit 1
fi

##
## show default settings about sendmail.mc and submit.mc ( if you need them )
##
#echo -n "This is a test for sendmail.mc." | sudo -u smmta -g smmsp /usr/lib/sendmail -Am ${sendmailopts} ${test_mail_addr}
#cat /etc/mail/sendmail.mc
#echo -n "This is a test for submit.mc."   | sudo -u smmsp -g smmsp /usr/lib/sendmail -Ac ${sendmailopts} ${test_mail_addr}
#cat /etc/mail/submit.mc

##
## configure sendmail.mc
##
original_sendmail_mc="${basedir}/sendmail.mc"
backup_sendmail_mc="${basedir}/sendmail.mc.${today}"

#
# modify sendmail.mc
#
sudo mv ${original_sendmail_mc} ${backup_sendmail_mc}
trap "if [ ! -e ${original_sendmail_mc} ] ; then ; mv ${backup_sendmail_mc} ${original_sendmail_mc}" EXIT

cat <<'EOF' > ${original_sendmail_mc}
divert(-1)dnl
#-----------------------------------------------------------------------------
# $Sendmail: debproto.mc,v 8.15.2 2020-03-08 00:39:49 cowboy Exp $
#
# Copyright (c) 1998-2010 Richard Nelson.  All Rights Reserved.
#
# cf/debian/sendmail.mc.  Generated from sendmail.mc.in by configure.
#
# sendmail.mc prototype config file for building Sendmail 8.15.2
#
# Note: the .in file supports 8.7.6 - 9.0.0, but the generated
#       file is customized to the version noted above.
#
# This file is used to configure Sendmail for use with Debian systems.
#
# If you modify this file, you will have to regenerate /etc/mail/sendmail.cf
# by running this file through the m4 preprocessor via one of the following:
#       * make   (or make -C /etc/mail)
#       * sendmailconfig
#       * m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
# The first two options are preferred as they will also update other files
# that depend upon the contents of this file.
#
# The best documentation for this .mc file is:
# /usr/share/doc/sendmail-doc/cf.README.gz
#
#-----------------------------------------------------------------------------
divert(0)dnl
#
#   Copyright (c) 1998-2005 Richard Nelson.  All Rights Reserved.
#
#  This file is used to configure Sendmail for use with Debian systems.
#
define(`_USE_ETC_MAIL_')dnl
include(`/usr/share/sendmail/cf/m4/cf.m4')dnl
include(`/etc/mail/tls/starttls.m4')dnl
VERSIONID(`$Id: sendmail.mc, v 8.15.2-18 2020-03-08 00:39:49 cowboy Exp ')
OSTYPE(`debian')dnl
DOMAIN(`debian-mta')dnl
dnl # Items controlled by /etc/mail/sendmail.conf - DO NOT TOUCH HERE
undefine(`confHOST_STATUS_DIRECTORY')dnl        #DAEMON_HOSTSTATS=
dnl # Items controlled by /etc/mail/sendmail.conf - DO NOT TOUCH HERE
dnl #
dnl # General defines
dnl #
dnl # SAFE_FILE_ENV: [undefined] If set, sendmail will do a chroot()
dnl #   into this directory before writing files.
dnl #   If *all* your user accounts are under /home then use that
dnl #   instead - it will prevent any writes outside of /home !
dnl #   define(`confSAFE_FILE_ENV',             `')dnl
dnl #
dnl # Daemon options - restrict to servicing LOCALHOST ONLY !!!
dnl # Remove `, Addr=' clauses to receive from any interface
dnl # If you want to support IPv6, switch the commented/uncommentd lines
dnl #
define(`confDOMAIN_NAME', `${domain_name}')dnl
define(`confRUN_AS_USER', `smmta:smmsp')dnl
define(`LOCAL_USER', `root daemon smmsp smmta postmaster mail')
define(`SMART_HOST', `${mta_host}')dnl

MASQUERADE_AS(`${domain_name}')
MASQUERADE_DOMAIN_FILE(`${masquerade_domains}')

FEATURE(`use_cw_file')dnl
FEATURE(`no_default_msa')dnl
FEATURE(`always_add_domain')
FEATURE(`masquerade_entire_domain')
FEATURE(`masquerade_envelope')
FEATURE(`allmasquerade')
FEATURE(`limited_masquerade')
FEATURE(`access_db', `hash -T<TMPF> -o /etc/mail/access')

DAEMON_OPTIONS(`Family=inet,  Name=MTA-v4, Port=smtp, Addr=127.0.0.1')dnl
DAEMON_OPTIONS(`Family=inet,  Name=MSP-v4, Port=submission, M=Ea, Addr=127.0.0.1')dnl

MAILER(`local')
MAILER(`smtp')
EOF

sed -i -e "s#\${domain_name}#${domain_name}#"               ${original_sendmail_mc}
sed -i -e "s#\${mta_host}#${mta_host}#"                     ${original_sendmail_mc}
sed -i -e "s#\${mta_port}#${mta_port}#"                     ${original_sendmail_mc}
sed -i -e "s#\${local_fqdn}#${local_fqdn}#"                 ${original_sendmail_mc}
sed -i -e "s#\${masquerade_domains}#${masquerade_domains}#" ${original_sendmail_mc}
sed -i -e "s#\${local_short}#${local_short}#"               ${original_sendmail_mc}

# for debug
sudo cat ${original_sendmail_mc}

##
## configure submit.mc
##
original_submit_mc="${basedir}/submit.mc"
backup_submit_mc="${basedir}/submit.mc.${today}"

sudo mv ${original_submit_mc} ${backup_submit_mc}
trap "if [ ! -e ${original_submit_mc} ] ; then ; mv ${backup_submit_mc} ${original_submit_mc}" EXIT

cat <<'EOF' > ${original_submit_mc}
divert(-1)dnl
#-----------------------------------------------------------------------------
# $Sendmail: submit.mc,v 8.15.2 2020-03-08 00:39:49 cowboy Exp $
#
# Copyright (c) 2000-2010 Richard Nelson.  All Rights Reserved.
#
# cf/debian/submit.mc.  Generated from submit.mc.in by configure.
#
# submit.mc prototype config file for building Sendmail 8.15.2
#
# Note: the .in file supports 8.7.6 - 9.0.0, but the generated
#       file is customized to the version noted above.
#
# This file is used to configure Sendmail for use with Debian systems.
#
# If you modify this file, you will have to regenerate /etc/mail/submit.cf
# by running this file through the m4 preprocessor via one of the following:
#       * make  (or make -C /etc/mail)
#       * sendmailconfig
#       * m4 /etc/mail/submit.mc > /etc/mail/submit.cf
# The first two options are preferred as they will also update other files
# that depend upon the contents of this file.
#
# The best documentation for this .mc file is:
# /usr/share/doc/sendmail-doc/cf.README.gz
#
#-----------------------------------------------------------------------------
divert(0)dnl
#
#   Copyright (c) 2000-2002 Richard Nelson.  All Rights Reserved.
#
#  This file is used to configure Sendmail for use with Debian systems.
#
define(`_USE_ETC_MAIL_')dnl
define(`confDOMAIN_NAME', `${local_fqdn}')dnl
define(`confRUN_AS_USER', `smmsp:smmsp')dnl
define(`LOCAL_USER', `root daemon smmsp smmta postmaster mail')dnl
include(`/usr/share/sendmail/cf/m4/cf.m4')dnl
include(`/etc/mail/tls/starttls.m4')dnl
VERSIONID(`$Id: submit.mc, v 8.15.2-18 2020-03-08 00:39:49 cowboy Exp $')
OSTYPE(`debian')dnl
DOMAIN(`debian-msp')dnl
dnl #
dnl #---------------------------------------------------------------------
dnl # Masquerading information, if needed, should go here
dnl # You likely will not need this, as the MTA will do it
dnl #---------------------------------------------------------------------
dnl MASQUERADE_AS()dnl
dnl FEATURE(`masquerade_envelope')dnl
dnl #
dnl #---------------------------------------------------------------------
dnl # The real reason we're here: the FEATURE(msp)
dnl # NOTE WELL:  MSA (587) should have M=Ea, so we need to use stock 25
dnl #---------------------------------------------------------------------
MASQUERADE_DOMAIN_FILE(`${masquerade_domains}'))
MASQUERADE_AS(`${domain_name}')
FEATURE(`use_cw_file')dnl
FEATURE(`masquerade_envelope')dnl
FEATURE(`always_add_domain')
FEATURE(`masquerade_entire_domain')
FEATURE(`allmasquerade')
FEATURE(`limited_masquerade')
FEATURE(`msp', `[127.0.0.1]', `25')dnl
dnl #
dnl #---------------------------------------------------------------------
dnl # Some minor cleanup from FEATURE(msp)
dnl #---------------------------------------------------------------------
dnl #
dnl #---------------------------------------------------------------------
EOF

sed -i -e "s#\${domain_name}#${domain_name}#"               ${original_submit_mc}
sed -i -e "s#\${mta_host}#${mta_host}#"                     ${original_submit_mc}
sed -i -e "s#\${mta_port}#${mta_port}#"                     ${original_submit_mc}
sed -i -e "s#\${local_fqdn}#${local_fqdn}#"                 ${original_submit_mc}
sed -i -e "s#\${local_short}#${local_short}#"               ${original_submit_mc}
sed -i -e "s#\${masquerade_domains}#${masquerade_domains}#" ${original_submit_mc}

# for debug
sudo cat ${original_submit_mc}


# run "sudo sendmailconfig"
# if you config sendmail.mc and submit.mc, then run "sudo sendmailconfig"
sctmpfile=$(mktemp /tmp/XXXXXX.expect)
trap "rm -f ${sctmpfile}" EXIT
cat <<EOF > ${sctmpfile}
spawn sudo sendmailconfig
sleep 1
expect {"/sendmail.conf? [Y]"}
sleep 1
send "y\r"
expect {"sendmail.mc? [Y]"}
sleep 1
send "y\r"
expect {"new configuration? [Y]"}
sleep 1
send "y\r"
sleep 1
expect eof
EOF
sudo expect ${sctmpfile}

# enable sendmail daemon
sudo systemctl enable sendmail
sudo systemctl start sendmail

# make test_mail.sh
homedir=$(getent passwd $(whoami) | awk -F: '{print $6;}')
test_script=${homedir}/test_mail.sh

cat <<EOF > ${test_script}
#!/bin/bash
#
# test_mail.sh
#
# usage: test_mail.sh <mail addr> [<sendmail options>]
#

# command runner
runner=\$(whoami)
curtime=\$(TZ='Asia/Tokyo' date "+%Y/%m/%d %H:%M:%S")

#
# set command runner
# usage: set_runner ["-Am"|"-Ac"]
# "-Am" : use sendmail.cf
# "-Ac" : use submit.cf
# example: check_runner "-Am"
# default : "-Am"
#
function set_runner() {
  runner=\$(echo "\${runner}" | awk -F' ' '{print \$1;}')
  mode=\${1:-"-Am"}
  optlist=\$(sudo /usr/lib/sendmail \${mode} -v -d37.1 < /dev/null)
  queuedir=\$(echo -e "\${optlist}" | grep -i -e "QueueDirectory" | awk -F' ' '{print \$3;}' | sed -e "s#^.*\=##")
  queuegrp=\$(sudo ls -al -d --dereference \${queuedir}  | awk -F' ' '{print \$4;}')
  queueuser=\$(sudo ls -al -d --dereference \${queuedir} | awk -F' ' '{print \$3;}')
  has_group=\$(id -a "\${runner}" | grep -i \${queuegrp})
  if [ -z "\${has_group}" ] ; then
    runner="\${queueuser} -g \${queuegrp}"
  else
    runner="\${queueuser}"
  fi
}


#
# test for sendmail.cf
#
toaddr=\$1
sendmailopts=\${2:-"${sendmailopts}"}

msg="To: <\${toaddr}>
Subject: test ( by \${runner} at \${curtime} )

This is a test mail.
."

#
# test for sendmail.cf
#
set_runner "-Am"
echo -e "\${msg}" | sudo -u \${runner} /usr/lib/sendmail -Am \${sendmailopts} \${toaddr}
echo -e "exit status ( sendmail.cf ) : \$?"

#
# test for submit.cf
#
set_runner "-Ac"
echo -e "\${msg}" | sudo -u \${runner} /usr/lib/sendmail -Ac \${sendmailopts} \${toaddr}
echo -e "exit status ( submit.cf ) : \$?"
EOF

# for debug
sudo cat ${test_script}

# test test_mail.sh
sudo bash -x ${test_script} ${test_mail_addr}

client ( sendmail ) の構築

上記ひな形をそのまま実行することで、何も設定していない状態での smtp-relay.gmail.com との通信を確認することができます。

この時点での sendmail.mc および submit.mc は以下の通り

コメントは抜いています

sendmail.mc

define(`_USE_ETC_MAIL_')dnl
include(`/usr/share/sendmail/cf/m4/cf.m4')dnl
include(`/etc/mail/tls/starttls.m4')dnl
VERSIONID(`$Id: sendmail.mc, v 8.15.2-18 2020-03-08 00:39:49 cowboy Exp ')
OSTYPE(`debian')dnl
DOMAIN(`debian-mta')dnl

undefine(`confHOST_STATUS_DIRECTORY')dnl

define(`confDOMAIN_NAME', `${domain_name}')dnl
define(`confRUN_AS_USER', `smmta:smmsp')dnl
define(`LOCAL_USER', `root daemon smmsp smmta postmaster mail')
define(`SMART_HOST', `smtp-relay.gmail.com.')dnl

MASQUERADE_AS(`${domain_name}')
MASQUERADE_DOMAIN_FILE(`/etc/mail/masquerade-domains')

FEATURE(`use_cw_file')dnl
FEATURE(`no_default_msa')dnl
FEATURE(`always_add_domain')
FEATURE(`masquerade_entire_domain')
FEATURE(`masquerade_envelope')
FEATURE(`allmasquerade')
FEATURE(`limited_masquerade')
FEATURE(`access_db', `hash -T<TMPF> -o /etc/mail/access')

DAEMON_OPTIONS(`Family=inet,  Name=MTA-v4, Port=smtp, Addr=127.0.0.1')dnl
DAEMON_OPTIONS(`Family=inet,  Name=MSP-v4, Port=submission, M=Ea, Addr=127.0.0.1')dnl

MAILER(`local')
MAILER(`smtp')

submit.mc

define(`_USE_ETC_MAIL_')dnl
define(`confDOMAIN_NAME', `${local_fqdn}')dnl
define(`confRUN_AS_USER', `smmsp:smmsp')dnl
define(`LOCAL_USER', `root daemon smmsp smmta postmaster mail')dnl
include(`/usr/share/sendmail/cf/m4/cf.m4')dnl
include(`/etc/mail/tls/starttls.m4')dnl
VERSIONID(`$Id: submit.mc, v 8.15.2-18 2020-03-08 00:39:49 cowboy Exp $')
OSTYPE(`debian')dnl
DOMAIN(`debian-msp')dnl

MASQUERADE_DOMAIN_FILE(`/etc/mail/masquerade-domains'))
MASQUERADE_AS(`${domain_name}')
FEATURE(`use_cw_file')dnl
FEATURE(`masquerade_envelope')dnl
FEATURE(`always_add_domain')
FEATURE(`masquerade_entire_domain')
FEATURE(`allmasquerade')
FEATURE(`limited_masquerade')
FEATURE(`msp', `[127.0.0.1]', `25')dnl

実行する前に以下の点を確認してください

  • example.com の箇所は所有している実ドメインを指定してください
  • foo@example.com の箇所は所有しているメールアドレスを指定してください
  • bootstrap.sh および test_mail.sh のデフォルトでの sendmail のオプションは sendmail -d を実行することに注意してください。つまり、大量の sendmail のデバッグ情報が流れます。通常は bootstrap.sh のオプションとして "-v" を指定するとよいでしょう
  • 実行結果 ( bootstrap.sh を実行することで作成される /root/test_mail.shbash /root/test_mail.sh var@example.com "-v" として実行した場合に、smtp-relay.gmail.com とのやり取りのみをコピー )

sendmai.mc の場合

foo@example.com... Connecting to smtp-relay.gmail.com. via relay...
220 smtp-relay.gmail.com ESMTP e19-20020a056122131300b004b74b27053dsm617387vkp.6 - gsmtp
>>> EHLO example.com.
250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
>>> STARTTLS
220 2.0.0 Ready to start TLS
>>> EHLO example.com.
250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
250-SIZE 157286400
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
>>> MAIL From:<smmta@example.com> SIZE=95 AUTH=smmta@example.com.
550-5.7.1 Invalid credentials for relay [XXX.XXX.XXX.XXX]. The IP address you've
550-5.7.1 registered in your Workspace SMTP Relay service doesn't match the
550-5.7.1 domain of the account this email is being sent from. If you are
550-5.7.1 trying to relay mail from a domain that isn't registered under your
550-5.7.1 Workspace account or has empty envelope-from, you must configure your
550-5.7.1 mail server either to use SMTP AUTH to identify the sending domain or
550-5.7.1 to present one of your domain names in the HELO or EHLO command. For
550-5.7.1 more information, go to
550 5.7.1  https://support.google.com/a/answer/6140680#invalidcred e19-20020a056122131300b004b74b27053dsm617387vkp.6 - gsmtp
/var/lib/sendmail/dead.letter... Saved message in /var/lib/sendmail/dead.letter
Closing connection to smtp-relay.gmail.com.
>>> QUIT

submit.mc の場合

foo@example.com... Connecting to [127.0.0.1] via relay...
220 example.com. ESMTP Sendmail 8.15.2/8.15.2/Debian-18; Wed, 21 Feb 2024 07:43:12 GMT; (No UCE/UBE) logging access from: [127.0.0.1](FORGED)-smtp-relay-client.example.com. [127.0.0.1] (may be forged)
>>> EHLO smtp-relay-client.example.com.
250-example.com. Hello smtp-relay-client.example.com. [127.0.0.1] (may be forged), pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-EXPN
250-VERB
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-AUTH DIGEST-MD5 CRAM-MD5
250-STARTTLS
250-DELIVERBY
250 HELP
>>> VERB
250 2.0.0 Verbose mode
>>> STARTTLS
220 2.0.0 Ready to start TLS
>>> EHLO smtp-relay-client.example.com.
250-example.com. Hello smtp-relay-client.example.com. [127.0.0.1] (may be forged), pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-EXPN
250-VERB
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-AUTH DIGEST-MD5 CRAM-MD5
250-DELIVERBY
250 HELP
>>> VERB
250 2.0.0 Verbose mode
>>> MAIL From:<smmsp@example.com> SIZE=95 AUTH=smmsp@smtp-relay-client.example.com.
250 2.1.0 <smmsp@example.com>... Sender ok
>>> RCPT To:<foo@example.com>
>>> DATA
250 2.1.5 <foo@example.com>... Recipient ok
354 Enter mail, end with "." on a line by itself
>>> .
050 <foo@example.com>... Connecting to smtp-relay.gmail.com. via relay...
050 220 smtp-relay.gmail.com ESMTP jp7-20020ad45f87000000b0068fa300e9c8sm32215qvb.48 - gsmtp
050 >>> EHLO example.com.
050 250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
050 250-SIZE 157286400
050 250-8BITMIME
050 250-STARTTLS
050 250-ENHANCEDSTATUSCODES
050 250-PIPELINING
050 250-CHUNKING
050 250 SMTPUTF8
050 >>> STARTTLS
050 220 2.0.0 Ready to start TLS
050 >>> EHLO example.com.
050 250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
050 250-SIZE 157286400
050 250-8BITMIME
050 250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
050 250-ENHANCEDSTATUSCODES
050 250-PIPELINING
050 250-CHUNKING
050 250 SMTPUTF8
050 >>> MAIL From:<smmsp@example.com> SIZE=414 AUTH=<>
050 550-5.7.1 Invalid credentials for relay [XXX.XXX.XXX.XXX]. The IP address you've
050 550-5.7.1 registered in your Workspace SMTP Relay service doesn't match the
050 550-5.7.1 domain of the account this email is being sent from. If you are
050 550-5.7.1 trying to relay mail from a domain that isn't registered under your
050 550-5.7.1 Workspace account or has empty envelope-from, you must configure your
050 550-5.7.1 mail server either to use SMTP AUTH to identify the sending domain or
050 550-5.7.1 to present one of your domain names in the HELO or EHLO command. For
050 550-5.7.1 more information, go to
050 550 5.7.1  https://support.google.com/a/answer/6140680#invalidcred jp7-20020ad45f87000000b0068fa300e9c8sm32215qvb.48 - gsmtp
050 <smmsp@example.com>... Using cached ESMTP connection to smtp-relay.gmail.com. via relay...
050 >>> RSET
050 <smmsp@example.com>... Deferred: Connection reset by smtp-relay.gmail.com.
250 2.0.0 41L7hCSd013473 Message accepted for delivery
foo@example.com... Sent (41L7hCSd013473 Message accepted for delivery)
Closing connection to [127.0.0.1]
>>> QUIT
221 2.0.0 example.com. closing connection

エラー文中に登場する URL はいずれも同じもの ( 下記サイト ) を指しています。

Google の設定を変更し、smtp-relay.gmail.com を使用するように修正する

手順は以下の URL を参考に ( 再掲 )

0. 事前準備

アクセス元の IP アドレスを確認する。
client ( sendmail ) の構築の際に smtp-relay.gmail.com からのレスポンスに IP アドレスが表示されているのでそれを控えておく。
下記の例では XXX.XXX.XXX.XXX がアクセス元の IP アドレスになる。

例 : 050 250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]

1. Google の管理コンソールに Workspace の管理者アカウントでログインする

URL は以下の通り

2. 管理コンソールから「アプリ」→ 「Google Workspace」→「Gmail」→「コンプライアンス」にアクセス

004.png

3. 「包括的なメールストレージ」の鉛筆アイコンをクリック

005.png

4. 「関連付けられているユーザーのメールボックスに、すべての送受信メールのコピーを保存します。」にチェックを入れた後に、「オーバーライド」をクリック

006.png

5. 管理コンソールのトップページに戻り、「アプリ」→「Google Workspace」→「Gmail」→「ルーティング」にアクセスする。

007.png

6. 新規に SMTP リレーサービスを追加する

「別のルールを追加」をクリック。

008.png

ポップアップしたダイアログに以下の内容を記入 ( 1 )

  • 設定名
    今回は「Google サーバー経由の SMTP リレー用」とした
  • 許可する送信者
    今回は「ドメイン内のアドレスのみ」とした
  • 認証
    「指定したアドレスからのみメールを受信する」を選択

009.png

ポップアップしたダイアログに以下の内容を記入 ( 2 )

  • IP アドレスまたは IP 範囲

「追加」をクリック

010.png

表示されたダイアログに以下の内容を記入

011.png

    • 説明
      今回は「Test for example.com」とした ( 実際には example.com の箇所は実ドメインを記載 )
    • IP アドレスまたは IP アドレス範囲を入力
      手順 0 で控えた IP アドレス ( XXX.XXX.XXX.XXX ) を入力 ( 実際には使用している Global IP を入力 )

入力後「保存」をクリック

  • SMTP 認証を必須にする
    今回はチェックしない
  • 暗号化
    チェックする

全て入力後「保存」をクリック

012.png

メールの送信チェック

sudo bash -x /root/test_mail.sh foo@example.com "-v" の実行結果

sendmail.mc の結果

foo@example.com... Connecting to smtp-relay.gmail.com. via relay...
220 smtp-relay.gmail.com ESMTP q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp
>>> EHLO example.com.
250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
>>> STARTTLS
220 2.0.0 Ready to start TLS
>>> EHLO example.com.
250-smtp-relay.gmail.com at your service, [XXX.XXX.XXX.XXX]
250-SIZE 157286400
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
>>> MAIL From:<smmta@example.com> SIZE=95 AUTH=smmta@example.com.
250 2.1.0 OK q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp
>>> RCPT To:<foo@example.com>
>>> DATA
250 2.1.5 OK q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp
354  Go ahead q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp
>>> .
250 2.0.0 OK  1708757786 q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp
foo@example.com... Sent (OK  1708757786 q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp)
Closing connection to smtp-relay.gmail.com.
>>> QUIT
221 2.0.0 closing connection q123-20020a0de781000000b006048ac4286esm46191ywe.71 - gsmtp

うまくいっているようです。

届いたメールの確認

014.png

明示的に SPF や DKIM を設定していませんが、gmail ではいずれも PASS という判定になっています。

届いた内容のメール情報

reverse-lookup.example.com は Google 側のサーバで IP からの逆引きで得られた FQDN です。
* は任意のアルファベットとアラビア数字 ( [A-Za-z0-9] ) です。

Delivered-To: foo@example.com
Received: by XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX with SMTP id gx9csp512376mdb;
        Fri, 23 Feb 2024 22:56:28 -0800 (PST)
X-Received: by XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX with SMTP id m145-20020a0dca97000000b006086ea5d555mr1776901ywd.41.1708757787768;
        Fri, 23 Feb 2024 22:56:27 -0800 (PST)
ARC-Seal: i=1; a=rsa-sha256; t=1708757787; cv=none;
        d=google.com; s=arc-20160816;
        b=******************************************************************
         *************+************************************+*************+***
         ********************************+************+**********************
         **************/*****************************************************
         ***************************+****************************************
         ****==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
        h=subject:to:from:date:message-id:dkim-signature;
        bh=******************/*+**********************=;
        fh=*******/************+*******+**************=;
        b=*****+******/******************************+****************/*****
         ******/*************************************************+***********
         **+*********************************************************+*******
         ********************************************************************
         +*****************************************/+************************
         ****==;
        dara=google.com
ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@example.com header.s=google header.b=gh7btfLD;
       spf=pass (google.com: domain of smmta@example.com designates XXX.XXX.XXX.XXX as permitted sender) smtp.mailfrom=smmta@example.com
Return-Path: <smmta@example.com>
Received: from mail-XXX-XXX.google.com (mail-XXX-XXX.google.com. [XXX.XXX.XXX.XXX])
        by mx.google.com with SMTPS id g12-20020a0ddd0c000000b00608d9fb3b62sor242ywe.19.2024.02.23.22.56.26
        for <foo@example.com>
        (Google Transport Security);
        Fri, 23 Feb 2024 22:56:27 -0800 (PST)
Received-SPF: pass (google.com: domain of smmta@example.com designates XXX.XXX.XXX.XXX as permitted sender) client-ip=XXX.XXX.XXX.XXX;
Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com header.s=google header.b=gh7btfLD;
       spf=pass (google.com: domain of smmta@example.com designates XXX.XXX.XXX.XXX as permitted sender) smtp.mailfrom=smmta@example.com
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=example.com; s=google; t=1708757786; x=1709362586; darn=example.com;
        h=subject:to:from:date:message-id:from:to:cc:subject:date:message-id
         :reply-to;
        bh=******************/*+**********************=;
        b=***************************************/**************************
         ********+****************************/******************************
         *************************************=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20230601; t=1708757786; x=1709362586;
        h=subject:to:from:date:message-id:x-gm-message-state:from:to:cc
         :subject:date:message-id:reply-to;
        bh=******************/*+**********************=;
        b=**+********+************************************++****************
         ********************************************************************
         ************************/***********/*******************************
         ***********************************************+**************+*****
         **************+*****************************************************
         ****==
X-Gm-Message-State: *******************+********************************/*** ********************************+********+********************************* *****+****************/***************+*****=
X-Google-Smtp-Source: AGHT+IEzwMYreqav2RcKboP1394uLjee7F7XDa7Se8U0XtlBBO1C27WcrZjQK209krmfwGfvSNr5ty+ml9jU
X-Received: by XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX with SMTP id df8-20020a05690c0f8800b0060820df8b15mr2056183ywb.3.1708757786345;
        Fri, 23 Feb 2024 22:56:26 -0800 (PST)
Return-Path: <smmta@example.com>
Received: from example.com. (reverse-lookup.example.com. [XXX.XXX.XXX.XXX])
        by smtp-relay.gmail.com with ESMTPS id q123-20020a0de781000000b006048ac4286esm46191ywe.71.2024.02.23.22.56.25
        for <foo@example.com>
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Fri, 23 Feb 2024 22:56:26 -0800 (PST)
X-Relaying-Domain: example.com
Message-ID: <65d9931a.0d0a0220.25b0a.3494SMTPIN_ADDED_BROKEN@mx.google.com>
X-Google-Original-Message-ID: <202402240656.41O6uN8R151018@example.com.>
Received: (from smmta@localhost) by example.com. (8.15.2/8.15.2/Debian-18) id 41O6uN8R151018 for foo@example.com; Sat, 24 Feb 2024 06:56:23 GMT
Date: Sat, 24 Feb 2024 06:56:23 GMT
From: Mail Transfer Agent <smmta@example.com>
To: <foo@example.com>
Subject: test ( by root at 2024/02/24 15:55:22 )

This is a test mail.

@gmail.com 宛てのメールも上図のような結果になっていることを確認。

投げられるメールの特徴としては Message-IDSMTPIN_ADDED_BROKEN@mx.google.com という文字列が含まれている点です。

設定の効果

SPF/DKIM/DMARC 関係の情報に注目してみます。

SPF/DKIM/DMARC の情報については以下の URL を参照。

DNS の対応

今回検証した実ドメインは、さくらインターネットで提供している DNS を利用しています。
今回確認した限りでは ACME DNS-01 の検証に対応しており、challenge が成功したら spf レコードの書き換えを行ってくれるようです。
当然ながら DNS を提供しているサービスによっては ACME DNS-01 に対応していない場合もありますので注意。

SPF

今回の検証環境では以下のような情報が設定されているようです。

dig txt example.com

(以下レスポンスの抜粋)
example.com.             3600    IN      TXT     "v=spf1 include:_spf.example.com include:_spf.google.com ~all"

~all とされているので、「それ以外から送信された場合は softfail とする」となります。

DKIM

例えば dig txt <selector>._domainkey.example.com. のような形式で DNS から引っ張ってきます
<selector> で指定する文字列は、メールの DKIM-Signature ヘッダ内に含まれる、s= の箇所で指定されています。
今回の検証環境では以下のような情報が設定されているようです。

<selector> に指定している箇所は実際には文字列 ( [A-Za-z0-9]+ ) です。

dig txt <selector>._domainkey.example.com.

(以下レスポンスの抜粋)

<selector>._domainkey.example.com. 3600 IN   TXT     "v=DKIM1; k=rsa; p==********************************************************************+************************************/********/*********************************+***********/********************************+***********+**********"

DMARC

dig txt _dmarc.example.com の結果は空でしたが、gmail 側で独自に DMARC ポリシーを設定している余地はあります。

ARC

ARCについては以下の URL を参照しました。

詳細は調べてませんが、メール内に ARC- ヘッダが付きます。

結論 ?

とりあえず、google で提供している機能を使えば大体のケースにおいて使えるとは思います。
但し、より厳密な制御を求めるのであれば DNS も含めたメールシステムの微調整は必要なようです。

1
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
1
1