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 にアクセスする
ここでは
-
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.sh
をbash /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」→「コンプライアンス」にアクセス
3. 「包括的なメールストレージ」の鉛筆アイコンをクリック
4. 「関連付けられているユーザーのメールボックスに、すべての送受信メールのコピーを保存します。」にチェックを入れた後に、「オーバーライド」をクリック
5. 管理コンソールのトップページに戻り、「アプリ」→「Google Workspace」→「Gmail」→「ルーティング」にアクセスする。
6. 新規に SMTP リレーサービスを追加する
「別のルールを追加」をクリック。
ポップアップしたダイアログに以下の内容を記入 ( 1 )
- 設定名
今回は「Google サーバー経由の SMTP リレー用」とした - 許可する送信者
今回は「ドメイン内のアドレスのみ」とした - 認証
「指定したアドレスからのみメールを受信する」を選択
ポップアップしたダイアログに以下の内容を記入 ( 2 )
- IP アドレスまたは IP 範囲
「追加」をクリック
表示されたダイアログに以下の内容を記入
-
- 説明
今回は「Test for example.com」とした ( 実際には example.com の箇所は実ドメインを記載 )
- 説明
-
- IP アドレスまたは IP アドレス範囲を入力
手順 0 で控えた IP アドレス ( XXX.XXX.XXX.XXX ) を入力 ( 実際には使用している Global IP を入力 )
- IP アドレスまたは IP アドレス範囲を入力
入力後「保存」をクリック
- SMTP 認証を必須にする
今回はチェックしない - 暗号化
チェックする
全て入力後「保存」をクリック
メールの送信チェック
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
うまくいっているようです。
届いたメールの確認
明示的に 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-ID
に SMTPIN_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 も含めたメールシステムの微調整は必要なようです。