log4jを使う

仕事上は何度か使ったことあるけれど
自分で実装したことが無かったので,これを機会に勉強.

log4jというのは,java向けのlog管理ツールとでも呼ぶべきもの.
apacheから提供されている.
Log4j – Apache Log4j 2 - Apache Log4j 2
設定ファイルさえ用意しておけば,あとはシンプルなコマンドで
ログを出力することが可能となる.

仕組みは以下に整理されている.
Log4j – Log4j 2 Architecture - Apache Log4j 2
書いていることを要約すると,以下のような感じ.

・Loggerは名前を持ち,1つのLogConfigを持つ.
・LogConfigはログレベルを管理し,複数のAppenderを持つ.
・Appenderは出力先と出力フォーマット(Layout)を管理する.

また,LogConfigには継承関係も成り立つ.
「foo」の子供は「foo.bar」になる,といった具合だ.
で,継承の方法については以下になる.

・ログレベルの継承は「上書き」
・出力先(Appender)の継承は「追加」

たとえば,
foo:ログレベルはtrace,コンソール出力のAppenderを設定
foo.bar:ログレベルはerror,ファイル出力のAppenderを設定
と設定した場合,
foo.barは
「errorイベントのログを,コンソールとファイルの両方に出力する」
という振る舞いを見せる.

Appenderの継承方法も「上書き」にしたい場合,
additivityの設定をfalseにしておけば良いそうだ.
その辺は以下を見ると分かりやすい.
Log4j – Log4j 2 API - Apache Log4j 2


というわけで,御託はこれくらいにして,実装してみる.
今回は超シンプルに,以下.

package jp.nk5.stockanalyzer.controller;

import jp.nk5.stockanalyzer.application.DownloadStockDataService;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class BatchController {

  private static final Logger logger = LogManager.getLogger();
	
  public static void main(String[] args) {
    logger.error("main() started.");
    DownloadStockDataService application = new DownloadStockDataService();
    application.downloadStockData();
    logger.error("main() finished.");
  }

}

ちなみに,設定ファイルはまだ用意していない.
「設定ファイルが無い場合,log4jはログレベル=errorでコンソール出力する」
という仕様があるため.上記は設定ファイル無しでも動くのだ.

実際の実行結果が,以下.

10:45:38.365 [main] ERROR jp.nk5.stockanalyzer.controller.BatchController - main() started.
10:45:40.257 [main] ERROR jp.nk5.stockanalyzer.controller.BatchController - main() finished.

さらに設定ファイルを以下の内容で用意してやれば,
app.logというファイルにログを出力してくれるようになる.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <File name="logFile" fileName="app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="logFile"/>
    </Root>
  </Loggers>
</Configuration>

設定ファイル(log4j2.xml)はクラスパス上であればどこに置いても良い.


というわけで,ロギング出来るようになったので,次回こそサーバ側を実装.

Eclipse+Git+Gradleな環境を作る

久しぶりにAndroidアプリ作ろうかなと思った.
今回やりたいのは以下のような感じ.

・サーバクライアントシステムにする.
・サーバ側で定期的に情報収集して,それをクライアントで参照.
・クライアントはもちろんAndroid

まずはサーバ側の開発環境を構築する.
IDEは手元にあったEclipseのバージョン4.4.0を使う.
いつダウンロードしたか覚えてない.

以下,やったこと.

(1)
Gitのリポジトリには,GitHubを使う.
アカウントは持っているので,そこに新たにリポジトリを作成.
何も入っていないが,当然最初はそれで良い.

(2)
前段で作ったリポジトリをcloneする.
コマンドラインでやっても良いと思うけど,Eclipseでやるなら,
「Window」の「Open Perspective」からGitのパースペクティブを開く.

3つくらいあるリンクのうちの「Clone a Git repository」を選択.
URLに,先ほどのリポジトリのURL(末尾が.gitのやつ)を入力して,
あとはよしなにウィザードを進めればcloneできる.

これは完全に気分の問題だけど,ローカルリポジトリのパスは,
普段Eclipseのworkspaceとして使っているディレクトリにした方が良いかな.

