セキュアな Linux コンテナーのためのマニュアル

SELinux と Smack で軽量コンテナーを強化する

またの名を VPS (Virtual Private Server)、Jail としても知られる軽量コンテナーは、信頼できないアプリケーションやユーザーを制限するために設計されたセキュリティー・ツールであると思われがちですが、現状のように構成されていたのでは十分なセキュリティーの保証になりません。これらの軽量コンテナーを SELinux または Smack ポリシーを使って強化することによって、遙かにセキュアなコンテナーを Linux® に実装することができます。この記事では、Linux Security Module で保護された一層セキュアなコンテナーを作成する方法を紹介します。SELinux ポリシーと Smack ポリシーはどちらも現在進行中の作業であり、それぞれのコミュニティーの支援による改善が見込まれています。

Serge Hallyn, Software Engineer, IBM

Serge Hallyn は IBM の Linux Technology Center の一員であり、Linux のカーネルとセキュリティーに関する業務を行っています。彼は College of William and Mary にてコンピューター・サイエンスの博士号を取得しています。彼はセキュリティー・モジュールをいくつか作成し、またいくつかのセキュリティー・モジュールに貢献してきました。現在は、仮想サーバー機能や、アプリケーションのチェックポイントとリスタート、POSIX ファイル機能などのサポートを追加するための業務に集中しています。



2009年 2月 03日

コンテナーについて初めて耳にした人の一般的な反応は、「どうやってセキュアなコンテナーを作成するのか」というものです。この記事では、LSM (Linux Security Module) を使ってコンテナーのセキュリティーを強化する方法を説明することによって、その質問にお答えします。具体的には、セキュリティーの目標を指定し、その目標を Smack および SELinux セキュリティー・モジュールのそれぞれを使って達成する方法を説明します。

Linux コンテナーに関する予備知識として、「LXC: Linux コンテナー・ツール」(developerWorks、2009年2月) を読んでください。

Linux コンテナーとは、実際には以下の Linux 技術に基づいて構築される概念上の手法のことです。

  • リソース名前空間により、プロセス、ファイル、SYSV IPC リソース、ネットワーク・インターフェースなどの検索操作をすべてコンテナー内部で行えるようにします。
  • 制御グループにより、コンテナーにリソース制限を設定できるようにします。
  • ケイパビリティー・バウンディング・セットにより、コンテナーが使用できる特権が制限されます。

コンテナーとして錯覚させるには上記の技術を調整しなければなりません。この機能をすでに提供しているプロジェクトには、以下の 2 つがあります。

  • libvirt は、Xen ハイパーバイザー、QEMU エミュレーター、KVM、さらに軽量コンテナーを使用して仮想マシンを作成できる大規模なプロジェクトです。
  • liblxc はそれよりも小規模なライブラリーとユーザー空間コマンドのセットで、1 つにはカーネル開発者が素早く簡単にコンテナーの機能をテストできるように作成されています。

LXC: Linux コンテナー・ツール」は liblxc をベースに書かれているので、ここでも引き続き liblxc を使用します。ただし、この記事で説明する内容は、libvirt のコンテナー・サポートを用いても同じく簡単に行うことができます。

主役その 1: LSM

