動作環境

必須の環境:

  • C ライブラリとして glibc、musl libc、uclib、bionic のいずれか
  • Linux カーネル 2.6.32 以上

lxc-attach の動作に必要な環境:

  • Linux カーネル 3.8 以上

非特権のコンテナが動作するのに必要な環境:

  • libpam-cgfs 非特権の cgroup 操作を行うためにシステムを設定する PAM モジュール
  • newuidmap、newgidmap を含む最新バージョンの shadow
  • Linux カーネル 3.12 以上

推奨ライブラリ:

  • libcap (ケーパビリティを落とすために必要)
  • libapparmor (コンテナに対して独自の apparmor プロファイルを設定するために必要)
  • libselinux (コンテナに対して独自の SELinux コンテキストを設定するために必要)
  • libseccomp (コンテナに対して seccomp ポリシーを設定するために必要)
  • libgnutls (色々なチェックサム確認に必要)
  • liblua (lua バインディングに必要)
  • python3-dev (python3 バインディングに必要)

インストール

通常はあなたがお使いのディストリビューションが、ディストリビューションのパッケージリポジトリもしくはバックポート用のチャンネル経由で、最新版の LXC を提供しているでしょう。

最初に LXC を使う場合は、LXC 4.0 の最新のバグフィックスのなされたバージョンのような、最新のサポート版リリースをお使いになることを推奨します。

Ubuntu を使っている場合、コンテナホストとして Ubuntu 18.04 LTS を使うことを推奨します。
LXC のバグフィックスリリースは、リリース後すぐに直接ディストリビューションのパッケージリポジトリ経由で利用可能で、パッチの当たっていないクリーンな最新版を提供します。

Ubuntu は、安全な非特権の LXC コンテナのために必要な全てをデフォルトで揃えている Linux ディストリビューションのいくつかのうちの 1 つです (Ubuntu 以外にもそのようなディストリビューションは存在します)。

Ubuntu では、LXC をインストールするのは次のように簡単です:

sudo apt-get install lxc

あなたのシステム上には、利用可能な LXC コマンドの全て、テンプレートの全て、LXC 処理のスクリプトに必要な python3 バインディングがインストールされるでしょう。

Linux カーネルに必要な機能を持っているかどうかをチェックするには次のコマンドを使います:

lxc-checkconfig

非特権コンテナの作成

非特権コンテナは最も安全なコンテナです。
非特権コンテナでは、コンテナで使う範囲の uid と gid を割り当てるために、uid と gid のマッピングを使います。
これはコンテナ内の uid 0 (root) が、コンテナの外では実際は uid が 100000 を持つというようになります。
それゆえ、万が一間違って問題のある操作を行ったり、攻撃者がコンテナを抜けだそうとしても、nobody ユーザと同じ程度の権限しか自身にないことがわかるでしょう。

残念ながら、このことは同時に以下のような操作が非特権コンテナでは許可されないことを意味します:

  • ほとんどのファイルシステムのマウント
  • デバイスノードの作成
  • マッピングが存在していない uid/gid に対する操作

このため、ほとんどのディストリビューションのコンテナテンプレートは動作しないでしょう。
代わりに、このような非特権の環境でも動くことを確認した、あらかじめビルド済みのディストリビューションのイメージを提供する "download" テンプレートを使う必要があります。

このあとの説明は、最新のカーネル、最新バージョンの shadow、libpam-cgfs、デフォルトの uid/gid 割り当てと言った、最新の Ubuntu や同等の Linux ディストリビューションを使用していると仮定して行います。

まず第一に、お使いの (非特権コンテナを使おうとする) ユーザが /etc/subuid と /etc/subgid で定義された uid/gid のマッピングを持っている必要があります。
Ubuntu では、デフォルトで 65536 個の uid と gid の割り当てが、システム上で全ての新規ユーザに与えられますので、Ubuntu をお使いの場合はすでにそのマッピングを持っているはずです。
もしマッピングがない場合は、usermod コマンドを使って割り当てる必要があります。

次に、非特権ユーザに与えるネットワークデバイスの範囲を設定するために使う /etc/lxc/lxc-usernet を設定します。
デフォルトでは、ホスト上で全くネットワークデバイスを割り当てできないことになっていますので、このファイルに以下のような設定を追加します:

echo "$(id -un) veth lxcbr0 10" | sudo tee -a /etc/lxc/lxc-usernet

これは、"your-username" にブリッジ lxcbr0 に接続する 10 個の veth デバイスの作成を許可するという意味です。