(3)
デフォルトだとGradleのプロジェクトを作ることはできないので,
まずは「Gradle IDE pack」をインストールする.

Eclipseの「Help」から「Eclipse Marketplace...」を選択する.
検索窓に「gradle ide」などと入力して,
「Gradle IDE Pack」をインストール.バージョンは「3.8.x+1.0.x」だそうだ.

作業中はスルーしてたけど,「Gradle IDEはもう古いよ」って警告が出るね.
後継は「Buildship」だそうです.そっちに差し替え.バージョンは2.0.

(4)
ローカルリポジトリにGradleプロジェクトを作る.
「File」の「New」からGradle(STS)プロジェクトを選択.
workspaceに,さきほどのローカルリポジトリを選択すれば良い.

BuildShipの場合は「Gradle Project」というのがあるのでそれを選ぶ.
あと,Buildshipだと同名フォルダがある場合,上書きしてくれないみたいなので
一旦別のフォルダにプロジェクトを作って,それらを一式
ローカルリポジトリに移動する,という作業が必要になる.

(5)
Gitのパースペクティブを開き,
masterブランチ上で右クリックコンテキストメニューの「Commit...」を選択.
適当なコメントを書き,全部選択して「Commit and Push」を選択.
認証を求められたら,GitHubのアカウント情報を入力すればOK.
あとは流れに従えば良い.

これでOK.
とりあえずこれで,Eclipse上でGradleプロジェクトを扱い,
それらをGitで構成管理……という構図を作成できた.
次からサーバ側の実装.

一切設計せずに見切り発車で進めるので,どっかでつまずく可能性99%.

2017年の広島東洋カープの展望

優勝の余波か,カープ戦のチケットがプレミア化していますね.
昨年も色々と話題&問題になりましたが,
今年は「広島ファンのマナー」が問われる1年になると思います.

じゃあ,展望予想.

■全体
4位と予想.
去年は出来すぎで,かつ「先発投手不足」という課題はそのまま.
いや,黒田の引退により,さらに状況は悪化したと言える.

一方,セリーグは他球団もイマイチ決め手に欠いているので,
ぶっちぎりで弱いとも言い切れないなと思っている.
なので,泥沼レースを勝ち抜いてくれることに期待して,4位.
シーズン中の補強が当たれば,もう1~2つは順位を上げられるかも.

ちなみに優勝は横浜と予想.
着実に戦力を築き上げている上に,ラミレスが監督として想像以上に優秀.
山口の穴埋めの可否という要素はあるが,十分首位を狙えると思う.

■先発
開幕時点で既に6人ローテの構築が絶望的な状態.
なし崩しで,九里をローテに組み込んでいるが,
おそらく5月には,いつものロングリリーフに落ち着いていると思う.

飛躍に期待する枠としては,戸田・薮田・塹江あたりだろうか.
ヘーゲンズに再び先発として頑張ってもらうことも選択肢に入れる必要があるかと.

■リリーフ
中崎・ジャクソン・今村の3枚が軸になるだろう.
昨年はさすがに出来過ぎだが,今年もそこそこやってくれると期待.
一昨年・去年と,運用面で失態を晒し続けた緒方監督&畝コーチだが,
そろそろ成長を見せてほしい.

■捕手
今年も石原を軸にする形にせざるを得ないと思う.
會澤と船越が,お互いに切磋琢磨しながら石原をサポートする形が理想かな.

■内野
新井をどれだけ上手に休ませられるかが重要になる.
交代要員が誰かというと,「こいつ!」とすぐに浮かばないが…….
セカンド菊池とショート田中は,仮に打てなくても基本的に固定だろう.
サードはペーニャかと思いきや,枠の都合で開幕2軍らしい.
安部の不調や西川の故障により,サードは1年通して頭を悩ませる形になりそう.

