Red Hat で OpenShift のコンサルタントをしている橋本です。
今回は Software Design 2021 年 4 月号に掲載予定だったものの、紙面の都合でカットしたトピックをご紹介します。 もともとは Fedora CoreOS とセキュリティをテーマに、ホスト OS や OKD におけるセキュリティの機能を紹介する予定でしたが、最終的に 20 ページになり、7 ページまで減らす必要があったため、ここでは次のテーマを掲載します。
- NIST SP 800-190 におけるセキュリティの範囲は?
- Multus CNI プラグインにどれを選ぶ?
- Linux カーネルを用いるコンテナ技術
- rpm-ostree による OS のバージョン管理
- Ignition によるシステムの初期化
NIST SP 800-190 におけるセキュリティの範囲は?
具体的にコンテナやコンテナ基盤に関するセキュリティとはどの様な分類でリスクを評価するのでしょうか。 例えば NIST SP 800-190 *1 ではコンテナやコンテナ基盤に対する汎用的なセキュリティ基準を定義しており、コンテナ技術における主要なコンポーネントに対するリスクを次のように分類します。*2
- イメージに関するリスク: イメージの脆弱性、Dockerfile やイメージ設定の不備、マルウェアや認証情報の埋め込み、信頼されないイメージの利用
- レジストリに関するリスク: セキュアでない通信の利用、利用していないイメージの脆弱性、認証や認可の不備
- オーケストレーターに関するリスク: マルチテナンシーと権限分離の不備、オーケーストレーション自身の認証、オーバーレイによるPod 間通信、機密性のレベルによる分離、Control Plane と Worker Node の信頼性
- コンテナに関するリスク: コンテナランタイムの脆弱性、コンテナからホスト OS へのアクセス、コンテナランタイムの設定不備、アプリケーションの脆弱性、不正なコンテナの起動
- ホスト OS に関するリスク: 攻撃対象となるサービスの公開、カーネルの共有、ホスト OS の脆弱性、不要なユーザ権限の付与、ファイルシステムの改ざん
この様にして、主要なコンポーネントに焦点をあてることで、コンテナ技術、ホスト OS、オーケストレーター、またはパブリックまたはプライベートクラウドなどプラットフォームに依存せず、汎用的に適用される規格を目的としています。そのため、例えば、開発者の検証環境、管理者のシステム、ホストハードウェアと仮想マシンといった間接的に関与するコンポーネントに関するリスクは含まれません。 次にコンテナに関するリスク想定する場合、コンテナランタイムの脆弱性による権限昇格や権限の適用に対する設定の不備などに加えて、マルウェアや設定の不備による認証情報の埋め込み、ソフトウェア提供ベンダーやコミュニティ以外からの信頼されないイメージの利用などコンテナイメージにおける関するリスクが考えられます。
そこで NIST SP 800-154 *3 におけるデータを中心としたシステム脅威モデルにより項目が評価され、保護すべきデータにイメージとコンテナが該当します。前者はアプリケーションのプロパティファイルやデータを含むファイルが、後者はメモリ、ストレージや NIC などホストで共有されるリソースが該当します。
Multus CNI プラグインにどれを選ぶ?
Kubernetes ではネットワークは単一であるため、Control Plane と Worker Node 間やアプリケーションをデプロイする Pod 間の通信は全て同じ仮想 NIC を経由します。ネットワークを追加する、つまり Pod に NIC を追加する場合、Multus CNI プラグインを利用します。*4 ただし複数の CNI プラグインがあるため、どれを選ぶべきかが悩みどころになります。
まず CNI ネットワークプラグインは Main、IPAM、Meta の 3 種類に分類されます。*5
- Main: NIC を作成し Pod に NIC を割り当てる
- IPAM: Main プラグインに対して IP アドレスを管理する
- Meta: Bandwidth、Tuning や Flannel など機能がプラグイン毎に異なる
次に主要な IPAM プラグインと Main プラグインの概要は次の通りになります。
IPAM プラグイン
Plugin | Description |
---|---|
host-local | * 同じホスト上だけを対象に IP アドレスを割り当てる * IP アドレスは各ホストのローカルで管理する |
dhcp | * IP アドレスの管理に外部の DHCP サーバを利用する |
whereabouts | * クラスタを対象に動的に IP アドレスを割り当てる * IP アドレス を Custom Resource で管理する |
static | * 静的に IP アドレスを割り当てる |
Main プラグイン
Plugin | Description |
---|---|
bridge | * 同じホスト上の Pod を仮想ブリッジ経由で通信させる * ホスト外部のネットワークに通信できない |
host-device | * 同じホスト上の Pod をホストの NIC 経由で通信させる * ホスト外部のネットワークに通信できる |
ipvlan | * 同じまたは異なるホスト上の Pod をホストの NIC (sub-interface) 経由で通信させる * Pod にホストの NIC と同じ MAC アドレスが割り当てられるため、MAC アドレスが制限される環境で選択する *6 *7 |
macvlan | * 同じまたは異なるホスト上の Pod をホストの NIC (sub-interface) 経由で通信させる * Pod に異なる MAC アドレスが割り当てられるため、MAC アドレスが制限されない環境で選択する |
上記の通り Main と IPAM プラグインは目的や制約が異なるため、ユースケースに応じたプラグインの選択とプラグインの組み合わせを検討する必要があります。 Main と IPAM プラグインにおいて、推奨される組み合わせの一例は次の様になります。*8
- bridge + host-local bridge: 同じホスト上の Pod 同士を通信させることを目的とするため、IP アドレスもローカルで管理する host-local IPAM プラグインを利用する
- host-device + dhcp: 同じホスト上の Pod を対象にホストの NIC 経由で通信させるが、ホストのネットワークを利用するため dhcp IPAM プラグインを利用する
- ipvlan + whereabouts: ipvlan の sub-interface は ホストの NIC と同じ MAC アドレスを共有し、MAC アドレスに依存する dhcp IPAM プラグインの利用は制限されるため、whereabouts IPAM プラグインを利用する *9
- macvlan + static: macvlan の sub-interface はホストの NIC とは異なる MAC アドレスが割り当てられるため、 dhcp や static IPAM プラグインを利用する
Linux カーネルを用いるコンテナ技術
Linux コンテナ は Namespaces、SELinux、cGroups (Control Groups)、Capabilities といった Linux カーネルを利用するため、基本的なセキュリティの考えは Linux カーネルに依存します。*10 *11
Linux Namespaces は Linux カーネルが扱うマウント、PID、ネットワークなどシステムリソースを分離します。アプリケーションをサンドボックス化するために Namespaces (名前空間) にを用いてホスト上で個々のコンテナを分離し、コンテナ毎に独立してリソースを分配、分割、制限させます。 *12
SELinux はポリシーベースのルールとラベルを用いて、コンテナ(プロセス) にラベルをセットすることで、実行中のコンテナがアクセスできるシステムリソースを他のコンテナやホストから隔離します。*13 これは Namespace 単体ではプロセスがコンテナ外部にアクセスすることを制御できず、別の隔離レベルを利用する必要があるためで、SELinux を有効にすることで新たに隔離する層を設けます。*14 特に SELinux はファイルシステムに対して有効性が挙げられた例が多く、privileged 権限に対する脆弱性 *15 や runc の脆弱性 *16 が挙げられます。
cGroups はプロセス (コンテナ) に対する CPU、メモリ、ネットワーク帯域幅 や ディスク I/O などリソースの割り当てや優先度を管理します。例えば Dos や DDos などの攻撃によりあるコンテナがシステムリソースを大量に消費して、他のコンテナやホストのリソースが枯渇することを防ぎます。
Capabilities はコンテナに割り当てる権限を管理します。Linux では UID 0 を持つ Root ユーザは Capabilities と呼ばれる複数のグループに分割した権限から構成され、Capabilities は個別に有効、または無効にすることができます。例えば Capabilities の 1 つに Well-known ポートをバインドさせる CAP_NET_BIND_SERVICE
がありますが、コンテナを起動する上で一般的に必要なく、かつ不要な権限をコンテナに割り当てることは Attack Surface を増やすため、必要最低限な Capabilities に制限されています。*17
rpm-ostree による OS のバージョン管理
rpm-ostree はソースコード管理における Git やコンテナのイメージレイヤーの様にファイルシステムをブランチ (ファイルシステムツリー)として管理します。バージョンのロールバックやアップグレードといった一連のファイルシステムに要求する変更に対し、Atomic Commit *18 を前提に単一のオペレーションで完結させます。 *19 またパッケージ管理において、FCOS は YUM や DNF はインストールされておらず、代わりに rpm-ostree ツールが提供され、rpm-ostree コマンドでパッケージをインストールするとコンテナイメージをビルドするようにファイルシステムに対して変更がコミットされ、レイヤー毎に変更がバージョンとして管理されます。*20 *21 これは rpm-ostree は OSTree *22 と libdnf *23 ライブラリから構成され、RPM や dpkg など従来のパッケージマネージャとは依存関係を解決するためのメタデータやスクリプトを用いたファイルシステムツリーが異なるためです。
なお OKD では、Kernel パラメタの設定を変更するなど /etc 配下に変更を加える場合、ホストにログインして手動で変更するのではなく、Machine Config Operator (MCO) を用いて自動化しています。Control Plane 上で中央管理される Machine Config オブジェクトに設定を定義し、MCO が 各 Worker Node に設定を配布します。*24 なお Configuration Drifft の観点から推奨はされませんが、ホスト上で設定を直接変更した場合、ホストへの変更は永続化されるため、OS の再起動で変更が切り戻されはしません。
Ignition によるシステムの初期化
Ignition は Anaconda Kicksart *25 による OS インストールの自動化や cloud-init *26 による OS 起動後の設定を追加するツールと似ていますが、システムの初期化を目的としている点が決定的に異なります。一例として Ignition は次の処理を実行させることができ、これは初期 RAM ディスクを利用するため 、ディスクのパーティションや物理ホストに対するスクラッチでの OS のインストールといったシステムがブートする直後に関する処理を管理します。*27
- ディスクのパーティション
- ディスクのフォーマット
- Systemd Unit の定義
- 各種設定ファイルの書き込みやディレクトリの作成
- ユーザの作成
なお Ignition Config の形式は JSON であり、またディスクパーティションや Systemd の設定など多岐に渡るため、Ignition Config を手動で管理することは煩雑です。そのため FCOS では Ignition Config と互換性のある YAML 形式の Fedora CoreOS Configuration (FCC) とコンバート用のツールである Fedora CoreOS Configuration Transpiler (FCCT) が提供され、FCCT を用いて YAML 形式の FCC ファイルを JSON 形式の Ignition Config として生成します。*28 例えば OKD をインストールする際、インストーラー (openshift-installer) が Ignition Config を生成するため、Ignition Config を手動で作成したり、FCCT で YAML から Ignition Config を生成することは基本的にありません。ただし設定の変更は可能でインストール前後で変更可能な設定やオペレーションを次のように Day 1 と Day 2 として定義しています。 *29
- Day 1: ディスクの暗号化、カーネルの引数やカーネルモジュールの有効化など OS インストール時に適用されるオペレーション
- Day 2: Systemd Unit の変更などクラスタが構築された後に適用されるオペレーション
いかがでしたでしょうか、紙面から除外した議題を掲載しているため、網羅性よりもかなり特定の議題にフォーカスしています。 OpenShift のセキュリティに関して興味がある方は Software Design 2021 年 4 月号をお待ち下さい。
*1:最終版は 2017 年 9 月 25 日に起草され、その後 2020 年 9 月 4 日に IPA より日本語版も公開されています。https://csrc.nist.gov/publications/detail/sp/800-190/final
*2:図は https://csrc.nist.gov/publications/detail/sp/800-190/final より抜粋しています。
*3:https://csrc.nist.gov/publications/detail/sp/800-154/draft
*4:Multus CNI プラグインのアーキテクチャや各プラグインの概要を解説しています。https://www.openshift.com/blog/demystifying-multus
*5:https://www.cni.dev/plugins/
*6:ポートセキュリティの様にスイッチの物理ポート毎に MAC アドレスの最大数が制限されているケースが該当します。
*7:例えばパブリッククラウドを利用する場合、AWS では Promiscuous Mode が許可されないため macvlan を利用することはできず、ipvlan が推奨されます。https://access.redhat.com/solutions/5508011
*8:サポートされる機能の制約ではないため、詳細は各プラグインの説明を参照して下さい。https://docs.okd.io/latest/networking/multiple_networks/understanding-multiple-networks.html
*9:DHCP Client ID だけに対応し dhcp IPAM プラグインの利用はサポートされていません。https://www.cni.dev/plugins/main/ipvlan/
*10:Linux コンテナを構成する技術の概要をこちらで公開しています。https://access.redhat.com/ja/articles/1459113
*11:コンテナに求められるセキュリティの概要をこちらで公開しています。https://www.redhat.com/ja/resources/container-security-openshift-cloud-devops-whitepaper
*12:各コンテナに独自の Init (PID 0)を持たせるために ホストや他のコンテナから PID を分離させる PID Namespace 、ルーティングテーブルや FW を分離し、仮想 NW スタックを持たせる Network Namespace が該当します。
*13:初学者向けに分かりやすくSELinux の概念を漫画で解説しています。 https://opensource.com/business/13/11/selinux-policy-guide
*14:Kubernetes で利用できる Linux カーネルのセキュリティモジュールに AppArmor が該当しますが、SELinux は AppArmour よりも複雑ではあるものの、AppArmour は Multi-Category Security (MCS) をサポートしていないことから、コンテナの権限を分離することができず、Pod 間の隔離に対応しないことなどが挙げられます。https://www.redhat.com/sysadmin/apparmor-selinux-isolation
*15:コンテナにファイルディスクリプタにアクセスする権限を与えても、コンテナが起動するホストにアクセスすることを防ぎます。https://www.redhat.com/en/blog/selinux-mitigates-container-vulnerability
*16:コンテナエスケープによりコンテナがホストに任意のコマンドを実行させることを防ぎます。https://www.redhat.com/en/blog/latest-container-exploit-runc-can-be-blocked-selinux
*17:Linux Capabilities の詳細と SCC を用いた Capabilities の適用方法を解説しています。https://www.openshift.com/blog/linux-capabilities-in-openshift
*18:https://en.wikipedia.org/wiki/Atomic_commit
*19:内部的には切り替えに chroot を用いています。https://ostreedev.github.io/ostree/adapting-existing/
*20:パッケージをインストールする毎にレイヤーが作成されるため、複数のパッケージを指定することで単一のレイヤーで完結させることができます。https://docs.fedoraproject.org/en-US/iot/add-layered/
*21:パッケージ管理の仕組みは、本誌 2020年7月号 “月間Fedoraジャーナル Fedora系ディストリビューションにおけるパッケージ管理の変遷” で解説しています。https://gihyo.jp/magazine/SD/archive/2020/202007
*22:以前の名称は OSTree で、現在は Libostree プロジェクトです。https://ostreedev.github.io/ostree/
*23:DNF のバックエンドとしてハイレベルな API を提供するために利用されています。
*24:https://docs.okd.io/latest/post_installation_configuration/machine-configuration-tasks.html
*25:https://anaconda-installer.readthedocs.io/en/latest/kickstart.html
*26:https://cloudinit.readthedocs.io/en/latest/#
*27:https://coreos.com/ignition/docs/latest/examples.html
*28:https://docs.fedoraproject.org/en-US/fedora-coreos/fcct-config/
*29:https://docs.okd.io/latest/installing/install_config/installing-customizing.html