本題に入る前に、LSM をあまりよく知らない読者のために LSM について概説しておきます。ウィキペディアの記事によると、LSM (Linux Security Module) は、Linux カーネルが特定のセキュリティー実装に偏ることなく、多種多様なコンピューター・セキュリティー・モデルをサポートできるようにするフレームワークです。LSM は GNU General Public License の条件の下で使用が許可され、Linux 2.6 以降、Linux カーネルの標準装備となっています。(中略) LSM は、強制アクセス制御モジュールを正常に実装するために必要とされる具体的な要件をすべて満たすように設計されています。同時に、Linux カーネルに対する変更を必要最小限に抑えるようにも設計されています。LSM では、Systrace で使用されているような、システム・コールに介入する手法を避けています。その理由は、この手法だとマルチプロセッサー・カーネルに対応することができず、TOCTTOU (競合) 攻撃を受けやすくなるからです。LSM ではこれに代わる手法として、カーネル内に「フック」(モジュールへの upcall) を挿入する手法を用います。この手法では、ユーザー・レベルのシステム・コールによって重要な内部カーネル・オブジェクト (i ノードやタスク制御ブロックなど) へのアクセスが行われるすべてのポイントに、フックを挿入します。(中略) このプロジェクトのスコープは、メインストリーム・カーネルに大々的で複雑な変更パッチが必要にならないよう、アクセス制御の問題の解決に絞り込まれています。LSM は汎用の「フック」や「upcall」メカニズムとして意図されているわけでも、仮想化をサポートするわけでもありません。(中略) LSM のアクセス制御の目的はシステム監査の問題と密接に関係していますが、システム監査とは微妙に異なります。システム監査の場合、アクセス試行のすべてを記録しなければなりませんが、LSM には不可能です。すべてのアクセス試行を記録するには、カーネルがシステム・コールの失敗を避けるために重要なオブジェクトの処理に近づく前にエラー・コードを返すようなケースも検出しなければならず、さらに大量のフックが必要になるからです。

システム・セキュリティーには、ある意味矛盾する 2 つの目標があります。その 1 つは、完全かつきめ細かなアクセス制御を達成することです。それには、情報のリークまたは破損が考えられるすべてのポイントで、制御を行使しなければなりません。あまりにもきめの粗い制御では、制御していないことと同じです。例えば極端な場合、すべてのファイルを 1 つのタイプとして分類し、そのうちのいずれか 1 つを全ユーザーが読み取り可能なファイルにしなければならないとしたら、そのタイプのすべてのファイルを全ユーザーが読み取り可能なファイルにしなければなりません。

その一方、構成が単純である必要もあります。そうでないと、管理者がデフォルトで必要以上のアクセス権を与えてしまいがちになるからです (この点についてはいくら強調しても足りないほどで、アクセス権をむやみやたらに与えることは、制御してないことと同じです)。例えば、プログラムを機能させるために数千ものアクセス・ルールが必要な場合、管理者はアクセス・ルールの 1 つひとつが実際に必要であるかどうかを検証せずに、プログラムに過剰なアクセス権を与えることになってしまう可能性が考えられます。

Linux の主要な 2 つのセキュリティー・モジュールでは、それぞれ別の取り組み方でこの 2 つの目標のバランスを取ります。

  • SELinux では、まずあらゆるものを制御するところから始め、優れたポリシー言語を使用してポリシー管理を単純化していきます。
  • Smack では単純なアクセス制御を提供することを第一とします。

主役その 2: SELinux

SELinux は、絶大な知名度を誇る Linux の MAC (強制アクセス制御) システムです。確かに SELinux に反対を唱える人々もまだ残っていますが、人気の高い Fedora® ディストリビューションには何年もの間、SELinux がデプロイされているという事実が、その成功を如実に物語っています。

SELinux の構成にはモジュール式ポリシー言語を使用するため、ユーザーが簡単にインストール済みのポリシーを更新することができます。この言語にはインターフェースも用意されており、上位レベルのステートメントを使って複数の詳細な「許可」ステートメントを表すことができるようになっています。

この記事では、新しいインターフェースを使用してコンテナーを定義します。コンテナーには多数のアクセス権を与えなければならないため、インターフェース自体はかなり大きなものになりますが、このインターフェースを使って新規コンテナーを作成する手順は非常に簡単です。このインターフェースがコアの配布ポリシーの一部となることを期待します。


主役その 3: Smack

