Sieve 入れてみた

last-updated at Mar 08, 2015 00:46, in /tech/dovecot

とかさんざん dis っておきながら、結局 Dovecot の Sieve プラグインを入れてるというこのザマね。

念のため言っておくと、配送時に特定条件で IMAP フラグをいじる方法が現実問題として他になくて、仕方なく入れたんだからね。言語仕様はやっぱりどうしようもなく気持ち悪いし、 implicit な挙動があれこれあって念入りに sieve-test しないと怖くて使えたもんじゃないし、実装毎の独自拡張がざらにあったりする時点でお察しですよ。まぁ syntax の気持ち悪さという点では Procmail も相当だけど、俺的には Procmail の方が全然食える。

とまぁ誰得なヘイトを撒き散らした (もしくは自分が如何に古臭い人間かを晒した) ところで、導入メモ。

要件整理

大きな要件は以下の二つ。

  1. ベイジアンフィルタ (具体的には Bogofilter ) で spam 判定されたメールを Junk フォルダに振り分ける。

  2. ヘッダ・本文中の文字列パターンに応じてフォルダに振り分ける、もしくは IMAP フラグを立てる。

前者が Sieve にとっては鬼門。 Procmail なら判定と振り分けをたった2行で書けるのに、 Sieve にはそもそも個別のユーザ権限で外部コマンドを叩く術が存在しない。あーはいはい、セキュリティ万歳、アーキテクチャ非依存万歳。ベイジアンフィルタは各ユーザが独自に DB 管理できて然るべきだろjk、とか語っても始まらないので、百歩譲って (どうせこのサーバのユーザは俺一人だし‥‥) After-Queue Content Filter の仕組みを使って Postfix のレイヤーで Bogofilter に通し、 Sieve ではヘッダを見て振り分けることにした。 extprograms でもイケるのかもしれないけど、できるだけ Sieve の深みには嵌まりたくない。

後者はさすがに余裕だろ、とか思ってたら落とし穴があって、 少なくとも Dovecot の Sieve 実装であるところの Pigeonhole (0.3.1) では、 body を :regex にかけると ^$ がいずれかの行の先頭・末尾 ではなく body の先頭と末尾にしかマッチしない。あーはいはい、 body が一つの string なんですね。もう :contains でいいよ好きにしろ。

設定方法とか

ターゲット環境が Debian wheezy 標準の Postfix + Dovecot + Bogofilter であることは大前提。

1. mailbox_command で LDA を Dovecot に

  • LDA/Postfix - Dovecot Wiki
  • うちは末端の配送処理は既に Dovecot LDA に任せてたので、これだけ。そうでなければもちろん LDA 自体の設定も必要。

2. smtpd を経由するメールが Bogofilter を通るように

3. Dovecot Sieve プラグインのインストールと設定

4. ユーザ用 Sieve スクリプトの準備と配置

5. (option) 再学習の仕組み作り

Arrange for users to send misclassified mail to a particular mailbox and make sure that the database is updated regularly.

file:///usr/share/doc/bogofilter/integrating-with-postfix

  • メールを直に流すなり定期的に拾うなり、何らかの方法で Bogofilter コマンドを叩くようにすればいい。
  • イタズラされたりしないように多少の考慮は必要 (なのでこっちの手の内は明かさない ;-)

感想

設定が済んでちゃんと動作してるのが無事確認できてしまうと、まぁそこそこの満足感があったりするのが SE としての悲しい性なのだけど、一方で Sieve を導入したことでこれからチョー夢がひろがりんぐ、とは全く思えないので、労力に見合うだけの価値があるかと聞かれれば、否。既に Procmail とか maildrop とかでバリバリ設定作り込んでるユーザが「 Sieve 、いいなぁ‥‥」とか反射的にヨダレ垂らす類のもの じゃない のはきっと間違いない。 Sieve にしかできないことなんてほとんどないし。

要は、ユーザみんな virtual なんで古き良き手法じゃユーザ個別のフィルタリングとか無理なんですよね、みたいなそこそこデカいメールシステムを作るときに ManageSieve とか共通スクリプトとかガンガン使いつつキモいところは UI でうまく隠蔽してほらフィルタリングできますよ vacation だって飛ばせますよとか売り込みたい、そんな SIer ポジションな皆様向けの機能だと思うんですよね、 Sieve って。まぁ知ってたけど。

rrsync がステキ

last-updated at Mar 07, 2015 02:19, in /tech/freenas

必要なディレクトリだけ cpio なりでアーカイブして、 rsync over SSH で吸い出すことにする。 まだ設定してないけど。

88171.net :: FreeNAS 稼働開始

というわけで、この鯖 (さくらの VPS) のクリティカルなデータを自宅の NAS にバックアップしましょう、と、ようやく重い腰を上げた次第。で、前々から目をつけてた rrsync がいざ使ってみたらちょいステキだったので軽く紹介してみようかと。

rrsync とは何ぞや

かの有名な rsync に付属のプログラム、というか 200 行ちょっとの Perl スクリプト。名前の由来は Restricted rsync の略 (のはず) 。

