Back to the news overview

LXC 3.1 リリースのお知らせ

2018/12/13

はじめに

LXC チームは LXC 3.1 のリリースをおしらせできることをうれしく思います!

これは、メジャーな LTS や LTS のバグフィックスのためのリリースではなく、それに向けての中間的な新機能を盛り込んだリリースです。将来的にはこの作業をさらに進める予定です。しかし、このリリースのサポートは限定的なものになります。これは、開発の大部分はプロダクション環境向けの LTS リリースにフォーカスしているためです。

新機能

AppArmor でのいくつかの再マウントオプションの有効化

読み書き可能なバインドマウントは MAC 制限をすり抜けるのを防ぐために、一部のパスで制限する必要があります。しかし、読み込み専用のバインドマウントではこのような問題はありません。さらに nosuidnodevnoexec フラグの組み合わせでも問題となることはなく、この組み合わせは新しいバージョンの systemd で必要になります。このような理由から、これらのオプションと ro,remount,bind との組み合わせではマウントを許可します。

ユーザースペースから setsockopt() 経由で使える、新しいソケットオプションである NETLINK_DUMP_STRICT_CHK を使って、ダンプリクエスト時にヘッダーと属性の厳密なチェックを要求できます。

ダンプリクエストに追加するヘッダーや属性のデータをもとにしたカーネル側のフィルタリングのようなダンプ機能を利用するには、ユーザースペースは NETLINK_DUMP_STRICT_CHK と 0 でない値で setsockopt() を呼ばなければなりません。これは LXC がネットワーク名前空間から情報を効率的に取得するのに使う IFA_TARGET_NETNSID の利用に必要です。

コンテナ起動時の新たなキーリング割り当て

ホストのキーリングを隔離して保護するために、LXC コンテナはそれぞれ、起動時に新しいキーリングを割り当てようとします。

完全な cgroup2 のサポート

LXC はしばらくの間、厳密な権限委譲モデルを守らずに cgroup2 をサポートしてきました。現在は cgroup2 をフルにサポートしました。

コンテナからネットワークデバイスとアドレスを取得する効率的な方法の実装

LXD チームが行ったカーネルでの作業によって、コストのかかる fork()setns() システムコールを実行する必要なく、ネットワーク名前空間を問い合わせることができるようになりました。代わりに、ネットワーク名前空間はネットワーク名前空間識別子を使って識別します。LXC は、このような新しいネットワーク名前空間が使えるバージョンと、getifaddrs() を非常に改良し安全になったバージョンである netns_getifaddrs() を使うようになりました。これは getifaddrs() の厳格なスーパーセットです。

API に lxc_has_api_extension() を追加

今後実装される新しい API が追加されるたびに、lxc_has_api_extension() に与えるユニークな名前が与えられます。これは LXD の API 拡張のチェックをモデルにしています。これにより、API ユーザーが特定の API 拡張がサポートされているかどうかを LXC インスタンスに問い合わせることができるようになります。

lxc.cgroup.relative 設定キーの追加

新たに設定項目として lxc.cgroup.relative を追加しました。この設定で LXC に root cgroup へエスケープしないように指示できます。これにより、ユーザーは cgroup2systemd が強制する制限を遵守するのが容易になります。具体的には、LXC コンテナを systemd サービスとして実行できます。

(コンテナ)起動時の新しいネットワーク名前空間識別子の割り当て

コンテナごとに、起動時に割り当てるユニークなネットワーク名前空間識別子を持つようになりました。LXC はこれを使い、ネットワーク名前空間上で実行する操作を大幅にスピードアップします。(ネットワークデバイスの設定や取得)。

lxc.rootfs.managed 設定キーの追加

LXC インスタンスがコンテナストレージを管理しているかどうかを示すために使える新しい設定キーを追加しました。LXC がストレージを管理していない場合、LXC はコンテナストレージ変更しません。例えば、API c->destroy(c) を呼び出すと、destroy フックが実行されますが、実際に rootfs は削除しません(もちろん、フックが LXC の背後でそのような処理を実行しない限り)。

すべての VLA の削除

LXC はデフォルトで -Wvla を付けてコンパイルされるようになりました。

AppArmor プロファイルの生成

この機能は lxd の AppArmor プロファイル生成機能のコピーです。この機能は、cgroup 名前空間、AppArmor 名前空間とスタッキングのサポートのような機能を検出しようとします。そして条件付きで、プロファイルの非特権コンテナ向けのパーツを持ちます。