Smack は、Simplified Mandatory Access Control Kernel の略語です。このポリシーではまず始めに、すべてのプロセス、ファイル、ネットワーク・トラフィックに単純なテキストのラベルを付けます。新しく作成されるファイルには、そのファイルを生成したプロセスと同じラベルが付けられます。いくつかのデフォルト・タイプには常に、明確に定義されたアクセス・ルールがあります。プロセスは、同じラベルが付いたオブジェクトに対しては常に読み取り操作と書き込み操作を実行することができます。Smack アクセス・ルールを迂回する特権を制御するのは POSIX ケイパビリティーです。したがって、CAP_MAC_OVERRIDE が設定されたタスクは Smack アクセス・ルールを書き換えることが可能で、CAP_MAC_ADMIN が設定されたタスクはルールおよびラベルを変更することができます。これらの特権については、「POSIX ファイル・ケイパビリティー root の権限を分配する」(「参考文献」を参照) で具体的に説明しています。


セキュリティーの目標

やみくもにポリシーを適用して何らかの有効な結果を期待するのではなく、まずは明確なセキュリティーの目標を定義することにします。Smack は単純なポリシーであることから、達成可能な目標は限られますが、ここでは以下の目標を達成することを目指します。

  1. 別個のファイルシステムで Web サービス、ssh サービスをそれぞれ提供するコンテナーを作成します。
  2. コンテナーを互いから保護します。コンテナー vs1 がもう 1 つのコンテナー vs2 で所有するファイルを読み取ったり、そのタスクをキルしたりできないようにします。
  3. ホストは、ホスト内に保存されている重要ファイルをコンテナーから保護できるようにします。
  4. コンテナー上の Web サーバーおよび ssh サーバーにコンテナー外部からアクセスできるようにします。

全体的なセットアップ

この記事では 2 つの実験を行います。まず SELinux で保護されたコンテナーをセットアップし、次に Smack で保護されたコンテナーをセットアップします。いずれの実験でも、事前に行うセットアップの内容はほとんど同じです。

実験には実際のマシンを使用することもできますが、仮想マシンを使ったほうが簡単で安心だと思います。QEMU または KVM を使用するには、qemu-img create vm.img 10G を実行してハード・ディスクを作成します。

kvm -hda vm.img -cdrom cdrom.iso -boot d -m 512M などのコマンドを使用して、CDROM から仮想マシンをブートします。推奨される CDROM イメージとしては、fedoraproject.org/get-fedora にアクセスして i386 対応の Fedora 10 インストール DVD をダウンロードしてください。前述のコマンドに含まれる cdrom.iso は、ダウンロードしたファイル名に置き換えます。イントール時にはデフォルト設定のほとんどを使用できますが、office and productivity の選択は必ず解除して、software development を選択するようにしてください。また、bridge-utils、debootstrap、ncurses-devel といった RPM パッケージをインストールする場合には、yum パッケージ・マネージャーを使用することになるはずです。

次に、カスタム・カーネルをコンパイルします。kernel-sources RPM パッケージをダウンロードし、enable-netns.patch (「ダウンロード」セクションを参照) を適用してネットワーク名前空間を指定します (2.6.29 ではアップストリームになりますが、Fedora 10 ではそうなりません)。構成を変更した上で root として以下の命令を実行し、コンパイルおよびインストールを完了します。

yumdownloader --source kernel
rpm -i kernel*
cd rpmbuild
rpmbuild -bc SPECS/kernel-*
cd BUILD/kernel-2.6.27/linux-2.6*
patch -p1 < ~/enable-netns.patch
make menuconfig
make && make modules_install && make install

どちらの実験でも、make menuconfig のステップでは Network Namespacesを選択します (Networking support -> Networking options メニュー)。Smack の実験を行う際には、さらに Security options メニューにアクセスして SELinux の選択を解除し、Smack オプションを選択してください。また、/boot/grub/grub.conf でデフォルト・ブート・エントリー 1 を 0 に戻さなければならない場合もあります。

ここで、liblxc を試してみたいので、そのための準備をします。liblxc の基本的な使用方法については「LXC: Linux コンテナー・ツール」で詳しく説明しているので、この記事では省略します。コンテナー・ネットワーク・デバイス同士が通信するためのブリッジは、container_setup.sh スクリプト (「ダウンロード」セクションを参照) を実行するだけでセットアップすることができます。また、このスクリプトはファイアウォールをクリアします (デフォルトでは、ファイアウォールはブリッジを処理するようにセットアップされていません)。さらに Smack の実験を行っている場合には Smack ポリシー (後で、/etc/smackaccesses ファイル内で作成します) もセットアップします。リブート後に必ず container_setup.sh を実行するか、あるいは方法がわかるようであれば、ブート時に container_setup.sh が自動的に実行されるように設定してください。