これが済むと、最後のステップは LXC の設定ファイルの作成です。

  • ~/.config/lxc ディレクトリがない場合は作成します。
  • /etc/lxc/default.conf を ~/.config/lxc/default.conf にコピーします。
  • 以下の 2 行をコピーしたファイルに追加します。
    • lxc.idmap = u 0 100000 65536
    • lxc.idmap = g 0 100000 65536

ここで設定した値は /etc/subuid と /etc/subgid にある値と一致している必要があり、標準の Ubuntu システムの初期ユーザのために存在が必要です。

mkdir -p ~/.config/lxc
cp /etc/lxc/default.conf ~/.config/lxc/default.conf
MS_UID="$(grep "$(id -un)" /etc/subuid  | cut -d : -f 2)"
ME_UID="$(grep "$(id -un)" /etc/subuid  | cut -d : -f 3)"
MS_GID="$(grep "$(id -un)" /etc/subgid  | cut -d : -f 2)"
ME_GID="$(grep "$(id -un)" /etc/subgid  | cut -d : -f 3)"
echo "lxc.idmap = u 0 $MS_UID $ME_UID" >> ~/.config/lxc/default.conf
echo "lxc.idmap = g 0 $MS_GID $ME_GID" >> ~/.config/lxc/default.conf

現時点の Ubuntu LTS 20.04 は次の追加の手順が必要です:

export DOWNLOAD_KEYSERVER="hkp://keyserver.ubuntu.com"

そして、最初のコンテナを作成しましょう:

systemd-run --unit=my-unit --user --scope -p "Delegate=yes" -- lxc-create -t download -n my-container

ダウンロードするテンプレートでは、選択できるディストリビューション、バージョン、アーキテクチャが表示されます。例えば、"ubuntu"、"focal"(20.04 LTS)、"amd64" のようなものです。

非特権ユーザーとして非特権コンテナを実行するには、事前に空の権限移譲された cgroup を割り当てる必要があります(これが必要な理由は cgroup2 のリーフノードと権限移譲モデルのためであり、liblxc で必要なわけではありません)。より詳細な情報は cgroups: cgroup2 のフルサポートをご覧ください。

ユーザーとしてシェルからコンテナを単純に起動し、自動的に cgroup を権限移譲することはできません。そのため、lxc-* コマンド群を呼び出すごとに、systemd-run コマンドでラップする必要があります。例えば、コンテナを起動するには、単に lxc-start my-container と実行する代わりに次のように実行します:

systemd-run --unit=my-unit --user --scope -p "Delegate=yes" -- lxc-start my-container

注意: もし、LXC をインストールする前に libpam-cgfs がホストマシン上にインストールされていない場合、最初のコンテナを作成する前にそのユーザが正しい cgroup に確実に所属しているようにする必要があります。これはログアウト・ログインするか、ホストマシンをリブートするとそのようになるでしょう。

実行したコンテナのステータスは以下のどちらかで確認できます:

lxc-info -n my-container
lxc-ls -f

コンテナ内でシェルを実行するには以下のようにします:

lxc-attach -n my-container

コンテナの停止は以下のように行います:

lxc-stop -n my-container

最後にコンテナを消去するには以下のようにします:

lxc-destroy -n my-container

root で非特権コンテナを作成する

システム全体で非特権コンテナを実行するには (これは root が非特権コンテナを実行するということです)、以下のような前述のステップの一部が必要なだけです。

具体的に言うと、root に対して割り当てる uid と gid の範囲を /etc/subuid と /etc/subgid に割り当てる必要があります。
そして、その範囲を先と同様に /etc/lxc/default.conf に lxc.idmap を使って設定します。

以上です。root はネットワークデバイスの範囲を設定する必要はありません。グローバルの設定ファイルの設定を使いますので、このステップは不要です。

このようにして作成したコンテナは非特権コンテナとして動作するでしょう。

特権コンテナ

特権コンテナは root が作成し、root が実行します。

ディストリビューションによっては、特権コンテナはケーパビリティをいくつか落としたり、apparmor プロファイルや、SELinux コンテキスト、seccomp ポリシーでプロテクトされているかもしれません。しかし、最終的にはプロセスは root 権限で実行されますので、信頼できないユーザに特権コンテナ内の root 権限を与えるべきではありません。

特権コンテナを作成する必要がある場合は、非常に簡単です。単純に前述のようなステップを踏むことなく、特権コンテナが作成されます。

sudo lxc-create -t download -n privileged-container

以上のコマンドで、ダウンロードテンプレートからのイメージを使って、システム上に新しい "privileged-container" という名前の特権コンテナが作成されるでしょう。

各ディストリビューションの LXC に関するドキュメント