■外野
センター丸とライト鈴木は固定かな.
特に鈴木は,昨年の自分との闘いになると思う.辛いだろうが頑張って欲しい.
レフトは,おそらく開幕はエルドレッドだろうけど,
最終的に調子が良い選手を流動的に使っていくことになるかな.
本格コンバートの堂林は……とりあえず代打で信頼築くところからスタートか.


それでは,選手の皆さん,頑張ってください.

postfixの勉強 #5

以下の続き.
postfixの勉強 #1 - NK5のノート
postfixの勉強 #2 - NK5のノート
postfixの勉強 #3 - NK5のノート
postfixの勉強 #4 - NK5のノート

qmgr(8)の後に起動するプロセスが,
送信時は「smtp」だったのに受信時は「local」だった,という話.

この使い分けは,前回も触れたtrivial-rewrite(8)の挙動に起因する.
ここで仕様を再掲しておく.
Postfix manual - trivial-rewrite(8)

上記リンクにもあるとおり,trivial-rewrite(8)は,メールアドレスを元に
主にtransport(配達員)とnexthop(配送先)を解決する.
で,このtransportが,先述のsmtpだったりlocalだったりする.

解決ルールについては,以下の表がわかりやすいと思う.
http://www.kobitosan.net/postfix/jman/rewrite.html#transport

local(8)は,ローカルユーザ宛の配送エージェント.
設定については前回触れたとおり.
さらに,alias_mapsにaliases(5)の変換テーブルを設定していれば,
・taro@mydomain宛のメールを,jiro@mydomain宛に配送
・all@mydomain宛のメールを,taroとjiroの両ユーザ宛に配送
のようなことが可能になる.後者はメーリスのイメージかな.
詳しくは以下.
Postfix manual - local(8)

smtp(8)は,外部ユーザ宛の配送エージェント.
SMTP認証なんかも関わってくるけど,今回は深くは触れないので
リンクだけ貼ってお茶を濁しておく.
Postfix manual - smtp(8)

virtual(8)は,バーチャルユーザ宛の配送エージェント.
バーチャルユーザというのは,名前の通り,実在しないユーザ.
「いちいち受信ユーザ毎にアカウントなんか作ってられるかよ」
という人向け,になるのかな.
実際にやってみる.

# vi /etc/postfix/main.cf
(編集作業,中略)
# grep "virtual_mailbox_domains =" /etc/postfix/main.cf
virtual_mailbox_domains = oreore.com
# grep "virtual_mailbox_base =" /etc/postfix/main.cf
virtual_mailbox_base = /etc/postfix/base
# grep "virtual_mailbox_maps =" /etc/postfix/main.cf
virtual_mailbox_maps = hash:/etc/postfix/v_m_maps
# grep "virtual_gid_maps =" /etc/postfix/main.cf
virtual_gid_maps = static:1919
# grep "virtual_uid_maps =" /etc/postfix/main.cf
virtual_uid_maps = static:114514

設定の意味合いとしては,
・oreore.com宛のメールは,virtual(8)が配送します.
・配送先のルートディレクトリは,/etc/postfix/baseです.
・配送先はv_m_mapsに基づいて最終決定します.
・メールの保存で使うグループのgidは1919です.
・メールの保存で使うユーザのuidは114514です.
といった感じになる.

ちなみに,gid:1919も,uid:114514も,存在しないidとなる.
これでもまあ,動くことは動くんだけど(それを証明するために使った),
管理面で面倒になるので,本来は管理用のグループ&ユーザを用意した方が良い.

また,「static:xxx」の部分は,今までのように「hash:yyy」として
変換テーブルyyyを用意……という戦法でも良い.
こういう手段もあるよ,というのを紹介するために使った.

上記で設定した「/etc/postfix/base/」と「/etc/postfix/v_m_maps」は
当然まだ存在しないので,以下のように用意してあげる.

# mkdir -m o+w /etc/postfix/base

# vi /etc/postfix/v_m_maps
(作成作業,中略)
# cat /etc/postfix/v_m_maps
taro@oreore.com taro/
# postmap /etc/postfix/v_m_maps