これで、マシンの準備は完了です。早速 liblxc を試してみましょう。最新のソースは、cvs を使用して lxc.sf.net からダウンロードすることができます。コンパイルするには、以下のコマンドを実行します。

cvs -d:pserver:anonymous@lxc.cvs.sourceforge.net:/cvsroot/lxc login
cvs -z3 -d:pserver:anonymous@lxc.cvs.sourceforge.net:/cvsroot/lxc co -P lxc
cd lxc
./bootstrap && ./configure && make && make install

README を調べると、開始する際のオプションにはかなりの数があることがわかります。コンテナーは極めて軽量にすることができます。これは、コンテナーはファイルシステムをはじめとする多数のリソースをシステムと共有できるためです。しかし、ここでの目標は単純な分離を行うことなので、lxc-debian スクリプトを使ってコンテナーごとの完全な debian chroot イメージを作成します。まず始めに、vsplain という名前のコンテナーを作成します。

mkdir /vsplain
cd /vsplain
lxc-debian create
	container name: vsplain
	hostname: vsplain
	IP 10.0.2.20
	gateway: 10.0.2.2

このコンテナーの構成は、/usr/local/var/lxc/vsplain ディレクトリーに保存されます。cgroupというファイルを調べると、devices. で始まる行がいくつかあるはずです。これらの行は、コンテナーによるデバイスの作成、読み取り、書き込みを仲介するデバイス・ホワイトリスト cgroup に対するディレクティブです。

このコンテナーを起動するために、lxc-start -n vsplainコマンドを実行します。ログイン・プロンプトが表示されるので、ユーザー名として root を使用し、パスワードは入力せずにコンテナーにログインします。コンテナーが起動したら、以下を実行してください。

apt-get install openssh-server
apt-get install apache

これで、vsplain の IP アドレスとして 10.0.2.20 を使用し、ホストの IP アドレスとして 10.0.2.15 を使用すると、KVM ホストからコンテナーに ssh 接続してコンテナーの Web ページを表示できます。また、KVM ホストの root 端末から lxc-stop -n vsplain コマンドを実行することで、いつでもコンテナーをシャットダウンすることができます。

このテンプレートから 2 つの新しい仮想マシンを複製すれば、今後の作業時間の節約になります。複製するには、vm をシャットダウンして以下のコマンドを実行してください。

cp vm.img selinux.img
cp vm.img smack.img

SELinux で保護されたコンテナー

このセクションで使用するコンテナーの SELinux ポリシーは、ポリシー・モジュールで構成します。このモジュールは、refpolicy -- SELinux Reference Policy development mail list に投稿されているので、/root/vs ディレクトリー内の vs.if、vs.fc、vs.te という 3 つのファイルそれぞれにポリシーをダウンロードしてください。新規モジュールをコンパイルしてインストールする方法は以下のとおりです。

cp -r /usr/share/selinux/devel /usr/share/selinux/vs
cp /root/vs.?? /usr/share/selinux/vs/
cd /usr/share/selinux/vs
make && semodule -i vs.pp

次に、以下のように lxc-debian を使用してコンテナー /vs1 および /vs2 を作成し、それぞれのファイルシステムのラベルを再設定します。

mkdir /vs1; cd /vs1
lxc-debian create
	container name: vs1
	hostname: vs1
	address: 10.0.2.21
	gateway: 10.0.2.2
	arch: 2 (i386)
mkdir /vs2; cd /vs2
lxc-debian create
	container name: vs2
	hostname: vs2
	address: 10.0.2.22
	gateway: 10.0.2.2
	arch: 2 (i386)