いざインターネット越しでサーバからデータを吸い出す処理を自動化しようと思うと、これが単純に見えて意外とめんどくさい。

  • --delete とかチョー便利だから、やっぱ scp とかじゃなくて rsync 使いたいよね。
  • インターネットに出るなら通信を暗号化したいから、やっぱ通信は over SSH 一択だよね。
  • その場合お決まりのパスフレーズなし鍵ペアで公開鍵認証、なんだけど、シェル開けるのはなんか怖いよね。
  • えっ?ログイン時に実行するコマンドを公開鍵に設定できるの?でも rsync のサーバ側コマンドなんてどう決め打てばいいのさ!?

このモヤモヤをさくっと解決してくれるのが、 rrsync 。

ざっくり使い方と仕組み

まずは rrsync をサーバ上のどっかに置く。 Debian 系のパッケージならたぶん /usr/share/doc/rsync/scripts/rrsync.gz に gzip 圧縮されたものが、 source tarball なら support/rrsync に生スクリプトがあるはず。

次に rsync したいアカウントで普通に鍵ペアを作って、公開鍵認証の設定をする。で、ログインできるのを確認したらサーバ側でおもむろに authorized_keys の command= に rrsync を設定する。アクセスを許可するサブディレクトリと、必要なら -ro (read-only) オプションも。

これだけで、そのアカウントの権限で SSH ログインする際、許可されたサブディレクトリに対する rsync アクセス ( -ro オプションが付いていれば pull なアクセス) 以外は慈悲なく蹴られるようになる。

仕組みとしては、公開鍵の設定で起動された rrsync が本来リクエストされた rsync のサーバ側コマンドライン (OpenSSH の仕様上、 $SSH_ORIGINAL_COMMAND から取れる) と自分の引数を突き合わせて、問題がなければ rsync を exec するというもの。まぁ言ってしまえば単なるラッパなんだけど、これマジメに自分で書こうと思うとかなりめんどいはず。

まとめ

めんどくさいので細かい設定手順とか書かなかったけど、まぁ実際に自分で rrsync のソースを読んでみてくだせぇ。大した分量じゃないし、設定方法も書いてあるし。

rrsync を適切に設定できれば、万一秘密鍵を盗まれても被害を最小限に抑えられます。

#まぁ、そもそも秘密鍵を盗まれるという管理体制が問題アリアリなんじゃ、というツッコミもあるけど、それはまた別のお話。

参考文献

  • rrsync のソースコード
  • SSHD(8) の AUTHORIZED_KEYS FILE FORMAT セクション

2015-03-07 追記

どうも2月の頭くらいからバックアップが止まってた様子。いろいろ調べてみたところ この FIX で、 crontab に rsync のコマンドラインを書くときにリモートパスをより一層ご丁寧にクォートするようになったことが原因らしい。

"user"@remote-host:\""remote-path"\"

つまりこれは FreeNAS のエンバグか?と言えばそう簡単な話でもなくて、普通はサーバ側の rsync もシェル経由で実行されるので、シェルがクォートを外してくれるから何ら問題はない。一方で rrsync は $SSH_ORIGINAL_COMMAND を自力でバラして解釈するので、結果的にクォートを外さずに rsync にパスを渡してしまい、

rsync: link_stat ""remote-path"" failed: No such file or directory (2)

とか言われるというわけ。そりゃ、ねーわ。

まぁイレギュラーなことしてるのはこっちですよねー、というわけで rrsync にクォート外す処理を入れて回避。

--- ./rrsync    2015-03-07 00:37:07+09  1.1
+++ ./rrsync    2015-03-07 00:43:58+09  1.2
@@ -180,6 +180,8 @@
       s{^$}{.};
       die "$0: do not use .. in any path!\n" if m{(^|/)\\?\.\\?\.(\\?/|$)};
     }
+    # Remove quotation from args
+    s{^["'](.*)["']$}{$1} if m{^".*"$} or m{^'.*'$};
     push(@args, bsd_glob($_, GLOB_LIMIT|GLOB_NOCHECK|GLOB_BRACE|GLOB_QUOTE));
   }
 }

セキュリティ的に本当に大丈夫かなこれ‥‥。

第3次カメラブーム

last-updated at Feb 15, 2015 21:49, in /diary

人生で3度目のカメラブームが来てる。俺の中で。

第1次: 高校生の頃

天安門広場, Zenit-122

カメラ欲しいなと思い立ったものの、当時重度なマニアック志向と金欠を拗らせていたために、 カメラ雑誌の通販広告か何かで見かけて安かった Zenit-122 に飛び付いた。 既に根っからのメガネ野郎だったので、ファインダーとの隙間から入る光のせいで露出計 (そんな立派な機構でもなかったけど) が全っ然アテにならず、電池抜いてフルマニュアルでバシバシ撮ってた。 修学旅行で普通に中国に持って行って、クラスの連中から軽く引かれたりもした。

結果的に写真の (というか露出の) イロハをこの Zenit-122 に教えてもらったわけだけど、 今になって思い返せば、最初に使うカメラのチョイスとしては如何なものか、と我ながら思う。

