Kubernetes securityContext:KubernetesにおけるLinux capabilities
2021年1月26日
0 分で読めますはるか昔、Unixオペレーティングシステムの権限モデルは比較的シンプルでした。通常のユーザーか、あらゆる操作を実行できる権限を持つスーパーユーザー、rootのどちらかでした。
通常のユーザーにファイルやディレクトリへの昇格した権限を与えることはできましたが、ほぼすべてのカーネルレベルの機能はrootユーザーに制限されていました。アプリケーションの動作にカーネルレベルの呼び出しが1つでも必要な場合、SUID権限を与える必要がありました。これは実質的に、通常のユーザーが起動した場合でも、そのプロセスにroot権限を付与することになります。
物理マシンを比較的少数のユーザーで利用していた時代には、この仕組みで十分に機能していました。しかし、この大まかな権限の区分は、現代の用途には適していませんでした。柔軟性とセキュリティを高めるため、Linuxカーネルの開発者は、カーネルレベルの呼び出しをケイパビリティにまとめ、個々のプロセスに割り当てられる、よりきめ細かな仕組みを考案しました。これにより、アプリケーションが必要とするカーネル呼び出しが1つだけなら、そのケイパビリティだけを割り当て、システムがさらされるセキュリティリスクを抑えられるようになりました。
Kubernetesコンテナの権限管理
では、コンテナではどのように機能するのでしょうか?
コンテナは、カーネルのcgroupと名前空間を使って分離された、システム上で実行されるプロセスにすぎません。そのため、ほかのプロセスと同じようにコンテナにもケイパビリティを割り当てられます。コンテナの作成時に、コンテナランタイムがこの処理を行います。
通常、コンテナランタイムはコンテナに既定のケイパビリティ一式を割り当て(Dockerが提供する既定のセットはこちらで確認できます)、ケイパビリティを追加または削除する仕組みも提供します。コンテナを--privilegedフラグ付きで実行すると、すべてのケイパビリティがコンテナに付与されます。コンテナをrootとして実行している場合、深刻なセキュリティ問題につながるおそれがあります。この点については、以前の記事で取り上げました。
KubernetesのsecurityContextでコンテナのケイパビリティを設定する
Kubernetesでコンテナランタイムを使用する際、Kubernetesのコントロールプレーンがこれらの制御機能を使い、コンテナの起動時に付与するケイパビリティを定義します。ケイパビリティの設定は、コンテナのYAMLにあるsecurityContextセクションの各種設定を通じて指定できます。設定例は次のとおりです。
この例では、すべてのケイパビリティを削除してから、CAP_NET_ADMINを追加します。securityContextのcapabilitiesセクションが空の場合、コンテナランタイムで定義された既定のケイパビリティ一式が適用されます。通常、このセットはかなり広範で、アプリケーションに必要な範囲を大きく超えている可能性があります。KubernetesでPodを起動し、どのケイパビリティが付与されるか確認してみましょう。
まず、PodをデプロイするためのYAMLを作成します。この設定では、Docker Hubのアップストリームイメージを使用します。Alpineベースのこのイメージには、コンテナが持つケイパビリティを確認できるcapshツールが追加されています。
このコンテナにはsecurityContextセクションを定義していないため、各設定にはシステムの既定値が適用されます。Kubernetesでユーザーを指定しない場合、コンテナはビルド元のDockerfileで指定された既定のユーザーとして実行されることを覚えておきましょう。多くのコンテナでは、これがrootです。
Podが起動したら、その中でシェルを開き、capshを実行してケイパビリティを確認できます。
KubernetesのsecurityContextでケイパビリティを削除する
このように、既定ではrootとして実行され、多くのケイパビリティが付与されています。これはコンテナランタイムで既定として定義されたセットです。次に、securityContextの設定でケイパビリティを1つ削除してみましょう。新しいファイルシステムノードを作成する機能、つまりCAP_MKNODを削除します。
最初のPodと比べると、このPodではcap_mknodケイパビリティが削除されていることがわかります。個別のケイパビリティだけでなく、securityContextですべてのケイパビリティを削除することもできます。
ケイパビリティがまったくないと、実行に失敗するシステム機能もあります。たとえば、apkを使ってbashをインストールしてみましょう。
コンテナはrootとして実行されていますが、bashパッケージのインストールに失敗します。ファイルシステムの権限設定が必要なのに、そのケイパビリティをコンテナから削除したためです。コンテナ内でのソフトウェアのインストールを禁止したい場合、ケイパビリティを使って権限を管理する方法が有効です。
capshを使うとコンテナが持つケイパビリティを見やすく表示できますが、確認方法はそれだけではありません。追加のソフトウェアをインストールせずに、procファイルシステムから直接情報を取得することもできます。
/proc/1/statusファイルでは、ケイパビリティがビットマップで表示されます。このコンテナではケイパビリティを有効にしていないため、すべて0です。すべてのケイパビリティが有効になっている元のコンテナで、同じファイルを確認すると次のようになります。
capshの出力ほど読みやすくはありませんが、ここに示された各ビットは、該当するカーネルのヘッダーで定義されている特定のケイパビリティを表しています。
コンテナの既定のケイパビリティを削除する方法について詳しく学び、Kubernetesのセキュリティを強化しましょう。
KubernetesのsecurityContextでケイパビリティを追加する
ケイパビリティは削除するだけでなく、再度追加することもできます。先ほどの例を使い、securityContextの設定にケイパビリティを1つ追加してみましょう。ここでもCAP_MKNODを追加します。
この例では、ケイパビリティが1つだけ有効になっていることがわかります。
KubernetesのsecurityContextにおける最小権限の原則
この記事では、コンテナにおけるケイパビリティの仕組みと、KubernetesのsecurityContextでの設定方法、そしてコンテナランタイムによる制御について説明しました。
最小権限の原則に従うなら、セキュリティのベストプラクティスは、コンテナに実際に必要なケイパビリティだけを付与することです。このテーマについては以前の記事で取り上げました。多くのプロセスでは、カーネルのケイパビリティが実際には必要ないことに驚くかもしれません。昇格した権限が必要な場合でも、その大半はファイルレベルの権限で制御できるためです。
まずsecurityContextですべてのケイパビリティを削除し、必要なものだけを追加していきましょう。SELinuxなどのツールの出力を確認すれば、どのケイパビリティが問題を引き起こしているかを特定し、失敗の原因を調べられます。また、別のユーザーを指定しない限り、Kubernetesのコンテナはrootとして実行される可能性があることにも注意が必要です。
KubernetesのsecurityContextでケイパビリティ設定を徹底する
ケイパビリティや非rootでの実行といったsecurityContextの設定を確実に適用するには、Kubernetesクラスターでアドミッションコントローラーを使い、適切なセキュリティ設定がないコンテナの起動を防ぐことができます。Kubernetesには、securityContextの設定を強制できるPodSecurityPolicyコントローラーが組み込まれています。
ただし、1.21リリースで非推奨になるため、Open Policy Agentなどの外部でメンテナンスされるプロジェクトが推奨されます。
こうしたセキュリティ設定を可視化し、修正する仕組みを開発プロセスに直接組み込むことも重要です。SnykはKubernetesのYAMLファイルをスキャンし、ケイパビリティなどに関する安全でないsecurityContextの設定を検出できるほか、開発者のワークフロー内で修正方法を提案します。この機能はSnyk CLIで利用でき、ソースコード管理システムや継続的インテグレーションにも直接統合できます。

この機能を試してみませんか?今すぐ無料アカウントに登録しましょう。
CTFを始めよう
オンデマンドのバーチャル入門ワークショップで、CTFチャレンジの解き方を学びましょう。