fixfiles relabel /vs1
fixfiles relabel /vs2

コンテナーを起動すると (例えば、lxc-start -n vs1 を使用)、おそらく SELinux アクセス拒否に関する監査メッセージを受け取ることになると思いますが、心配には及びません。コンテナーは正常に起動してネットワーク・サービスが有効になり、2 つのコンテナーが分離された状態になります。コンテナーを起動する前に mount --bind / /vs1/rootfs.vs1/mnt を実行してコンテナー vs1 が他のファイルシステムを覗けるようにした場合、root ユーザーであっても ls /mnt/root 操作は拒否される結果になります。

この仕組みを理解するには、vs.if インターフェース・ファイルを調べてください。このファイルが定義する container というインターフェースは、引数を 1 つ取り、その引数をコンテナーの基本名として定義します。vs.te ファイルはこの関数をコンテナー vs1 と vs2 で 2 回呼び出します。インターフェースでは $1 が引数に展開されるため、container(vs1) を呼び出すと $1_tvs1_t になります (ここから先は、コンテナーの基本名を vs1 と定義しているという前提で説明を進めます)。

とりわけ重要な行は、vs1_exec_t が関係している行です。コンテナーは、vs1_t タイプで動作しますが、コンテナーがこのタイプになるのは、unconfined_t が、コンテナーの vs1_exec_t タイプが設定された /sbin/init を実行した時点です。

ポリシーの残りのほとんどの部分は、ただ単に、システムのネットワーク・ポート、デバイス、コンソールなどの部分にアクセスするのに十分な特権をコンテナーに付与するためだけにあります。このように、このインターフェースは既存の SELinux リファレンス・ポリシーが持つきめ細かな特性に従います。この後説明するように、Smack で保護されたコンテナーのポリシーは遙かに単純ですが、それと引き換えに、不正なシステム・サービスに対する保護の柔軟性は大幅に低下します。

必要な作業はもう 1 つあります。お気付きかもしれませんが、コンテナーは $1_exec_t、つまり /sbin/init を上書きすることができません。その代わりに、以下のような方法を採れます。

mv /sbin /sbin.bak
mkdir /sbin
touch /sbin/init

この結果、/sbin/init は vs1_file_t タイプになります。コンテナー管理者がなぜこのようにタイプを変更しようとする場合があるかと言えば、この変更によって、コンテナー (ssh デーモンを含む) は unconfined_t ドメインで起動されることになります。すると、コンテナー管理者には特権シェルが与えられ、ここで強制しようとしている SELinux の制約のを受けずに済んでしまうからです。

この事態を防ぐには、カスタム・スクリプトによるコンテナーの起動で、sbin/init のラベルを vs1_exec_t に変更してからコンテナーを起動する必要があります。実のところ、コンテナー管理者が気にしないのであれば、init の初期コピーをコンテナーに再びコピーし、そのラベルを変更することは可能ですが、ここでは既存の init のラベルを変更するだけにとどめておきます。

cat >> /vs1/vs1.sh << EOF
#!/bin/sh
chcon -t vs1_exec_t /vs1/rootfs.vs1/sbin/init
lxc-start -n vs1
EOF
chmod u+x /vs1/vs1.sh

これで、コンテナーは手動で lxc-start を使って起動するのではなく、/vs1/vs1.sh で起動しなければならなくなりました。


Smack で保護されたコンテナー

Smack を有効にしてカーネルを再コンパイルしてください。/root/rpmbuild/BUILD/kernel*/linux* ディレクトリーに移動し、make menuconfig を実行してセキュリティー・メニューに進むことで、簡単に SELinux を無効にして Smack を有効にすることができます。あとは単純に、make && make modules_install && make install のステップを繰り返すだけです。

ユーザー空間が SELinux を構成しないようにする必要もあります。それには、SELinux 管理 GUI を使用して SELinux を無効にするか、/etc/selinux/config を編集して SELINUX=disabled を設定します。また、Smack ポリシーをインストールするには、ブート時に以下の追加ステップを実行する必要があります。