これにより、次のように設定が変更されます:

  • lxc.apparmor.profile = generated
    'generated' という固定値を設定するとこの機能を使います。設定しない場合、次の設定で明示的に指定しない限りは、機能的な変更はありません
  • lxc.apparmor.allow_nesting
    これは真偽値を設定します。有効な場合、次のような変更が行われます: generated な AppArmor プロファイルが使われる場合、ネストしたコンテナを使うのに必要な変更が含まれます。通常のマウントポイントに加えて、lxcfs のオーバーレイなしで、/dev/.lxc/proc/dev/.lxc/sysprocfssysfs のマウントポイントに含まれます。generated な AppArmor プロファイルが使われている場合は、直接読み書きはできません
  • lxc.apparmor.raw
    プロファイルに加える、生の AppArmor プロファイル行のリストです。generated なプロファイルを使っているときのみ有効です

apparmor_parser のキャッシュを使うために、./configure オプションに --with-apparmor-cache-dir を追加します。

マウントを挿入する API の追加

この作業は、LizaTretyakova の学士論文の一部としてなされました。開発チームは、この素晴らしい仕事についてとてもハッピーですし、感謝もしています!

コンテナの実行中に、動的にマウントを操作できることは、ユーザーからの長年の要望で、LXD では長い間サポートされてきた機能でした。この機能は主に次のような使用例を可能にします:

  • 実行中のコンテナに対してマウントを挿入する
    この機能により、ユーザーはコンテナに動的にマウントを追加できます。例えば、ディスクスペースが足りなくなる前にコンテナに新たに専用のストレージデバイスを追加するようなことです。
  • デバイスのホットプラグが可能に
    コンテナに新しいデバイスを追加するためには、コンテナの実行中にマウントを操作することが必要です。具体的には、CAP_MKNOD のようなデバイスノードを作成できるケーパビリティが落とされているような特権コンテナや、非特権コンテナはデバイスを作成できません。このようなデバイスは、ホストの名前空間上でホスト上の十分な特権を持ったプロセスによって作成され、コンテナにマウントとして挿入されることが必要です。

この目的で、新たにふたつの API 呼び出しが追加されました:

int (*mount)(struct lxc_container *c, const char *source, const char *target,
                    const char *filesystemtype, unsigned long mountflags, const void *data,
                    struct lxc_mount *mnt);

int (*umount)(struct lxc_container *c, const char *target, unsigned long mountflags,
                      struct lxc_mount *mnt);

lxc.monitor.signal.pdeath の追加

lxc モニタープロセスが exit した際に、コンテナの init に送るシグナルを設定します。デフォルトでは SIGKILL が設定されています。つまり、lxc モニタープロセスが死んだ場合は、コンテナのプロセスすべてが kill されることになります。lxc モニタープロセスが死んでもコンテナを起動したままにしておきたい場合はこれを 0 にセットします。

liblxc の共有と静的ライブラリのビルド

デフォルトで、LXC では共有と静的の両方のライブラリがビルドされるようになりました。

Linux カーネル 4.18 で行われた mknod() の変更への適応

55956b59df33 ("vfs: Allow userns root to call mknod on owned filesystems.")

上のコミットで、Linux では、CAP_MKNOD が有効な場合は、ユーザー名前空間内の root による mknod() ができるようになりました。
しかし、これで作られたデバイスノードは実用になりません。なぜなら、

static struct super_block *alloc_super(struct file_system_type *type, int flags,
                                       struct user_namespace *user_ns)
{
        /* <snip> */

        if (s->s_user_ns != &init_user_ns)
                s->s_iflags |= SB_I_NODEV;

        /* <snip> */
}

が、ファイルシステムに SB_I_NODEVE を設定するからです。init のユーザー名前空間以外で作られたデバイスノードが open() されるとき、次の呼び出しにヒットします:

bool may_open_dev(const struct path *path)
{
        return !(path->mnt->mnt_flags & MNT_NODEV) &&
                !(path->mnt->mnt_sb->s_iflags & SB_I_NODEV);
}

これにより EPERM が発生します。なぜなら、デバイスノードは非 init のユーザー名前空間が所有するファイルシステム上に存在し、SB_I_NODEV により、デバイスノードへのアクセスが許可されないからです。LXC は、このケースを正しく扱うようになりました。

アプリケーションコンテナの実行に execveat() を使用