# ll /etc/postfix
total 172
-rw-r--r-- 1 root root 20876 Jun 10  2014 access
drwxrwxrwx 2 root root     6 Mar 28 14:18 base
-rw-r--r-- 1 root root 11681 Jun 10  2014 canonical
-rw-r--r-- 1 root root  9904 Jun 10  2014 generic
-rw-r--r-- 1 root root 21545 Jun 10  2014 header_checks
-rw-r--r-- 1 root root 27480 Mar 28 13:59 main.cf
-rw-r--r-- 1 root root  6105 Jun 10  2014 master.cf
-rw-r--r-- 1 root root  6816 Jun 10  2014 relocated
-rw-r--r-- 1 root root 12549 Jun 10  2014 transport
-rw-r--r-- 1 root root    22 Mar 28 13:36 v_m_maps
-rw-r--r-- 1 root root 12288 Mar 28 13:36 v_m_maps.db
-rw-r--r-- 1 root root 12494 Jun 10  2014 virtual

baseディレクトリ作成時に権限を追加してるのは,
gid:1919およびuid:114514とかいう,実在しない適当なユーザでも
ファイルやディレクトリ作成を出来るようにするため.

メールの保存ディレクトリは
「(virtual_mailbox_baseの設定値)/(virtual_mailbox_mapsの変換結果)」
になるので,上記設定だと,taro@oreore.com宛のメールは
/etc/postfix/base/taro/
に保存されることになる.

最後に忘れずにリロード

# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

これで,バーチャルユーザ「taro」にメールを届ける準備が整った.
(useraddもgroupaddも実施していない)
早速メールを送ってみる.以下は送信後の状況.

# journalctl -n 7
Mar 28 14:28:39 3832cc77bd2d postfix/smtpd[1032]: connect from (メールの送信元のホスト名)[172.17.0.1]
Mar 28 14:29:10 3832cc77bd2d postfix/smtpd[1032]: 73BDB60138FD: client=(メールの送信元のホスト名)[172.17.0.1]
Mar 28 14:29:54 3832cc77bd2d postfix/cleanup[1036]: 73BDB60138FD: message-id=<>
Mar 28 14:29:54 3832cc77bd2d postfix/qmgr[1028]: 73BDB60138FD: from=<■■■@gmail.com>, size=321, nrcpt=1 (queue active)
Mar 28 14:29:55 3832cc77bd2d postfix/virtual[1037]: 73BDB60138FD: to=<taro@oreore.com>, relay=virtual, delay=57, delays=57/0.07/0/0.03, dsn=2.0.0, status=sent (delivered to maildir)
Mar 28 14:29:55 3832cc77bd2d postfix/qmgr[1028]: 73BDB60138FD: removed
Mar 28 14:29:57 3832cc77bd2d postfix/smtpd[1032]: disconnect from (メールの送信元のホスト名)[172.17.0.1]

# ll /etc/postfix/base/taro/
total 0
drwx------ 2 114514 1919  6 Mar 28 14:29 cur
drwx------ 2 114514 1919 58 Mar 28 14:29 new
drwx------ 2 114514 1919  6 Mar 28 14:29 tmp

# cat /etc/postfix/base/taro/new/1490711394.Vfc01If0052bdM984614.3832cc77bd2d
Return-Path: <■■■@gmail.com>
X-Original-To: taro@oreore.com
Delivered-To: taro@oreore.com
Received: from oreore (メールの送信元 [172.17.0.1])
        by 3832cc77bd2d.oreore.com (Postfix) with SMTP id 73BDB60138FD
        for <taro@oreore.com>; Tue, 28 Mar 2017 14:28:57 +0000 (UTC)
from:■■■@gmail.com
to:oreore@oreore.com
subject:test mail

this is test mail for virtual user.

配送ユーザとしてvirtual(8)が選択されており,
「taro」というディレクトリにメールが保存されていることがわかると思う.
taroディレクトリは,存在しなければvirtual(8)が勝手に作ってくれる.
(baseディレクトリの権限を緩めたのはこのため)

その他,virtual(8)に関する細かい設定は以下参照.
Postfix manual - virtual(8)