mkdir /smack
cd /usr/src
wget http://schaufler-ca.com/data/080616/smack-util-0.1.tar
tar xf smack-util-0.1.tar; cd smack-util-0.1
make && cp smackload /bin

実際の Smack ポリシーは、リスト 1 のようなものです。

リスト 1. smackaccesses
vs1 _ rwa
_ vs1 rwa
vs2 _ rwa
_ vs2 rwa
_ host rwax
host _ rwax

上記のポリシーを /etc/smackaccesses という名前のファイルにコピーします。次に /bin/container_setup.sh を実行するときには、このファイルが smackload にロードされます。

このポリシーはかなり単純なもので、デフォルトでは、すべてのラベルが、_ のラベルが付いたデータを読み取ることができます。そこで、ここではコンテナーからアクセスできないようにされている、ホスト専用のデータを対象とした host という新しいラベルを定義し、このラベルを container_setup.sh スクリプトで cgroups ファイルシステムに割り当てることにします。/etc/shadow などのその他の機密ファイルにも当然、このラベルを付けます。

ラベルを付けるコンテナーとして、vs1vs2 を定義します。デフォルトでは、これらのコンテナーはそれぞれ独自のデータにアクセスできるので、さらに _ に対する書き込み操作を許可するルールを追加して、コンテナーがネットワーク・パケットを送信できるようにします。vs1vs2 のデータにはアクセスできません。またその逆も然りなので、コンテナーは互いに保護されます。

前述したように、Smack アクセス・ルールを定義または迂回できるかどうかを決定するのは、CAP_MAC_ADMIN および CAP_MAC_OVERRIDE ケイパビリティーです。そこで、コンテナーにこれらのケイパビリティーが設定されないようにする必要があります。それには、ヘルパー・プログラム、dropmacadmin.c (「ダウンロード」セクションを参照) を使用してください。コンテナーのライブラリー・バージョンはホストとは異なるため、このプログラムは以下のように静的にコンパイルしなければなりません。

gcc -o dropmacadmin dropmacadmin.c -static
cp dropmacadmin /bin/

vs1 という名前の新しいコンテナーを作成します。

mkdir /vs1; cd /vs1
lxc-debian create
	container name: vs1
	hostname: vs1
	address: 10.0.2.21
	router: 10.0.2.2
	arch: 2 (i386)

vs1 のファイルシステム内にあるすべてのファイルに、vs1 というラベルを設定します。

for f in `find /vs1/rootfs.vs1`; do
	attr -S -s SMACK64 -V vs1 $f
done

ここで、コンテナーを安全に起動するスクリプトを作成します。つまり、プロセス・ラベルを vs1 に設定し、コンテナーの /sbin/init を dropmacadmin などでラップするスクリプトを作成します。

cat >> /vs1/vs1.sh << EOF
#!/bin/sh
cp /bin/dropmacadmin /vs1/rootfs.vs1/bin/
attr -S -s SMACK64 -V vs1 /vs1/rootfs.vs1/bin/dropmacadmin
echo vs1 > /proc/self/attr/current
lxc-start -n vs1 /bin/dropmacadmin /sbin/init
EOF
chmod u+x /vs1/vs1.sh

vs1 に、マウント先の tmpfs ファイルシステムに対する書き込み操作を許可するには、以下のコードも必要になります。

sed -i 's/defaults/defaults,smackfsroot=vs1,smackfsdef=vs1/' \
	/vs1/rootfs.vs1/etc/fstab

上記のコードにより、/dev/shm にマウントされた tmpfs ファイルシステムには vs1 ラベルが設定されるため、vs1 がこのファイルシステムに対して書き込み操作を行えるようになります。このようにしないと、vs1 init スクリプトはネットワークのセットアップ中に使用する /dev/shm/network ディレクトリーを作成することができません。同様に、ram ベースの /tmp を使用する場合にも、これと同じオプションが必要になります。

