ToC
メールサーバーをマネージドサービスで…
「マネージドサービスを使うことでサーバー管理の手間を減らしたい」というのはよく聞く話です。
Alibaba DirectMail や Amazon SES など、多くのマネージドサービスもREST API
やイベント連携
によるメール機能を提供しています。
これらを利用すると、アプリケーションからAPIコールで直接メールが送信できるので非常に簡単です。
そして、アプリケーションの他にもProxy下のネットワーク環境で、メール通知するツール群があります。
ツール群の多くは、API
ではなく、標準的なSMTP
プロトコルを利用してメールを送信しているものが多くあります。
今回は、これらもすべて対応して、メールサーバーを完全に無くしてしまおう
と欲を出すという話です。
メールサーバーへのアクセス用のテストプログラム
PySocksというモジュールを使って、SMTP通信をProxyサーバー経由で送信ができるようです。
今回は、サンプルコードを公開されていたPythonでメール送信 のコードをベースに詳細の説明を確認しながら
Proxy経由でのメール送信のテストプログラムを作成しました。
(サンプルの公開、ありがとうございます)
import smtplib
import ssl
import socks # PySocks
from email.mime.text import MIMEText
from email.utils import formatdate
FROM_ADDRESS = 'from-address@example.tokyo'
MY_PASSWORD = 'put-your-password-here'
SMTP_SERVER = 'smtpdm-ap-southeast-1.aliyun.com'
SMTP_PORT = 465
# for Squid
HTTP_PROXY_HOST = 'localhost'
HTTP_PROXY_PORT = 3128
# for Dante
SOCKS_PROXY_HOST = 'localhost'
SOCKS_PROXY_PORT = 1080
def create_message(from_addr, to_addr, subject, body):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = to_addr
msg['Date'] = formatdate()
return msg
def send_mail(body_msg, context=None):
_context = context if context is not None else ssl.create_default_context()
nego_combo = ("ssl", SMTP_PORT)
client = smtplib.SMTP_SSL(SMTP_SERVER, nego_combo[1], timeout=10, context=_context)
client.set_debuglevel(2)
client.login(FROM_ADDRESS, MY_PASSWORD)
client.send_message(body_msg)
client.quit()
if __name__ == '__main__':
to_address = 'to-address@example.yokohama'
msg = create_message(FROM_ADDRESS, to_address, 'Hello, world', 'テストメールです。')
# for Squid
socks.setdefaultproxy(socks.HTTP, HTTP_PROXY_HOST, HTTP_PROXY_PORT)
socks.wrapmodule(smtplib)
socks_context = ssl.create_default_context()
send_mail(msg, socks_context)
# for Dante
socks.setdefaultproxy(socks.SOCKS5, SOCKS_PROXY_HOST, SOCKS_PROXY_PORT)
socks.wrapmodule(smtplib)
socks_context = ssl.create_default_context()
send_mail(msg, socks_context)
Proxyについて、少し勉強してみた
SOCKSプロキシとHTTPプロキシの違いについて勉強してみた の記事を読んで、Squid
のような Http Proxy
(L6) の他に
Socks Proxy
(L4)があるということだったので、今回は両方を試してみることにしました。
記事では、Socks Proxy
として、delegate
というプロダクトを使って検証されていたのですが、
筆者の方が「20世紀的発送」と少し自虐的に書いておられたので、 他のプロダクトも調査してみました。
2021年 現在、継続的にメンテナンスされているプロダクトとして、DanteというSocks Proxy
が良い感じだったので、今回はこれを使ってみました。
Proxy経由でのメール送信
Squidアクセス
Squid
には、通信を許可するプロトコルやアクセス先を設定するAccess Control List (acl)
があり、SMTPのポートを追加してみます。
デフォルトで、各種ポートの設定が、/etc/squid/squid.conf
に設定されると思いますが、これに追加して下記の設定も追加してみます。
squid-acl.conf
acl SSL_ports port 465
acl Safe_ports port 465
テストプログラムを実行してみると、Squid
のProxyを経由して、Alibaba DirectMailのSMTPを使った
メール送信ができました。Dockerで構築したので、参考までにDockerfileは下記です。
squid_1 | 172.19.0.1 - - [27/Jun/2021:05:39:17 +0000] "CONNECT 47.88.198.4:465 HTTP/1.1" 200 5092 "-" "-" TCP_TUNNEL:HIER_DIRECT
Danteアクセス
Dante
にも、通信制御のためのAccess Control List (acl)
があり、SMTPのポートを追加してみます。
アクセス先のエンドポイントの制御もできそうでしたので、少し試してみました。
sockd.conf
debug: 0
logoutput: stdout
internal: 0.0.0.0 port = 1080
external: eth0
socksmethod: username none
clientmethod: none
user.privileged: root
user.unprivileged: nobody
client pass {
from: 0.0.0.0/0 port 1-65535 to: 0.0.0.0/0 port 1-65535
log: error
}
socks pass {
from: 0.0.0.0/0 to: smtpdm-ap-southeast-1.aliyun.com
log: ioop
}
こちらもテストプログラムを実行してみると、Dante
のSocks Proxyを経由して、メール送信ができました。
Dockerfileは下記です。
dante_1 | Jun 27 07:21:11 (1624778471.680951) sockd[80]: info: pass(2): tcp/connect -: 172.21.0.1.62300 172.21.0.2.1080 -> 172.21.0.2.62300 47.88.198.4.465 (517)
dante_1 | Jun 27 07:21:11 (1624778471.761069) sockd[80]: info: pass(2): tcp/connect -: 47.88.198.4.465 172.21.0.2.62300 -> 172.21.0.2.1080 172.21.0.1.62300 (4184)
結論
Proxy経由で、マネージドサービスのSMTPプロトコルを利用することは可能でした。 マネージドサービスを利用するために追加の構成要素が必要になると本末転倒なので、そういう意味では良かったです。
ただ、HTTP_PROXY
のように環境変数に設定するだけで動作すると良いのですが、サードパーティ製のアプリケーションが対応しているかどうかはわかりません。
このあたりは、継続調査が必要そうですね。
検証のために作成したDockerfile
お気に入りのAmazon Linux2
ベースで作成しています。
Squid
Dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2
RUN amazon-linux-extras enable python3.8 squid4 \
&& yum clean metadata \
&& yum -y update \
&& yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \
&& yum install -y squid \
python38 \
jq \
tar \
zip \
unzip \
git \
vim-common \
nkf \
gcc \
make \
autoconf \
automake \
zlib-devel \
bzip2 \
bzip2-devel \
readline-devel \
sqlite \
sqlite-devel \
openssl-devel \
xz \
xz-devel \
libffi-devel \
which \
&& yum clean all \
&& rm -rf /var/yum/cache \
&& rm -rf /var/cache/yum
# Symbolic link to python3
RUN ln -s /usr/bin/python3.8 /usr/bin/python3
# Install native python3 pip3
RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" \
&& python3 get-pip.py
# Setup squid
RUN ls -al /etc/squid
RUN mkdir -p /etc/squid/conf.d \
&& cp /etc/squid/squid.conf /etc/squid/conf.d/default.conf \
&& echo 'include /etc/squid/conf.d/*.conf' > /etc/squid/squid.conf
RUN chown -R squid:squid /etc/squid/conf.d \
&& chown squid:squid /dev/stdout
COPY squid-log.conf /etc/squid/conf.d/
COPY squid-acl.conf /etc/squid/conf.d/
EXPOSE 3128
user squid
CMD ["/usr/sbin/squid", "-NYCd 1"]
squid-log.conf
logfile_rotate 0
cache_store_log none
access_log stdio:/dev/stdout combined
cache_log stdio:/dev/stdout
coredump_dir /dev/null
pid_filename /var/run/squid/squid.pid
Dante
Dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2
ENV DANTE_VER=1.4.3
ENV DANTE_URL=https://www.inet.no/dante/files/dante-$DANTE_VER.tar.gz
ENV DANTE_SHA=418a065fe1a4b8ace8fbf77c2da269a98f376e7115902e76cda7e741e4846a5d
ENV DANTE_FILE=dante.tar.gz
ENV DANTE_TEMP=/tmp/dante
RUN set -ex \
# Build environment setup
RUN yum clean metadata \
&& yum -y update \
&& yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \
&& yum groupinstall -y "Development Tools" \
&& yum install -y jq \
tar \
zip \
unzip \
git \
wget \
which \
perl-Digest-SHA \
&& mkdir ${DANTE_TEMP} \
&& cd ${DANTE_TEMP} \
&& curl -sSL ${DANTE_URL} -o ${DANTE_FILE} \
&& echo "${DANTE_SHA} *${DANTE_FILE}" | shasum -c \
&& tar xzf ${DANTE_FILE} --strip 1 \
&& ./configure \
&& make install \
&& cd .. \
&& rm -rf ${DANTE_TEMP} \
&& yum groupremove -y "Development Tools" \
&& yum clean all \
&& rm -rf /var/yum/cache \
&& rm -rf /var/cache/yum
COPY sockd.conf /etc/dante/sockd.conf
ENV CFGFILE=/etc/dante/sockd.conf
ENV PIDFILE=/run/sockd.pid
ENV WORKERS=10
EXPOSE 1080
CMD sockd -f $CFGFILE -p $PIDFILE -N $WORKERS
参照
- Python3.5 + smtplib.SMTP_SSL.send_message()で、Gmailからメールを送信する
- Pythonでメール送信; smtplibのサンプルコード
- SOCKSプロキシとHTTPプロキシの違いについて勉強してみた
- Dante - A free SOCKS server