Spring Boot + Spring Integration でいろいろ試してみる ( その30 )( Docker Compose でサーバを構築する、SMTP over SSL+POP over SSLサーバ編 )
概要
記事一覧はこちらです。
Spring Integration のアプリケーションで使用するサーバを Docker Compose で構築します。
- SMTP over SSL+POP over SSLサーバを構築します。
- 今回は Spring Boot + Spring Integration でいろいろ試してみる ( その25 )( Docker Compose でサーバを構築する、SMTP+POP3サーバ編 ) で作成した mail-server コンテナの設定を変更して SMTP over SSL+POP over SSLサーバにします。
- SMTP over SSL は認証あり、SSLあり、ポート番号は465番を使用。
- POP over SSL は認証あり、SSLあり、ポート番号は995番を使用。
- 認証に使用するパスワードは、サーバ側で PLAIN TEXT で保存します。
また動作確認のために Webmail クライアントの hardware/rainloop を利用します。Rainloop を利用するためには IMAP が必要なので、mail-server コンテナの IMAP の設定も利用可能に設定します(認証あり、SSLあり、ポート番号は993番を使用)。
参照したサイト・書籍
tomav/docker-mailserver - Configure SSL
https://github.com/tomav/docker-mailserver/wiki/Configure-SSLUsing SSL/TLS with Postfix SMTP and Courier POP3/IMAP
https://www.mad-hacking.net/documentation/linux/applications/mail/using-ssl-tls-postfix-courier.xmlbyeCloud: Building a mailserver with modern webmail
https://www.davd.eu/byecloud-building-a-mailserver-with-modern-webmail/hardware/rainloop
https://github.com/hardware/rainloopRainloop initial configuration
https://github.com/hardware/mailserver/wiki/Rainloop-initial-configurationUsing Gmail as SMTP server from Java, Spring Boot apps
https://sanaulla.info/2017/09/15/using-gmail-as-smtp-server-from-java-spring-boot-apps/Fixing PKIX Path Building Issues When Using JavaMail and SMTP
http://springinpractice.com/2012/04/29/fixing-pkix-path-building-issues-when-using-javamail-and-smtpJavaにSSL証明書を追加する
https://qiita.com/nenokido2000/items/b36b6e5f0854d7d63ba6keytool を使用して証明書を削除する
https://docs.oracle.com/cd/E19226-01/821-1299/ghleq/index.htmlhow to ignore server cert error in javamail
https://stackoverflow.com/questions/4894954/how-to-ignore-server-cert-error-in-javamailSTARTTLSについて解説
https://go-journey.club/archives/2326JavaMail API - SMTP Servers
https://www.tutorialspoint.com/javamail_api/javamail_api_smtp_servers.htmJavaMail API - POP3 Servers
https://www.tutorialspoint.com/javamail_api/javamail_api_pop3_servers.htmNetworking features in Docker for Windows
https://docs.docker.com/docker-for-windows/networking/
目次
- SMTP over SSL+POP over SSL サーバを構築する
- Webmail クライアントの Rainloop で動作確認する
- SMTP over SSL でメールを送信するサンプルを作成する。。。が送信できませんでした
- 証明書の問題を解決する
- POP over SSL でメールを送信するサンプルを作成する
手順
SMTP over SSL+POP over SSL サーバを構築する
docker-compose.yml の mail-server コンテナの設定を変更し、rainloop コンテナの設定を追加する
Rainloop のデータを保存するディレクトリ docker/rainloop/data を作成します。
docker/mail-server/config/dovecot.cf の以下の点を変更します。
# SSL を使用しない場合には以下のコメントアウトを解除し、ssl = yes をコメントアウトする # disable_plaintext_auth = no # ssl = no ssl = yes # debug したい場合には以下の行のコメントアウトを解除する # auth_verbose = yes # auth_debug = yes
disable_plaintext_auth = no
、ssl = no
の2行をコメントアウトします。ssl = yes
を追加します。
docker-compose.yml の以下の点を変更します。
mail-server: image: tvial/docker-mailserver:latest container_name: mail-server hostname: mail domainname: example.com ports: - "25:25" - "110:110" - "143:143" - "465:465" - "587:587" - "993:993" - "995:995" volumes: - ./docker/mail-server/config/:/tmp/docker-mailserver/ environment: # debug したい場合には以下の行のコメントアウトを解除する # - DMS_DEBUG=1 - ENABLE_SPAMASSASSIN=0 - ENABLE_CLAMAV=0 - ENABLE_FETCHMAIL=0 - ENABLE_FAIL2BAN=0 - ENABLE_POSTGREY=0 - ENABLE_POP3=1 cap_add: - NET_ADMIN - SYS_PTRACE restart: always # Webmail クライアント rainloop: image: hardware/rainloop container_name: rainloop ports: - "8888:8888" volumes: - ./docker/rainloop/data/:/rainloop/data
- mail-server コンテナの ports に以下の行を追加します。
- "143:143"
- "465:465"
- "587:587"
- "993:993"
- "995:995"
- rainloop コンテナの設定を追加します。
サーバを起動する
docker-compose up -d
コマンドを実行します。
Webmail クライアントの Rainloop で動作確認する
まずは管理画面にアクセスして mail-server コンテナに設定している mail.example.com ドメインを追加します。http://localhost:8888/?admin にアクセスしてログイン画面を表示した後、admin / 12345 でログインします。
ログイン後、「Language」「Language(admin)」で「日本語」を選択します。
画面左側のメニューから「ドメイン」を選択した後、画面右側に表示されるドメイン一覧の中の「gmail.com」のチェックを外してから、
「ドメインを追加」ボタンをクリックします。
「ドメインを追加」ダイアログが表示されるので、以下の値を入力します。
- 「名前」には docker-compoyse.yml の mail-server コンテナの hostname + "." + domainname の文字列(mail.example.com)を入力します。
- 「IMAP」「SMTP」どちらも、
左下の「接続テスト」ボタンをクリックして「IMAP」「SMTP」の文字が緑色で表示されることを確認したら、右下の「追加」ボタンをクリックします。
ドメイン一覧に "mail.example.com" がチェックされた状態で表示されます。
今度はユーザ画面にログインしてメールを送受信してみます。http://localhost:8888/ にアクセスしてログイン画面を表示した後、tanaka@mail.example.com / xxxxxxxx でログインします。
ログインしたら左上の「新規作成」ボタンをクリックします。
メール送信用の画面が開いたら件名とメール本文を入力した後、「送信」ボタンをクリックして送信します。
画面右上の人のアイコンのボタンをクリックして、メニューから「ログアウト」を選択します。
ログイン画面に戻ったら、suzuki@mail.example.com / yyyyyyyy でログインします。
受信トレイに tanaka@mail.example.com からのメールが届いています。
mail-server コンテナのログを見ると postfix/smtps/smtpd
と smtps
になっており、Anonymous TLS connection established
とも出力されているので、SMTP によるメール送信は SMTP over SSL で送信できているようです。
Rainloop は Docker で簡単に起動できて設定も分かりやすく、画面のデザインもシンプルで見やすくていいですね。
SMTP over SSL でメールを送信するサンプルを作成する。。。が送信できませんでした
src/main/java/ksbysample/eipapp/dockerserver/flow/MailFlowConfig.java は修正する必要はありません。今は POP over SSL の受信処理はしないので、mailRecvByPop3Flow メソッドに付与した @Bean アノテーションをコメントアウトしておきます。
// @Bean public IntegrationFlow mailRecvByPop3Flow() { ..........
SMTP の SSL 化及びポート番号の変更は application.properties を以下のように変更します。
spring.mail.host=localhost #spring.mail.port=25 spring.mail.port=465 spring.mail.username=tanaka@mail.example.com spring.mail.password=xxxxxxxx spring.mail.properties.mail.smtp.ssl.enable=true # メール送信処理を debug したい場合には以下の行のコメントアウトを解除する # spring.mail.properties.mail.debug=true
spring.mail.port=25
はコメントアウトしてspring.mail.port=465
を追加します。- SMTP 送信時に認証が必要になるので、以下の2行を追加します。
spring.mail.username=tanaka@mail.example.com
spring.mail.password=xxxxxxxx
- SSL/TLS で通信をするために以下の1行を追加します。
spring.mail.properties.mail.smtp.ssl.enable=true
Tomcat を起動して送信してみますが、javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
という例外が発生して送信できません。。。
証明書の問題を解決する
原因を調べたところ、Fixing PKIX Path Building Issues When Using JavaMail and SMTP という記事を見つけました。Java がメールサーバに設定されている自己証明書を信頼できないので例外が発生しているようです。
解決方法は以下の2つ。
【解決法その1】keytool -import
コマンドでメールサーバの証明書をキーストアに追加する
まずはメールサーバの自己証明書をファイルに保存します。https://github.com/tomav/docker-mailserver/wiki/Configure-SSL#testing-certificate に記載されている docker exec mail-server openssl s_client -connect 0.0.0.0:25 -starttls smtp -CApath /etc/ssl/certs/
コマンドを実行します。
赤枠の -----BEGIN CERTIFICATE-----
~ -----END CERTIFICATE-----
の部分(この2行も含む)をコピーして D:\project-springboot\ksbysample-boot-integration\ksbysample-eipapp-dockerserver\mail-example-com.cer というファイルに保存します。
次にファイルに保存した自己証明書をキーストアに追加します。現在使用している Java SE が 8u192 でインストール先が D:\Java\jdk1.8.0_192
の場合、Java のキーストアは D:\Java\jdk1.8.0_192\jre\lib\security\cacerts
になります。
keytool -import -alias mail.example.com -file D:\project-springboot\ksbysample-boot-integration\ksbysample-eipapp-dockerserver\mail-example-com.cer -keystore D:\Java\jdk1.8.0_192\jre\lib\security\cacerts
コマンドを実行します。
- 「キーストアのパスワードを入力してください:」の部分で入力待ちになりますので、
changeit
と入力します(これがデフォルトのパスワードです)。 - 「この証明書を信頼しますか。 [いいえ]:」の部分で再度入力待ちになりますので、
はい
と入力します。
keytool -list -keystore D:\Java\jdk1.8.0_192\jre\lib\security\cacerts
で登録済の証明書一覧を表示し、mail.example.com が登録されていることを確認します。
(.....省略.....)
これでメールサーバの証明書を信頼するようになったので、メールが送信できるようになったはずです。Tomcat を起動すると、例外は throw されずにメールが送信できています。
Rainloop で suzuki@mail.example.com の受信トレイを見ると、メールが届いていました。受信したメールは削除します。
最後に keytool -delete -noprompt -alias mail.example.com -keystore D:\Java\jdk1.8.0_192\jre\lib\security\cacerts
コマンドでキーストアから登録した証明書を削除します。
【解決法その2】spring.mail.properties.mail.smtp.ssl.trust=*
の設定で全ての証明書を信頼する
src/main/resources/application.properties に spring.mail.properties.mail.smtp.ssl.trust=*
を追加します。
spring.mail.host=localhost #spring.mail.port=25 spring.mail.port=465 spring.mail.username=tanaka@mail.example.com spring.mail.password=xxxxxxxx spring.mail.properties.mail.smtp.ssl.enable=true spring.mail.properties.mail.smtp.ssl.trust=* # メール送信処理を debug したい場合には以下の行のコメントアウトを解除する # spring.mail.properties.mail.debug=true
これで全ての証明書を信頼してチェックしないようになったので、メールが送信できるようになったはずです。Tomcat を起動すると、例外は throw されずにメールが送信できています。
Rainloop で suzuki@mail.example.com の受信トレイを見ると、メールが届いていました。受信したメールは削除します。
POP over SSL でメールを送信するサンプルを作成する
src/main/java/ksbysample/eipapp/dockerserver/flow/MailFlowConfig.java の以下の点を変更します。
@Bean public Pop3MailReceiver pop3MailReceiver() { Pop3MailReceiver pop3MailReceiver // テキストベースの POP3 を使用する場合には上の、POP over SSL を使用する場合には下の行のコメントアウトを解除する // = new Pop3MailReceiver("localhost", "tanaka@mail.example.com", "xxxxxxxx"); = new Pop3MailReceiver("localhost", 995, "tanaka@mail.example.com", "xxxxxxxx"); pop3MailReceiver.setShouldDeleteMessages(true); Properties javaMailProperties = new Properties(); // テキストベースの POP3 を使用する場合には下の4行をコメントアウトする javaMailProperties.put("mail.pop3.connectiontimeout", "5000"); javaMailProperties.put("mail.pop3.timeout", "5000"); javaMailProperties.put("mail.pop3.ssl.enable", "true"); javaMailProperties.put("mail.pop3.ssl.trust", "*"); // debug したい場合には以下のコメントアウトを解除する // javaMailProperties.put("mail.debug", "true"); pop3MailReceiver.setJavaMailProperties(javaMailProperties); return pop3MailReceiver; }
- ポート番号 995番を使用するので、
= new Pop3MailReceiver("localhost", "tanaka@mail.example.com", "xxxxxxxx");
→= new Pop3MailReceiver("localhost", 995, "tanaka@mail.example.com", "xxxxxxxx");
に変更します。 - POP over SSL とは関係ありませんが、タイムアウトを設定したいので以下の2行を追加します。
javaMailProperties.put("mail.pop3.connectiontimeout", "5000");
javaMailProperties.put("mail.pop3.timeout", "5000");
- POP over SSL を使用するために以下の2行を追加します。今回は全ての証明書を信頼するよう設定します。
javaMailProperties.put("mail.pop3.ssl.enable", "true");
javaMailProperties.put("mail.pop3.ssl.trust", "*");
これで POP over SSL の設定は完了です。ただし Tomcat を起動して試してみたのですが、以下の画像の結果になりました。
- 何件かは正常に受信できるが、少し経つと
org.springframework.messaging.MessagingException: failure occurred while polling for mail; nested exception is javax.mail.MessagingException: Connect failed;
というエラーメッセージが表示されて全く受信できなくなる。自動で復旧する様子はなし。 - POP over SSL での受信で上のエラーが出ると SMTP over SSL での送信も失敗する。
原因を調査してみましたが、
docker-compose down
、docker-compose up -d
でコンテナを再起動しても復旧しない。- mail-server コンテナへの接続だけの問題かと思ったが、Rainloop(http://localhost:8888)や ftp、sftp サーバへの接続も全て失敗する。ホストからコンテナへ接続できなくなっている模様。
docker exec -it mail-server /bin/sh
は成功する。コマンド実行後にls
コマンド等は動作する。docker ps
コマンドを実行すると e1594ff0dcc5 のコンテナの PORTS には0.0.0.0:995->995/tcp
が設定されている。docker inspect --format '{{ .NetworkSettings }}' mail-server
を実行すると995/tcp:[{0.0.0.0 995}]
が出力されている。- docker 本体を再起動すると復旧する。
- SMTP over SSL でメールを送信するだけではこの現象は発生しない。
- docker/mail-server/config/dovecot.cf の設定を
disable_plaintext_auth = no
、ssl = no
に戻して docker-compose コマンドでコンテナを再起動した後、ksbysample.eipapp.dockerserver.flow.MailFlowConfig の pop3MailReceiver メソッドの実装を 110番ポートの SSL ではない POP3 に戻した時はこの現象は発生しない。 - 110番ポートで
mail.pop3.starttls.enable=true
、mail.pop3.starttls.required=true
の設定をして STARTTLS で接続した場合も、この現象は発生する。POP over SSL で通信した時だけ発生する模様。
結論。全然原因が分かりません! ネットワークインターフェース周りとかポートフォワードあたりのように思えますが Windows でどう調べればよいのか。。。
全てのコンテナへの通信ができなくなるなら今回実装した Java の問題ではなく Docker の問題のようなので、今回は SMTP over SSL、POP over SSL の実装方法が書けたということで終わりにします。POP の実装は実際に動くテキストベースの POP3 の方にしておきます(POP over SSL の実装はコメントアウトします)。
時間が出来たら tvial/docker-mailserver 以外の Image で SMTP, POP サーバを立てて試しみることにします。
履歴
2018/12/16
初版発行。