第2次: 大学生の頃

我が母校, たしか SMENA 8M

現役の Zenit 使い、しかもフルマニュアルに抵抗がない人間の興味がいわゆるソビエトカメラ全般に向かうのは最早必然で、 結果的に SMENA 8M に Zarya 、 Zorki を入手した。 中でも Zarya は、 FED-2 からレンジファインダーを取っ払ってピント目測、 という至極漢気溢れる仕様だった割に、一番扱いやすくて好きなカメラだった。 シャッターリボン切れちゃって以来、修理にも出さずに放置しちゃってるけど。

ちなみに、ダークレス使ってモノクロネガの自家現像とか手を出してたのもこの頃。 まぁ深く追い求める気にはならなかったけど。

第3次: なう

EOS M + EF28mm F2.8

デジタルな時代の流れには抗えず、 IXY DIGITAL 25 IS とか何となくメインで使ってたものの、 型落ちのさらに販売終了目前で安くなってたのに釣られて (ついでに M2 の新機能に全く魅力を感じなかったので) EOS M のダブルレンズキットを衝動買いしたのが去年の秋。

台湾に持って行って久しぶりに街角スナップとかしてて気付いたのは、 やっぱ AF 遅いなぁ、ということ。 図ったかのようなタイミングで AF 性能がガチ強化された EOS M3 が発表された のは見なかったことにして、 MF で満足に使えるレンズとして EF28mm F2.8 を中古で入手。 AF は言うほど遅くない代わりに (ホントに) ジーコジーコうるさくてビックリしたけど、 どうせ MF でしか使わないからいいかな、と。

まぁ今日 CP+ 2015 で M3 の実機触らせてもらったら、 AF めっちゃ速いしダイヤル操作楽だしグリップ快適だしで正直ちょっとだけ いいな なんて思っちゃったんだけど、 まぁ買いませんよ。 M を使い潰しますよ。

台北旅行

last-updated at Feb 06, 2015 15:47, in /diary

先週ちょっくら台北に行ってきたので、その写真を何枚か。

Canon EOS M, EF-M18-55mm F3.5-5.6 IS STM, ISO 100, f/5.6, 1/320 sec, 1/3 EV

Canon EOS M, EF-M18-55mm F3.5-5.6 IS STM, ISO 250, f/5.6, 1/100 sec, 1/3 EV

Canon EOS M, EF-M18-55mm F3.5-5.6 IS STM, ISO 160, f/5.6, 1/30 sec, 0.7 EV

Canon EOS M, EF-M18-55mm F3.5-5.6 IS STM, ISO 6400, f/5.6, 1/30 sec, -1 EV

また行きたい。

git-svn で loose object が corrupt した時のヒント

last-updated at Nov 10, 2014 01:50, in /tech/git

git svn rebase の最中とかにたとえばマシンがお亡くなりになって、もっかい叩いたら

fatal: loose object b2460997f61ed75f4025ea7177f96afe54e5d3bc (stored in /some/where/.git/objects/b2/460997f61ed75f4025ea7177f96afe54e5d3bc) is corrupt
could not detach HEAD
rebase refs/remotes/git-svn: command returned error: 1

とか言われて身動き取れなくて泣きそうな時のヒント、というか自分用のメモ。 前提として、 git-svn しか想定してない。 そもそも全 revision を Subversion リポジトリから最悪引っ張り直さないといけないから泣きそうなのであって、 リモートも Git だったり、 Subversion だけど小規模だってんなら黙って clone し直した方がきっと早い。

まず、 git fsck をかけて corrupt だと言われるオブジェクトファイルを除けていく。 最終的に missing な blob が絞り込めれば、そいつらを作り直せばいいだけ。 ここでもし blob 以外が missing だと言われたら、他をあたってください。

$ git fsck --full
*snip*
broken link from    tree 0b69d49c2b28cd44ab9dd117953c69898dab54ca
              to    blob b2460997f61ed75f4025ea7177f96afe54e5d3bc
broken link from    tree 0b69d49c2b28cd44ab9dd117953c69898dab54ca
              to    blob 6d987d2a105d4e1a1b6e1a0bd245bda6da1e4845
missing blob b2460997f61ed75f4025ea7177f96afe54e5d3bc
missing blob 6d987d2a105d4e1a1b6e1a0bd245bda6da1e4845

次に、既に fetch までは終わってるはずなので、リモートブランチのログを raw フォーマットで眺める。 ハッシュで引っかければ、問題の blob の元がどのリビジョンのどのファイルなのかがわかる。

$ git log --raw remotes/git-svn
*snip*
    git-svn-id: some_repository@rev
*snip*
:100644 100644 d311938... b246099... M  path/to/one
:100644 100644 2c19eff... 6d987d2... M  path/to/another

元ファイルを svn checkout して git hash-object に食わせれば正常かつハッシュの一致する blob が手に入るはず。 それを書き戻してあげれば、オブジェクトファイルに関しては解決。

あとはワーキングコピーが中途半端に rebase されてる可能性があるので、 ちゃんと clean な状態に戻してあげれば晴れて OK なはず。

1 2 3 4 »