以上,これでPostfixの仕組みと,基本的な設定は理解できた,気がする.
受信したメールはPOP3やIMAP4といったプロトコルで参照することになるが,
それはまた別のアプリケーションの話(dovecotとか)になるので,今回は割愛.


余談だけど,こういう実験の舞台準備としてdockerは超便利だなと実感.

postfixの勉強 #4

以下の続き.
postfixの勉強 #1 - NK5のノート
postfixの勉強 #2 - NK5のノート
postfixの勉強 #3 - NK5のノート

Postfixが受け取ったメールはcleanup(8)の正規化を経て,
incomingキューに投入され,それをqmgr(8)が拾う,という話だった.

qmgr(8)は,incomingキューに届いたメールを
「このメールは自分宛?それとも他所宛?」
と仕分けしてくれる.
仕分け自体はtrival-rewrite(8)というデーモンが代行するようだ.

Postfix manual - qmgr(8)
Postfix manual - trivial-rewrite(8)

自分宛だったら,自分の然るべきディレクトリにメールを格納するし,
他所宛だったら,別のメールサーバに転送する.

さて,今までの記事では他所宛,すなわち「送信」しかしてこなかったが,
どうせならここで「受信」も出来るようにしてみようと思う.

そもそも,何もせずに,Postfixにメールを送ろうとしたらどうなるか?
試しに外部からpostfixサーバ(172.17.0.2)の25番ポートにtelnetしてみる.

# telnet 172.17.0.2 25
Trying 172.17.0.2...
telnet: Unable to connect to remote host: Connection refused

このように,残念ながら拒否される.
Postfixは,main.cfにinet_interfacesというパラメータを有しており,
ここに書かれている者以外の利用を認めないからである.

Postfix設定パラメータ

なので,これを以下のように修正する.

# vi /etc/postfix/main.cf
(編集作業,中略)
# grep "inet_interfaces =" /etc/postfix/main.cf
inet_interfaces = all

# postfix stop
postfix/postfix-script: stopping the Postfix mail system
# postfix start
postfix/postfix-script: starting the Postfix mail system

allというのは,「全部許容しますよ」という意味.
上記まで実施したら,改めて外部からtelnet.今度はうまく繋がる.

# telnet 172.17.0.2 25
Trying 172.17.0.2...
Connected to 172.17.0.2.
Escape character is '^]'.
220 3832cc77bd2d.localdomain ESMTP Postfix
quit
221 2.0.0 Bye
Connection closed by foreign host.

さらに,メールを受信できるように,以下のように設定しておく,

# vi /etc/postfix/main.cf
(色々と編集,中略)
# grep "mydomain =" /etc/postfix/main.cf
mydomain = oreore.com
# grep "mydestination =" /etc/postfix/main.cf
mydestination = $mydomain
# grep "home_mailbox =" /etc/postfix/main.cf
home_mailbox = Mailbox

# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

それぞれ,
・自分のドメイン名はoreore.comです
・自分宛のメールはmydomain(先ほど設定したoreore.com)です
・メールは各ホームディレクトリの「Mailbox」というファイルに格納します
という意味となる.

試しに外部からtelnetでroot@oreore.com宛でメールを送ってみる.

# telnet 172.17.0.2 25
Trying 172.17.0.2...
Connected to 172.17.0.2.
Escape character is '^]'.
220 3832cc77bd2d.oreore.com ESMTP Postfix
HELO hogehoge
250 3832cc77bd2d.oreore.com
MAIL FROM:<tekitou@tekitou.com>
250 2.1.0 Ok
RCPT TO:<root@oreore.com>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
FROM:tekitou@tekitou.com
TO:root@oreore.com
SUBJECT:Test Mail
test mail
.
250 2.0.0 Ok: queued as D62D860138FD
quit
221 2.0.0 Bye
Connection closed by foreign host.

送れたっぽいメッセージが表示されている.
さっそく,Postfix側のログとディレクトリを確認してみる.

