前回は、root権限で起動するコンテナを使って多段にネストさせたコンテナを起動させたり、LXCコンテナ内でDockerを起動したりしました。
今回は、一般ユーザで起動する非特権コンテナでネストを試してみます。同時に、Ubuntu 16.04で採用された、一般ユーザに書き込み権限のあるcgroupを作るためのpam_cgfsも紹介します。
pam_cgfs
コンテナのネストを試す前に、一般ユーザ権限で起動する非特権コンテナで使用するpam_cgfsというPAMモジュールを紹介します。pam_cgfsはLXCFSに含まれています。
Ubuntuではこれまで、cgmanagerやパッチを適用したsystemd-logindを使って、ユーザ権限で操作できるcgroupを作成し、非特権コンテナを起動してきました。Ubuntu 16.04では、ログイン時にpam_cgfsというPAMモジュールを使って、ユーザ権限で操作できるcgroupを作成しています。
pam_cgfsは、/etc/pam.d
以下にある、いろいろなPAMの設定ファイルでincludeされているcommon-session
やcommon-session-noninteractive
で、以下のように定義されています。
オプションでユーザ向けのcgroupを作成するcgroupfsツリーを指定しています。16.04ではfreezer、memoryサブシステムと、systemd用のcgroupツリーにユーザ用のcgroupを作成し、initとしてsystemdを採用した非特権コンテナが起動できるように設定しています。
これでログイン時にpam_cgfsによりユーザ所有のcgroupが作成されますので、一般ユーザ権限で起動したコンテナからcgroupが利用できます。
非特権コンテナのネスト
LXCは一般ユーザによる特権を持たないコンテナでもネストをサポートしています。前回に試した特権コンテナと同じように、一般ユーザでネストしたコンテナを作成してみましょう。今回の実行例はUbuntu 16.04上で実行しています。
一般ユーザでコンテナを作成し、起動するための設定は、この連載の第17回をご参照ください。
ここでは、ネストさせるすべての層で、コンテナを非特権コンテナとして起動してみます。この記事の例では、一般ユーザとしてubuntu
ユーザを利用しています。
非特権コンテナをネストさせる場合のIDマッピング
非特権コンテナ内で非特権コンテナを動かす際には注意が必要です。非特権コンテナの場合、コンテナ内のuid,gidとホストのuid,gidをマッピングさせました。注意が必要なのはその際のマッピングの数です。
Ubuntuではユーザを追加すると、/etc/subuid
、/etc/subgid
に65536個のサブIDの定義を追加します。例えば次のようになっています。
これは通常システムを起動すると65536個のIDを使うためです。ホストOS上で上のような設定で非特権コンテナを起動すると、コンテナ(親コンテナ)内で65536個のuid,gidが利用できます。しかし、そのコンテナで65536個すべてのIDを消費してしまいますので、ネストして子コンテナを起動しようとすると、親コンテナから割り当てるIDがありません。
コンテナをネストさせて、非特権の「親コンテナ」内でさらに非特権の「子コンテナ」を起動させるには、ホストOS上では65536*2=131072個のサブIDを割り当て、コンテナでも同じように設定しておく必要があります。
ネストした非特権コンテナの作成と起動
それでは早速コンテナを作成し、起動していきましょう。今回はホスト上にコンテナを作成し、その子どものコンテナまで作成してみましょう。つまり先の例で示したように、ホスト上では、親コンテナ分、子コンテナ分合わせて65536*2=131072個のIDを確保しておく必要があります。まずはユーザに対して131072個のサブIDを使えるようにする設定を行います。
準備ができたらコンテナを作成します。ユーザ権限で起動した親コンテナですのでuser-parent
と名づけました。
作成できたら、上のようにネストするためにnesting.conf
のinclude設定と、131072個のIDのマッピングを設定します。
それでは、このコンテナを起動させてLXCをインストールしましょう。LXCをインストールする時はrootで作業する必要があります。
LXCのインストール後に、コンテナを作成するユーザに対して、サブIDを許可する設定をしておきましょう。
設定ができたら、その一般ユーザでログインします。
ここでuser-parent
コンテナ上で、一般ユーザでコンテナを操作する際には少し注意が必要です。一般ユーザ(ここではubuntu
ユーザ)になった後に、/sys/fs/cgroup
以下に存在するサブシステム用のディレクトリmemory
やfreezer
配下のツリーにユーザ権限のcgroupが作成されているかチェックしてください。
親コンテナにopenssh-server
をインストールしてubuntu
ユーザでsshログインするか、lxc-console
コマンドで親コンテナのコンソールにアクセスしubuntu
ユーザでログインすると、pam_cgfs経由でユーザが権限を持つcgroupが作られるので確実だと思います。筆者が試した所、lxc-attach
コマンドでコンテナに入り、su
コマンドでubuntu
ユーザにスイッチした場合はcgroupが作られずにコンテナの起動に失敗しました。
cgroupが存在していることが確認できたらコンテナを作成します。user-child
と名づけました。
上のように、コンテナ作成後に65536個のIDマッピングを設定します。これで子コンテナ"user-child"が起動するはずです。
無事起動しました。
ネストした非特権コンテナのcgroup
前回、特権コンテナでネストさせたときに確認したのと同様に、非特権コンテナでネストした場合にcgroupツリーがどうなるかを確認しておきましょう。
ホスト上からfreezerサブシステムの様子を見てみます。
ここで、ツリー中の(ユーザ名)/(数字)
はpam_cgfs
がユーザ用に作成したcgroupです。「数字」はログインセッションのIDです。
親コンテナからfreezerサブシステム以下を見ると、上の実行例のツリーで、user-parent
より下のツリーが見えていることがわかります。非特権コンテナの場合でも、cgroup namespaceが機能していることがわかります。
user-child
コンテナ内から見ると、普通にホスト上でcgroupfsがマウントされたときのようにcgroupのrootだけがある状態です。
まとめ
以上のように、今回は一般ユーザによる非特権コンテナをネストさせました。
今回紹介したネストでは、いずれのコンテナも一般ユーザ権限でネストさせました。しかし、一般ユーザ権限で動いているコンテナ内でさらにコンテナを起動させる場合は、今回のようなややこしい設定を行わずに、そのままroot権限で起動させても問題がないケースも多いでしょう。
なぜなら、ネスト元のコンテナが一般ユーザ権限で動いていれば、ネスト元のコンテナの親であるホストやコンテナ上では、そのネスト元のコンテナを実行しているユーザが持つ権限しかないからです。
今回紹介したような、ネストさせたコンテナを一般ユーザ権限で起動するのは、ネストしたコンテナの親のコンテナに対しても権限を制限したい場合です。このようにネストさせるケースはあまりないかもしれません。LXCではこのように柔軟に権限を設定できるのだということを頭の片隅にでも置いておいていただいて、必要になった際にこちらを参照いただければと思います。
その他に、ユーザ権限でのcgroupを作るための仕組みとしてpam_cgfsを紹介しました。pam_cgfsは今の所LXCFSに含まれていますが、LXCFSに密接に結びついているモジュールではありませんので、将来的にはLXCに含まれるようになるようです。
さて、前回の記事を書いてから今回の記事を書くまでの間に、LXCの新しいバージョンである2.1が公開されました。2.1では表面的には大きな機能の追加はないものの、設定項目がかなり変化していたり、細かい改良が行われていたりします。変更点については今後この連載で紹介したいと思います。