この文書について
この文書は、連載記事「LXC 1.0: Blog post series」の一つである以下の記事を翻訳したものです。連載の目次や注意点はこちらを参照してください。
この文書のライセンスは原文と同じく、Creative Commons BY-NC-SA 2.5のもとに提供されています。
コンテナーのセキュリティについて話す時、多くの人はコンテナーが本質的にセキュアか、本質的にセキュアでないかのどちらかであると考えます。実際のところは白か黒かという話ではなく、LXCはセキュリティに関するさまざまな懸念事項を和らげるための、数多くの技術をサポートしているのです。
最初に一つはっきりさせておくと、「特権コンテナーを使う限りLXCはセキュアである」なんてことを人々に教えるようなLXCメンテナーはいないということです。しかしながら、少なくともUbuntuにおいては、わかる範囲ですべての攻撃を防ぐように、cgroupアクセスと広範囲のAppArmorプロファイルの両方を十分に設定したコンテナーを提供するようにしています。
以下では、あなたが思い通りにコンテナーに制限をかけられるよう、LXCがサポートしているさまざまな技術について紹介します。非特権コンテナーを使う場合を除いて、心に留めておいて欲しいのは、あなたがホストに対して管理者権限を与えたくない人に対して、コンテナーに対する管理者権限を与えるべきではない、ということです。
ケーパビリティ(Capability)
LXCに追加された最初のセキュリティ機能は、Linuxのケーパビリティのサポートです。この機能を使うと、コンテナーを起動するまえに削除したいケーパビリティのリストや、追加したいすべてのケーパビリティのリスト(つまり他のすべては削除されます)を設定できます。
これら二つの設定オプションは次のとおりです:
- lxc.cap.drop
- lxc.cap.keep
どちらも、capabilities(7)(訳注:日本語でも良ければcapabilities(7))にリストアップされているケーパビリティ名を使用できます。
これは、コンテナーを安全に保つ良い方法のように思えるでしょう。実際、特定のケースでは安全になると思います。しかしながらシステムコンテナーを使用している場合、すぐに net_admin
や sys_admin
の削除はとても実用的ではなく、さらにそれらを削除するだけではコンテナーを十分に安全にできないことに気づくでしょう(コンテナーの中の管理者は削除されたあらゆるケーパビリティを再度取得できるのです)。
Ubuntuでは、コンテナー起動時の既知の問題に対応するために lxc.cap.drop
で、 sys_module
、 mac_admin
、 mac_override
、sys_time
を削除しています。
Control group
「Control group」は相互に関連はするものの、かなり異なる技術を組み合わせて、複数のことを成し遂げるという意味で興味深いと思います。
- リソース管理
- リソースの分配
- アクセス制限
最初の二つは、本質的にはあまりセキュリティには関係ありません。しかしリソースの分配は、(メモリーやCPU、I/Oの制限を設定することで)ホストへの明確なDoSを防ぐ効果があるでしょう。
最後の一つは、主にデバイスcgroupと呼ばれるもので、どのキャラクターデバイスやブロックデバイスがコンテナーからアクセスできるか、何ができるか(メジャー番号、マイナー番号の組み合わせを用いて、作成、読み込み、書き込みアクセスを個別に制限できます)を定義することができます(メジャー番号、マイナー番号の組み合わせを用いて、作成、読み込み、書き込みアクセスを個別に制限できます)。
LXCにおけるcgroupの設定は、「 lxc.cgroup.*
」オプションを使って行います。これは主に次のようなフォーマットになります: lxc.cgroup.<コントローラー>.<キー> = <値>
たとえば、p1のメモリーを制限するには、次の設定を追加してください:
lxc.cgroup.memory.limit_in_bytes = 134217728
これによりメモリーが128MB(値の単位はバイトです)になります。これは、 /sys/fs/cgroup/memory/lxc/p1/memory.limit_in_bytes
に同じ値を書き込むことで、同じ事が実現できます。
多くのLXCテンプレートは、いくつかのデバイスコントローラーのエントリーのみが標準で設定されています:
# 標準で設定されるcgroup
lxc.cgroup.devices.deny = a
## すべて(ただしこのノードを使っていない場合のみ)のmknodの許可
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
## /dev/null と zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
## コンソール
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 5:1 rwm
## /dev/{,u}random
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 1:9 rwm
## /dev/pts/*
lxc.cgroup.devices.allow = c 5:2 rwm
lxc.cgroup.devices.allow = c 136:* rwm
## rtc
lxc.cgroup.devices.allow = c 254:0 rm
## fuse
lxc.cgroup.devices.allow = c 10:229 rwm
## tun
lxc.cgroup.devices.allow = c 10:200 rwm
## full
lxc.cgroup.devices.allow = c 1:7 rwm
## hpet
lxc.cgroup.devices.allow = c 10:228 rwm
## kvm
lxc.cgroup.devices.allow = c 10:232 rwm
この設定は、コンテナー(通常はudev)に必要なすべてのデバイスの作成を許可し(「 m
」をワイルドカードで指定し)、後半のエントリーで個別に許可しているデバイス以外をすべて(拒否エントリーで「 a
」を指定しすることで)ブロックしています。ここではコンテナーが動作するために必要な機能がすべて列挙されています。
コントローラーやコントロールファイル、サポートされる値についての最新のドキュメントは、このURLを参照してください: https://www.kernel.org/doc/Documentation/cgroups/
AppArmor
少し前に、AppArmorプロファイルのサポートもLXCに追加されました。Apparmorのサポートはより単純で、たった一つのオプションである「 lxc.aa_profile
」を設定するだけで、コンテナーで使用するAppAromorプロファイルを指定できます。
LXCはコンテナーを設定したあと、コンテナーを起動する前にAppArmorにそのプロファイルへの移行ができるかどうか問い合わせます。UbuntuのLXCプロファイルは、コンテナーを抜け出したり、ホストにダメージを与え得る既知の方法を防ぐために、若干複雑になっています。
現在のところ、Ubuntuは3つのAppArmorプロファイルを提供しており、lxc.aa_profile
で指定できます:
- lxc-container-default (
lxc.aa_profile
が設定されなかった時の初期値です) - lxc-container-default-with-nesting (初期値に加えて、入れ子コンテナーに必要な権限を追加しています)
- lxc-container-default-with-mounting (初期値に加えて、extやxfs、btrfsファイルシステムをマウントするための権限を追加しています)
- unconfined (コンテナーのAppArmorサポートを無効化するための特別な値です)
/etc/apparmor.d/lxc/
にある設定ファイルを複製し、必要な設定を追加し、独自の名前を指定し、「 sudo /etc/init.d/apparmor reload
」によってAppAArmorを再読み込みし、「 lxc.aa_profile
」に新しいプロファイル名を指定することで、ユーザー独自の設定を追加することができます。
SELinux
SELinuxのサポートはAppArmorとだいぶ似通っています。SELinuxのコンテキストは「 lxc.se_context
」で設定可能です。
例えば次のように設定します:
lxc.se_context = unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023
AppArmorと同様に、LXCはコンテナーを起動する前に新しいSELinuxコンテキストにスイッチします。私の知る限り、現時点でSELinuxのコンテキストに初期値を設定しているディストリビューションはありませんが、ほとんどのディストリビューションはSELinuxサポートを有効にしてLXCをビルドしています(Ubuntuも同様で、ホスト起動時にAppArmorではなくSELinuxを選択できます)。
Seccomp
Seccompは最近のカーネルに追加された仕組みで、システムコールのフィルタリングができます。ユーザーは、seccompポリシーファイルを作成した上で、コンテナーの設定に「 lxc.seccomp
」を記述することでそのポリシーを反映できます。他と同様に、このポリシーは実行中のコンテナーにのみ適用されて、システムコールをリジェクトした上で指定した戻り値を返すように変更できます。
例えば、(制限や不便なところはありますが)seccompのポリシーファイルは次のようになります:
1
whitelist
103
これは、103番のシステムコール( syslog
)のみコンテナーで許可し、それ以外を拒否します。
seccompはより低レベルの機能で、非常に限定された状況でのみ使えるものです。すべてのシステムコールは名前ではなくID番号で参照され、アーキテクチャによってこの番号は異なります。また、現在のところ、ホストが64ビットでseccompのポリシーファイルをロードしている場合、すべての32ビットのシステムコールは拒否されます。この問題を解決するには、個人単位のseccompプロファイルが必要になりますが、対応の優先度は高くありません。
ユーザー名前空間
コンテナーを確実に安全にするためのおそらく唯一の、大事な方法を伝え忘れていました。LXCは最近、ユーザー名前空間(User Namespace)をサポートしました。その機能や使い方については、今後の記事で書く予定ではありますが、簡単に言うとLXCはもはや管理者権限で実行する必要はなく、たとえ攻撃者がコンテナーから抜け出せたとしても、ホスト上の一般ユーザーと同じ権限しか得られないようにできます。
これは、UID/GIDのレンジを既存のユーザーに割り当てることで実現しています。これによりホスト上のユーザーは新しいユーザー名前空間をクローンでき、すべてのUID/GIDはユーザーのレンジ内のUID/GIDにマップされます。
この方法だと、LXCを使う個々のユーザーごとにかなりとんでもない量のUIDとGIDが必要になることを意味します。理想的な世界において、ユーザーごとのさらにコンテナー毎に、65536個のUIDとGIDを割り当てることになるでしょう。現実の世界では、いくつかのシステムではすぐにUID/GIDを使い切ることが起こりうるため、私は「ぴったり」65536個のUIDとGIDだけをLXCを使うであろう個々のユーザーに割り当て、すべてのコンテナーでそのレンジを共有するようにしています。
とにかく、今のところユーザー名前空間に関する情報はこれで十分です。実際の設定や使い方については、次の記事の非特権コンテナーで説明する予定です。