# journalctl -n 7
-- Logs begin at Sat 2017-03-25 08:35:13 UTC, end at Sat 2017-03-25 10:16:50 UTC. --
Mar 25 10:15:41 3832cc77bd2d postfix/smtpd[429]: connect from (telnetしたマシンのホスト名)[172.17.0.1]
Mar 25 10:16:12 3832cc77bd2d postfix/smtpd[429]: D62D860138FD: client=(telnetしたマシンのホスト名)[172.17.0.1]
Mar 25 10:16:49 3832cc77bd2d postfix/cleanup[432]: D62D860138FD: message-id=<>
Mar 25 10:16:49 3832cc77bd2d postfix/qmgr[428]: D62D860138FD: from=<tekitou@tekitou.com>, size=292, nrcpt=1 (queue active)
Mar 25 10:16:49 3832cc77bd2d postfix/local[433]: D62D860138FD: to=<root@oreore.com>, relay=local, delay=48, delays=48/0.12/0/0.08, dsn=2.0.0, status=sent (delivered to mailbox)
Mar 25 10:16:49 3832cc77bd2d postfix/qmgr[428]: D62D860138FD: removed
Mar 25 10:16:50 3832cc77bd2d postfix/smtpd[429]: disconnect from (telnetしたマシンのホスト名)[172.17.0.1]

# ll /root
total 8
-rw------- 1 root root  432 Mar 25 10:16 Mailbox
-rw------- 1 root root 3354 Dec 14 14:59 anaconda-ks.cfg

# cat /root/Mailbox
From tekitou@tekitou.com  Sat Mar 25 10:16:49 2017
Return-Path: <tekitou@tekitou.com>
X-Original-To: root@oreore.com
Delivered-To: root@oreore.com
Received: from hogehoge ((telnetしたマシンのホスト名) [172.17.0.1])
        by 3832cc77bd2d.oreore.com (Postfix) with SMTP id D62D860138FD
        for <root@oreore.com>; Sat, 25 Mar 2017 10:16:01 +0000 (UTC)
FROM:tekitou@tekitou.com
TO:root@oreore.com
SUBJECT:Test Mail

test mail

無事,メールを受信できていることを確認した.

※なお,言うまでもなく,oreore.comなんてドメインは存在しない.
 正しく活用するためには,ドメインを然るべき場所で取得した上で
 DNSサーバにMXレコードを登録する必要があるが,今回は省略.

さて,ログをよく見ると,qmgrの後の登場人物が「local」になっている.
今までは「smtp」だったが,何が違うのか?


以外と長くなったので次回に.今度こそ次で終わり.

postfixの勉強 #3

以下の続き.
postfixの勉強 #1 - NK5のノート
postfixの勉強 #2 - NK5のノート

postfixは受け取ったメールをcleanup(8)デーモンでキレイにするが,
そのときにcanonical(5)に基づいて送信元/送信先を変換できる,
というところまでが,前回の確認事項.

変換手段としてもう1つ,virtual(5)も用意されている.

Postfix manual - virtual(5)

気を付けてほしいのは,同名のvirtual(8)というデーモンが存在すること.
最初それに気づいていなくて混乱した.
前回から(1)とか(5)とか(8)とか明記するようにしたのはそのため.

話を戻すと,virtual(5)の使い方は,canonical(5)とほぼ同様.
以下,設定からメール送付までの操作.
捕捉として,前回設定したcanonical(5)は無効化してます.

# ll /etc/postfix/
total 148
-rw-r--r-- 1 root root 20876 Jun 10  2014 access
-rw-r--r-- 1 root root 11681 Jun 10  2014 canonical
-rw-r--r-- 1 root root  9904 Jun 10  2014 generic
-rw-r--r-- 1 root root 21545 Jun 10  2014 header_checks
-rw-r--r-- 1 root root 27195 Mar 23 18:25 main.cf
-rw-r--r-- 1 root root  6105 Jun 10  2014 master.cf
-rw-r--r-- 1 root root  6816 Jun 10  2014 relocated
-rw-r--r-- 1 root root 12549 Jun 10  2014 transport
-rw-r--r-- 1 root root 12494 Jun 10  2014 virtual