ここでもう一度、vs1 が他のファイルシステムを覗けるようにします。vs1 を作成した手順に従って、vs2 を作成してください (各ステップでは、vs1vs2 に置き換えます)。次に、vs1 の /mnt の下に root ファイルシステムをバインド・マウントします。

mount --bind /vs1 /vs1
mount --make-runbindable /vs1
mount --rbind / /vs1/rootfs.vs1/mnt

vs1.sh を使用してコンテナーを起動します。KVM ホストからはまだ、vs1vs2 の Web ページを表示できることに注意してください。また、vs1 はネットワーク上で vs2 にアクセスできないだけでなく、vs2 のファイルを参照することもできません。

vs1:~# ls /mnt/
  (directory listing)
vs1:~# ls /mnt/vs2/rootfs.vs2
  ls:/mnt/vs2/rootfs.vs2: Permission denied
vs1:~# mkdir /cgroup
vs1:~# mount -t cgroup cgroup /cgroup
vs1:~# ls /cgroup
  ls:/mnt/vs3: Permission denied
vs1:~# mknod /dev/sda1 b 8 1
  mknod: `/dev/sda1': Operation not permitted
vs1:~# mount /mnt/dev/sda1 /tmp
  mount: permission denied

このコンテナーが参照できるのは、ホストのファイルシステムです。そのため、保護する必要がある対象にはすべて host ラベルを付けることができます。cgroup ファイルシステムで使ったのはこの方法で、そのために ls /cgroup は失敗したというわけです。

最終的には、デバイス・ホワイトリスト cgroup によって、ディスク・デバイスを作成できないだけでなく、ディスク・デバイスが存在するとしても、マウントできないようになっています (/mnt を使用しているため)。

もちろん、このセットアップ方法では、コンテナー管理者が /mnt/dev/sda1 を削除できるとともに、様々な手段でホストを混乱させることができるため、このバインド・マウントは説明のためという目的以外では、明らかに好ましくありません。

SELinux システムでは、デフォルトの (そして簡単な) ルートでネットワーク上でのコンテナー相互の対話を可能にしていました。Smack では、それとは逆のことが当てはまります。現時点では、コンテナー相互の対話を可能にするのは非常に困難です。しかし近い内に IP アドレスにラベルを設定する機能が登場し、コンテナー間の通信を可能にするポリシーを設定できるようになるはずです。

この Smack ネットワーキングのセットアップには、もう 1 つ問題があります。それは、kill -9 -1 コマンドによって、システム上のすべてのタスクがキルされてしまうことです。コンテナー内のタスクがこのコマンドを実行するときには、同じコンテナー内のタスクだけをキルしなければなりません。この振る舞いは現在、アップストリーム・カーネルでは修正されていますが、私たちが使用している Fedora 10 ではまだ未修正です。そのため、すべてのタスクに -9 シグナルが送信されることになります。

SELinux で保護されたコンテナーでは、SELinux によってシグナルがコンテナー境界を通過できないようになっているため、kill -9 -1 は事実上安全です。一方 Smack では、タスクにネットワークと同じくデフォルトで _ のラベルが付けられます。ネットワークに対する書き込み許可を可能にするため、コンテナーには _ への書き込みを許可していること、そして Smack ではタスクのキルは書き込みアクセスと見なされることから、コンテナー管理者にシステム全体で任意のタスクをキルできるようにしていることと同じです。

もう 1 つの欠点 (これは、SELinux コンテナーにもあります) は、Unix98 疑似端末に関係します。2 つのグラフィック端末を開いて、最初の端末で vs1 を起動し、/dev/pts を調べてみてください。少なくとも各端末に属する 2 つのエントリー、0 と 1 があるはずです。つまり、vs1 コンテナーから、もう一方の端末に対応するエントリーに書き込めるということです。

Fedora カーネルでのソリューションは 2 つあります。まず 1 つは、デバイス・ホワイトリスト cgroup を使用して、コンテナーがデバイスを開けないようにするという方法です。ただしコンテナーに端末へのアクセス権を付与するためには、コンテナーが起動するたびに、この作業を手動で行わなければなりません。これと同じ効果を実現できるのが、SELinux と Smack のラベルを適用するというもう 1 つのソリューションです。

新しい 2.6.29 カーネルは、devpts 名前空間をサポートします。したがって、コンテナーは /dev/pts を再マウントした後、ホストまたは他のコンテナーに属する devpts エントリーにはアクセスできなくなります。


まとめ

この記事では、LSM 保護コンテナーを作成するための基本的なツールを紹介しましたが、まだやらなければならない作業が大量に残っています。

  • Smack では、host としてラベルを付けるファイルを選択しなければなりません。
  • SELinux では、container インターフェースを微調整してから、アップストリーム・リファレンス・ポリシーに組み込む必要があります。

このような作業を続けながら LSM 保護コンテナーで十分な経験を積むまでは、信頼できない root ユーザーから保護する上で、これらのメカニズムに全面的に頼るべきではありません。

私の知る限り、コンテナーを作成する際のベスト・プラクティスはまだ確立されていませんが、始める価値のあるベスト・プラクティス案はいくつかあります。何よりもまず、コンテナー (およびホスト) の重複を最小限にするとともに、分離を確実にするという、ある意味相反する 2 つの目標を集約させようとしていることを忘れないでください。この 2 つの目標を達成するには、コンテナーがまったく実行されない単一の完全かつ最小限の rootfs を作成し、これにすべてのコンテナーが読み取り可能なタイプのラベルを付けるという方法が考えられます。その上で、lxc-sshd スクリプトのカスタム・バージョンを使用し、実際のコンテナーをそれぞれプロトタイプに基づいて作成し、コンテナーのファイルシステムの大部分に使用する読み取り専用マウントを作成します。それとともに、コンテナー専用の書き込み可能な場所 (例えば、/scratch) を提供して、コンテナーがファイルを保存できるようにします。各コンテナーは専用のマウント名前空間を持つことから、そのコンテナー専用にする必要があるファイルやディレクトリー、あるいはそのコンテナー専用の共有ディレクトリーから書き込み可能にする必要があるファイルやディレクトリーなどをバインド・マウントすることができます。例えば、コンテナーに専用 /lib が必要な場合には、mount --bind /scratch/rootfs/lib /lib を実行することができます。同様に、管理者はすべてのコンテナーが起動時に必ず mount --bind /scratch/shadow /etc/shadow を実行するようにもできます。

この記事で説明した SELinux と Smack 両方の手法で明らかな制約は、コンテナー管理者が管理者独自のコンテナー内での情報のフローを制御するために LSM を利用できないことです。これらの手法では単純さを期して、コンテナー内のタスクはすべて、MAC ポリシーによって同じように扱われるようになっています。コンテナー管理者が独自の LSM ポリシーを指定できるとともに、ポリシーそれぞれの制約を受けずに済ますことができないようにする方法については、別の記事で説明したいと思います。

この資料は Defense Advanced Research Projects Agency (米国防総省国防高等研究計画局) が Agreement No. HR0011-07-9-0002 の下に行った作業の成果を基にしたものです。

謝辞

Smack で保護されたコンテナーの実現を支援してくれた Smack の作成者、Casey Schaufler 氏、そして親切にも SELinux ポリシーに関するフィードバックを提供してくれた Dan Walsh 氏に感謝いたします。


ダウンロード

内容ファイル名サイズ
Code for this articlecode.zip3KB

参考文献

学ぶために

製品や技術を入手するために

  • SourceForge.net の Linux Resource Containers プロジェクトは Linux カーネルでのアプリケーション・コンテナー実装のコードを集めたリポジトリーです。ここが、linux-kernel mailing list に送られる前のコードのステージング・エリアとなります。
  • developerWorks から直接ダウンロードできる IBM ソフトウェアの試用版を利用して皆さんの次期 Linux プロジェクトを構築してください。

議論するために

その他のダウンロード

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=374731
ArticleTitle=セキュアな Linux コンテナーのためのマニュアル
publish-date=02032009