アプリケーションコンテナは、そのワークロードを実行するために、最小限の機能を持つ init システムに依存します。コンテナ内にバインドマウントしたファイルを開くことでアプリケーションコンテナを実行する代わりに、シンプルに execveat() にファイルディスクリプタを渡します。これにより、アプリケーションコンテナがより安全にシンプルに起動します。

ロギング時のスレッドごとのコンテナ名出力が可能に

異なるコンテナを実行しているが、単一のログファイルを共有しているスレッドはそれぞれ、コンテナ名をログに出力することで識別できるようになりました。

cgroup の扱いのリファクタリング

cgroup のコンストラクタ実装を、よりシンプルで、スレッドセーフなオンデマンドモデルの cgroup ドライバーの初期化モデルに置き換えました。
cgroup 初期化コードをコンストラクタ内で実行することは、共有ライブラリがマップされるたびに cgroup をパースするコードが走ることを意味します。これは不要なオーバーヘッドでした。よりクリーンな実装は、必要になったときにオンデマンドで cgroup ドライバーを割り当てることです。

フック実行時に ambient ケーパビリティを上げる

非常に制限されたコンテナ(例えば、単一の root 以外のユーザーにのみマッピングされて実行している非特権コンテナ)では、起動時に特権が必要な操作を実行できませんでした。
フックの実行時に ambient ケーパビリティを上げることで、exec を超えて特権を維持できます。

非特権コンテナ内で /sysrw でマウント可能に

LXD チームによる新しいカーネルへの取り組みにより、ユーザー名前空間内へ uevent を送ることができるようになりました。これは、コンテナ内で udev が実行できるようになったことを意味します。この前提条件は、/sysrw でマウントされていることです。そうでなければ、udev は起動を拒否します。

strlcpy()strlcat() を追加、strncpy()strncat() を廃止

strlcat()strlcpy() は常に有効な文字列を返し、適切に切り詰めのチェックができますので、文字列の扱いがより安全になりました。

コンパイラベースのハードニング

デフォルトで、LXC は次のようなコンパイラが持つ様々なハードニングのオプションを有効にします:

-Wimplicit-fallthrough
-Wcast-align
-Wstrict-prototypes
-fstack-clash-protection
-fstack-protector-strong
--mcet -fcf-protection
-Werror=implicit-function-declaration

スレッドセーフに関する改良

コードベースはマルチスレッド環境で使えるように、より強化されました。

seccomp: アーキテクチャースタッキングのサポート

例えば次のようなケースなどをサポートできるようになりました:

  • 32bit コンテナを実行する 64bit カーネルと 64bit ユーザースペース
  • 64bit コンテナを実行する 64bit カーネルと 32bit ユーザースペース
  • 64bit コンテナを実行する 32bit コンテナを実行する 64bit カーネルと 64bit ユーザースペース
  • ...

コンテナ内に uid 0 がないアプリケーションコンテナのサポート

コンテナ内の uid 0 に対するマッピングを持たないコンテナが起動できるようになりました。
サンプルの設定は次のようなものです:

lxc.include = /usr/share/lxc/config/common.conf
lxc.include = /usr/share/lxc/config/userns.conf
lxc.arch = linux64
lxc.rootfs.path = dir:/home/brauner/.local/share/lxc/c1/rootfs
lxc.uts.name = c1
lxc.net.0.type = empty
lxc.hook.mount =

# ホスト上の uid, gid 1000 のみがコンテナ内の uid, gid 1001 にマップされる
lxc.idmap = u 1001 1000 1
lxc.idmap = g 1001 1000 1

# コンテナ内の uid, gid を 1001 に切り替える
lxc.init.uid = 1001
lxc.init.gid = 1001

gid マウントオプションがないカーネル上での devpts マウントのサポート

古いカーネルは特定のグループへのアクセスを許可するための gid マウントオプションをサポートしません。LXC はこのケースを自動的に扱い、カーネルで gid マウントオプションがサポートされている場合のみ、gid = 5 を追加します。

バグ修正

LXC 3.1.0 は LXC 3.0.1、3.0.2、3.0.3 と同じバグ修正がすべて含まれています。

サポートとアップグレード

LXC 3.1 は LTS リリースではありません。したがって、LXC 3.2 がリリースされるまでのみサポートされます。強力なサポートのコミットが必要なユーザーは LTS リリースを使い続けることをおすすめします。

ダウンロード