# vi /etc/postfix/virtualtest
(ファイル作成,中略)
# cat /etc/postfix/virtualtest
■■■@gmail.com  〇〇@yahoo.co.jp
root    oreore@oreore.com
# postmap /etc/postfix/virtualtest

# ll /etc/postfix/
total 164
-rw-r--r-- 1 root root 20876 Jun 10  2014 access
-rw-r--r-- 1 root root 11681 Jun 10  2014 canonical
-rw-r--r-- 1 root root  9904 Jun 10  2014 generic
-rw-r--r-- 1 root root 21545 Jun 10  2014 header_checks
-rw-r--r-- 1 root root 27195 Mar 23 18:25 main.cf
-rw-r--r-- 1 root root  6105 Jun 10  2014 master.cf
-rw-r--r-- 1 root root  6816 Jun 10  2014 relocated
-rw-r--r-- 1 root root 12549 Jun 10  2014 transport
-rw-r--r-- 1 root root 12494 Jun 10  2014 virtual
-rw-r--r-- 1 root root    69 Mar 23 18:29 virtualtest
-rw-r--r-- 1 root root 12288 Mar 23 18:31 virtualtest.db

# vi /etc/postfix/main.cf
(ファイル編集,中略)
# grep "virtual_alias_maps =" /etc/postfix/main.cf
virtual_alias_maps = hash:/etc/postfix/virtualtest
# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

# sendmail ■■■@gmail.com
From:oreore@oreore.com
To:■■■@gmail.com
Subject:this is test mail
This is virtual test mail.
.

# journalctl -n 5
Mar 23 18:50:18 100eec03ccac postfix/pickup[586]: 20DE060138FB: uid=0 from=<root>
Mar 23 18:50:18 100eec03ccac postfix/cleanup[590]: 20DE060138FB: message-id=<20170323185018.20DE060138FB@100eec03ccac.localdomain>
Mar 23 18:50:18 100eec03ccac postfix/qmgr[587]: 20DE060138FB: from=<root@100eec03ccac.localdomain>, size=342, nrcpt=1 (queue active)
Mar 23 18:50:18 100eec03ccac postfix/smtp[592]: 20DE060138FB: to=<〇〇@yahoo.co.jp>, orig_to=<■■■@gmail.com>, relay=mx2.mail.yahoo.co.jp[182.22.12.117]:25, delay=27, delays=26/0.16/0.21/0.28, dsn=2.0.0, status=sent (250 ok dirdel)
Mar 23 18:50:18 100eec03ccac postfix/qmgr[587]: 20DE060138FB: removed

流れ的には,
・virtual(5)テーブルの文法に沿った変換テーブルを作成し,dbを作成
・main.cfのvirtual_alias_mapsにテーブルを紐づけ
Postfixをreload
sendmail(1)でメールをお試し送信
・journaldのログチェック
という感じ.
Gmail宛としたにも関わらず,yahooメールのMTAにリレーしているのがわかると思う.

canonical(5)とvirtual(5)の違いは,変換対象にある.
canonical(5)は,変換対象を柔軟に設定できるのに対し,
(対象はmain.cfのcanonical_classesで設定できる)
virtual(5)は,エンベロープの受信者(送信先)だけを変換対象とする.

エンベロープって何ぞ?」という人は,以下を見てもらった方が早い.

f:id:bob_l_s:20170324040549p:plain

実際に受信したYahooアカウント側から見えるメール.
注目すべきところはメールのTo:の部分.
sendmail(1)で指定したGmailアドレスのままであることがわかると思う.

メールには,乱暴に言えば宛先を2つ書く欄があって,
それが「エンベロープ」と「メールヘッダ」.
普段,俺たちがThunderbirdOutlookで見ているToは後者なんだけど,
メールサーバがやり取りで使用する真のToは前者となる.

つまり,virtual(5)とは,
「メールヘッダ(ユーザから見える世界の宛先)に手を加えることなく,
 メールの送信先をコントロールしたい」
