はじめに

LXC コンテナは以下のような 2 種類の構成を取ることができます:

  • 特権コンテナ
  • 非特権コンテナ

特権コンテナは以前から存在するスタイルのコンテナと考えることができます。特権コンテナは全く安全ではありません。この種類のコンテナは非特権コンテナが利用できない場合で、ホストに対して root 権限を与えることになるコンテナユーザを信頼できる場合にのみ使うべきです。

非特権コンテナは LXC 1.0 (2014 年 2 月リリース) で導入されました。非特権コンテナにはかなり最近のカーネル (3.13 以上) が必要です。非特権コンテナの利点は、コンテナは root 権限を使った攻撃に対して安全であると考えられるので、カーネルのセキュリティ問題に対処している限りは、コンテナは安全であるということです。

特権コンテナは安全ではないと見なせるので、一般的にはコンテナは CVE や迅速な修正が必要なセキュリティ上の問題を悪用することからは逃れられないでしょう。しかし、私たちはホストを予期しないダメージから守るために、このような問題の影響を軽減するようにします。

特権コンテナ

特権コンテナは、コンテナの uid 0 がホストの uid 0 にマッピングされるコンテナとして定義されます。
このようなコンテナでは、ホストの保護とコンテナからの脱出の防止は、強制アクセス制御 (apparmor, selinux)、seccomp フィルタ、ケーパビリティの削除、名前空間の利用を通して行います。

以上の技術の組み合わせは、主にホストに対する予期しないダメージを防ぐでしょう。ここで言うダメージとは、ホストのハードウェアやホストカーネルの再設定、ホストのファイルシステムへのアクセスのような行為です。

LXC 開発者は、このようなコンテナは root 権限を使った攻撃に対して脆弱であり、安全にはできないという見解を取っています。

特権コンテナは、信頼できる作業を実行するような環境や、コンテナ内で root 権限で信用できないタスクが実行されていない環境では今でも役に立ちます。

私たちは、このようなコンテナから抜け出し、ホスト上で root 権限を全て取得するような多数の exploit を知っています。
これらの exploit には普通にブロックできるものもありますので、それがわかった時点でそれまでとは異なったポリシーに更新します。
たくさんの主要な機能をブロックする必要があり、普通のコンテナが全く使えないものになるためブロックできない exploit もあるでしょう。

非特権コンテナ

非特権コンテナは仕様上安全です。コンテナの uid 0 はコンテナ外では非特権ユーザにマッピングされます。そしてコンテナには自身が所有するリソースにのみ特権を持っています。

このようなコンテナでは、セキュリティ上の理由から SELinux, AppArmor, Seccomp, ケーパビリティを使う必要はありません。
LXC は今でも以上の技術を使っています。これは万が一のカーネルのセキュリティ上の問題に対処できるセキュリティの追加レイヤーを加えるためです。この追加レイヤーは以上の技術によってセキュリティモデルを強制されません。

非特権コンテナを動作させるため、LXC は 3 つの setuid コードと協調して動作します。

  • lxc-user-nic (ホスト上に veth ペアとブリッジを作るための setuid ヘルパー )
  • newuidmap (shadow パッケージに含まれます。uid のマッピングを設定します。)
  • newgidmap (shadow パッケージに含まれます。gid のマッピングを設定します。)

これ以外はあなた自身のユーザで動作するか、あなたのユーザが所有する uid で動作します。

この結果、これらのコンテナの持つほとんどのセキュリティ上の問題 (コンテナからの脱出、リソースの悪用、...) はランダムな非特権ユーザに対して働きます。そのため、LXC の問題というよりは一般的なカーネルの問題になるでしょう。

LXC 開発者は喜んでこのようなセキュリティ上の問題を追跡することを手伝います。そして可能な限り早く問題を解決するために Linux カーネルコミュニティと連絡を取ります。

DoS 攻撃の可能性

LXC はデフォルトでは DoS 攻撃を防ぐことはありません。複数の信頼できないコンテナが実行中であったり、信頼できないユーザに対してコンテナの実行を許可している場合、いくつかの事項を考慮し、適切に設定を更新しておく必要があります:

Cgroup による制限

LXC は cgroup による制限を親 cgroup から継承します。Linux ディストリビューションでは、実際には制限は設定されていません。その結果、コンテナ内のユーザが、fork bomb を実行したり、システムのメモリを全て使ったり、カーネルが out of memory になるまでネットワークインターフェースを作成したりすることで、簡単にホストに対して DoS 攻撃を実行できます。

これは、関係する lxc.cgroup エントリ (memory, cpu, pids) を設定したり、ログイン時に親となるユーザが適切に設定された cgroup 内に配置されるようにすることで軽減できます。

ユーザに対する制限

cgroup のように親の制限が継承されるので、非特権コンテナは親よりも高い値の ulimits を設定できません。

しかし、覚えておくべきことがひとつあります。ulimit は名前が表すように、カーネルレベルで uid と結びついています。これはグローバルのカーネル uid であり、ユーザ名前空間内の uid ではありません。

もし 2 つのコンテナが同一か重複する id のマッピングで、共通のカーネル uid を共有している場合、制限も共有するということを意味します。そして、あるコンテナ内のユーザは他のコンテナの同じユーザに DoS 攻撃を加えることができることを意味します。

これを防ぐために、信頼できないユーザやコンテナは完全に別の id マッピングを持つべきです (理想では、65536 個の uid と gid それぞれについて)。

ネットワークブリッジの共有

LXC はコンテナに対して基本的なレイヤー 2 の接続性を設定します。利便性のために、システムでひとつのデフォルトのブリッジも提供します。

ブリッジに接続するコンテナは、通信したいどんなレイヤー 2 のトラフィックでも送信できるので、ブリッジ上で MAC アドレスや IP アドレスのスプーフィングを行えます。

信頼できないコンテナが実行されていたり、信頼できないユーザにコンテナの実行を許可している場合、理想的には信頼できないコンテナのユーザもしくはグループごとにひとつブリッジを作成すべきです。そして、ユーザに対して割り当てられたブリッジだけを使えるように /etc/lxc/lxc-usernet を設定すべきです。

IPv6 ルータ広告の受け入れをセキュアにする

これに加えて、コンテナが IPv6 のルータ広告(Router Advertisement)を通して LXC ホストの IPv6 ルーティングテーブルを変更する可能性を考慮しておかなければなりません。これは、デフォルトの LXC のブリッジが IPv4 のみで構成されているからです。これは /proc/sys/net/ipv6/conf/default/accept_ra の値が lxcbr0 インターフェースに対しても適用されるということです。この値が 0 より大きな場合、LXC ホストはブリッジに接続されたコンテナからの(悪意がある可能性がある)ルータ広告を受け入れます。

これを防ぐため、/etc/default/lxc-net ファイルで LXC_IPV6_* 変数を設定することで、デフォルトブリッジ上での IPv6 アドレスを設定できます(これは /proc/sys/net/ipv6/conf/lxcbr0/forwarding を有効にします。このファイルの値が 1 の場合は、/proc/sys/net/ipv6/conf/lxcbr0/accept_ra を事実上無効化します。詳しくは https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt をご覧ください)。もしくは、lxcbr0 が作成された時に accept_ra が無効になるように、/proc/sys/net/ipv6/conf/default/accept_ra0 に設定することもできます。しかし、もし LXC ホストで IPv6 を使っており、外部ネットワークからのルータ広告に依存しているのであれば、外部との接続が失われるのを防ぐために、外部インターフェースで確実に accept_ra が有効になっているようにすべきです。

セキュリティ上の問題の報告

セキュリティ上の問題ができるだけ素早く同時に全ての Linux ディストリビューションで解決するように、問題は以下のどちらかの方法で報告してください:

私たちはセキュリティ上の問題を確認し、すべてのサポート中のリリースに対する修正を準備します。そして、テスト用にあなたにパッチを準備し、CVE 番号の割り当てを取得し、あなたと Linux ディストリビューションコミュニティに対して調整したリリース日を提供します。