という要求に答えてくれる機能,と思えば良いと思う.

他にもcleanup(8)は色々やってくれているようだけど,
とりあえず今回の勉強で触れるのはここまでとする.

cleanup(8)は,整形したメールをincomingキューに放り込む.
で,それを拾い上げるのがqmgr(8)デーモンとなる.


で,ここから先はまた次回.たぶん次で終わり.

postfixの勉強 #2

以下の続き.
postfixの勉強 #1 - NK5のノート

postfixでメールを送る際に
pickup→cleanup→qmgr→smtp→qmgr
というキャッチボールが行われているらしい,という話だったけど,
そもそもトリガとして使った「sendmail」自体が
postfixの提供するコマンドの1つだったりする.

Postfix manual - sendmail(1)

Sendmail」という,別のメール転送エージェントとの同じコマンドで
メール送れますよ,ということらしい.
ともあれ,アプリケーションからPostfixをキックするときは,
このsendmail(1)コマンドを用いることになる.

ちなみに末尾の(1)は「ユーザコマンド」の意だそうだ.
(5)が「ファイル書式」で(8)が「デーモン」なんだと.

メールはmaildropキューに投入される.
キューのパスはmain.cfのqueue_directoryで定義される.

# grep "queue_directory =" /etc/postfix/main.cf
queue_directory = /var/spool/postfix
# ll /var/spool/postfix/
total 0
drwx------ 2 postfix root       6 Mar 19 18:30 active
drwx------ 2 postfix root       6 Mar 19 18:30 bounce
drwx------ 2 postfix root       6 Jun 10  2014 corrupt
drwx------ 2 postfix root       6 Jun 10  2014 defer
drwx------ 2 postfix root       6 Jun 10  2014 deferred
drwx------ 2 postfix root       6 Jun 10  2014 flush
drwx------ 2 postfix root       6 Jun 10  2014 hold
drwx------ 2 postfix root       6 Mar 19 18:30 incoming
drwx-wx--- 2 postfix postdrop   6 Mar 19 18:30 maildrop
drwxr-xr-x 2 root    root     134 Mar 19 18:11 pid
drwx------ 2 postfix root     256 Mar 19 17:47 private
drwx--x--- 2 postfix postdrop  73 Mar 19 17:47 public
drwx------ 2 postfix root       6 Jun 10  2014 saved
drwx------ 2 postfix root       6 Jun 10  2014 trace

これを拾い上げるのが,pickup(8)デーモンなんだそうだ.

Postfix manual - pickup(8)

pickup(8)デーモンは,メールをcleanup(8)デーモンに引き渡す.
これは名前のとおり,メッセージを規約に沿った形に整形してくれる.
前回の俺の適当なsendmail(1)でもメールを送信できたのは,こいつのおかげ.

Postfix manual - cleanup(8)

また,ここでアドレスの置換なんかも出来るらしい.
とりあえず/etc/postfix配下に以下のようなものを作ってみた.

# cat /etc/postfix/canonicaltest
root 〇〇@yahoo.co.jp
oreore@oreore.com 〇〇@yahoo.co.jp

〇〇@yahoo.co.jpは,実在するyahooメールアドレスとなる.
で,main.cfに「canonical_maps」の設定を追加する.

# grep canonical_maps /etc/postfix/main.cf
canonical_maps = hash:/etc/postfix/canonicaltest

設定を読み込ませるために以下も実施.

# postmap /etc/postfix/canonicaltest
# postfix reload
postfix/postfix-script: refreshing the Postfix mail system
# postconf | grep canonical_maps
canonical_maps = hash:/etc/postfix/canonicaltest

さっそく,sendmail(1)でメールを送信し,確認.
確かに〇〇@yahoo.co.jp名義で送付されていたが,さすがに
送信サーバがおかしいよ,気を付けてね」
という旨の警告が表示されていた.まあ,そうだよね.

アドレスの置換には,もう1つ,virtual(5)という手段もあるらしいけど,
疲れてきたので今回はここまで.