理解しておきたいKubernetesのセキュリティコンテキスト設定10選
2021年3月10日
0 分で読めますKubernetesでワークロードを安全に実行するのは簡単ではありません。Kubernetes APIのセキュリティに影響を与える設定は数多くあり、正しく実装するには相応の知識が必要です。この領域でKubernetesが提供する強力なツールの一つが、すべてのPodおよびContainerマニフェストで利用できるsecurityContext設定です。このチートシートでは、さまざまなsecurityContext設定を取り上げ、それぞれの意味と使い方を解説します。

runAsNonRoot
runAsUser / runAsGroup
seLinuxOptions
seccompProfile
privileged / allowPrivilegeEscalation
capabilities
readonlyRootFilesystem
procMount
fsGroup / fsGroupChangePolicy
sysctls
PodとContainerの設定
KubernetesのsecurityContext設定は、PodSpec APIとContainerSpec APIの両方で定義されています。このドキュメントでは、各設定の適用範囲を示すため、[P]および/または[C]の注釈を付けています。なお、設定が両方の範囲で利用可能かつ構成されている場合は、Container側の設定が優先されます。
それでは、特定の順番にこだわらず、securityContext設定を見ていきましょう。
1. runAsNonRoot [P/C]
Containerはnamespaceとcgroupを使ってプロセスを制限できますが、デプロイ設定に一つでも誤りがあると、そのプロセスがホスト上のリソースにアクセスできてしまう可能性があります。プロセスがrootとして実行されている場合、ホストのrootアカウントと同じアクセス権がそれらのリソースに対して与えられます。さらに、procMountやcapabilitiesなど、制約を緩和するほかのPodまたはContainer設定を使用すると、root UIDによって、それらが悪用された場合のリスクがさらに高まります。よほどの理由がない限り、Containerをrootとして実行してはいけません。
では、デプロイするイメージがrootを使用している場合は、どうすればよいでしょうか?
方法1:ベースイメージに用意されたユーザーを使う
ベースイメージには、すでにユーザーが作成されていて利用できる状態になっていることがよくありますが、それを活用するかどうかは開発チームやデプロイチームに委ねられています。たとえば、公式のNode.jsイメージにはUID 1000のnodeというユーザーが含まれており、そのユーザーで実行できますが、Dockerfileでは現在のユーザーとして明示的に設定されていません。そのため、実行時にrunAsUser設定で構成するか、派生Dockerfileを使ってイメージ内の現在のユーザーを変更する必要があります。前者の場合、UID 1000がアプリケーションディレクトリ内のファイルを読み取れることが前提となります。ここでは、派生Dockerfileを使って独自のイメージをビルドする例を見てみましょう。
イメージのビルドについて詳しく掘り下げずに、ビルド済みのnpmアプリケーションがあるとします。以下は、[**node:slim**](https://hub.docker.com/_/node)をベースにイメージをビルドし、用意されているnodeユーザーで実行するための最小限のDockerfileです。
重要なのはUSERで始まる行です。この行によって、このイメージから起動するすべてのContainerでnodeがデフォルトユーザーになります。ユーザー名ではなくUIDを指定するのは、KubernetesがContainerの起動前にイメージのデフォルトユーザー名をUIDに対応付けられないためです。そのため、runAsNotRoot: trueを指定してデプロイするとエラーが返されます。
方法2:ベースイメージにユーザーが用意されていない場合
nodeのベースイメージに利用可能なユーザーが用意されていない場合はどうすればよいでしょうか?多くのプロセスでは、派生Dockerfileでユーザーを作成して使用すれば問題ありません。先ほどの例を拡張して、次のようにします。
ご覧のとおり、追加したのはユーザーを作成するRUN行だけです。構文はベースイメージのディストリビューションによって異なる場合があります。その後、それに合わせてユーザー名とパスの参照先を変更しています。
注:この方法はnode.jsやnpmでは問題なく機能しますが、ほかのツールではファイルシステム内の別の要素についても所有者の変更が必要になる場合があります。問題が発生した場合は、使用するツールのドキュメントを確認してください。
2. runAsUser / runAsGroup [P/C]
Containerイメージには、プロセスの実行に使用する特定のユーザーやグループが設定されている場合があります。これはrunAsUserおよびrunAsGroupの構成設定で上書きできます。これらは、同じ所有者IDを持つファイルが含まれたボリュームマウントと併せて設定されることがよくあります。
これらの設定を使う際には、元のイメージと互換性がない実行時の判断をContainerに対して行うことになるため、注意が必要です。たとえば、CI用の公式サーバーイメージjenkins/jenkinsは、jenkins:jenkinsというグループとユーザーで実行され、アプリケーションファイルはすべてそのユーザーが所有しています。別のユーザーを設定すると、そのユーザーがイメージの/etc/passwdファイルに存在しないため、起動に失敗します。仮に存在したとしても、jenkins:jenkinsが所有するファイルの読み書きに問題が発生する可能性が高いでしょう。簡単なdocker runコマンドで確認できます。
先ほど説明したように、Containerのプロセスをrootユーザーで実行しないようにすることは非常に重要です。ただし、これを保証するためにrunAsUserやrunAsGroup設定だけに頼らないでください。将来、誰かがこれらの設定を削除する可能性があります。必ずrunAsNonRootもtrueに設定してください。
3. seLinuxOptions [P/C]
SELinuxは、Linuxシステム上のアプリケーション、プロセス、ファイルへのアクセスを制御するポリシーベースのシステムです。LinuxカーネルにLinux Security Modulesフレームワークを実装しています。SELinuxはラベルの概念に基づいており、システム内のすべての要素にラベルを適用して、要素をグループ化します。これらのラベルはセキュリティコンテキストと呼ばれます。KubernetesのsecurityContextとは別のものです。セキュリティコンテキストはuser、role、type、および省略可能なlevelフィールドで構成され、形式はuser:role:type:levelです。
SELinuxはポリシーを使って、特定のコンテキストに属するプロセスが、システム内のほかのラベル付きオブジェクトにアクセスできるかどうかを定義します。SELinuxは厳格に適用でき、その場合はアクセスが拒否されます。また、アクセスをログに記録するだけの許容モードに設定することもできます。Containerでは通常、プロセスがイメージ内のファイルにのみアクセスできるように、ContainerのプロセスとContainerイメージにSELinuxラベルを付けます。
Containerの起動時には、ContainerランタイムによってデフォルトのSELinuxラベルが適用されます。securityContextのseLinuxOptions設定を使うと、カスタムSELinuxラベルを適用できます。ContainerのSELinuxラベルを変更すると、Container内のプロセスがContainerイメージから抜け出し、ホストのファイルシステムにアクセスできる可能性があるため注意してください。
この機能は、ホストOSがSELinuxをサポートしている場合にのみ適用されます。
4. seccompProfile [P/C]
Seccompはsecure computing modeの略で、ユーザー空間からカーネルに対して特定のプロセスが実行できる呼び出しを制限する、Linuxカーネルの機能です。seccompプロファイルは通常、システムコールの一覧と、これらのシステムコールが発生した場合に実行するデフォルトのアクションを定義したJSONです。
Kubernetesでは、securityContextのseccompProfile設定を通じてカスタムプロファイルを使用できます。
typeフィールドには、次の3つの値を指定できます。
Localhost:localhostProfile設定で、Container内のseccompプロファイルへのパスを指定します。Unconfined:プロファイルを適用しません。RuntimeDefault:Containerランタイムのデフォルトを使用します。typeが指定されていない場合のデフォルト値です。
これらの設定は、PodSecurityContextまたはsecurityContextのどちらにも適用できます。両方が設定されている場合は、ContainerレベルのsecurityContext設定が使用されます。なお、securityContext構成APIはKubernetes v1.19でリリースされました。それ以前のバージョンにデプロイする場合は構文が異なるため、詳細や例についてはKubernetesのドキュメントサイトを確認してください。
ほとんどのセキュリティ関連設定と同様に、ここでも最小権限の原則が適用されます。Containerには必要な権限だけを付与し、それ以上は与えないでください。まず、実行されたシステムコールを記録するだけのプロファイルを作成し、アプリケーションをテストして許可するシステムコールを決めます。この手順の詳細は、Kubernetesのチュートリアルで確認できます。
5. 特権Containerと権限昇格を避ける [C]
Containerに特権を付与するのは危険です。通常は、capabilitiesへのアクセスを許可すれば制御できる特定の権限を、より簡単に実現する方法として使われます。特権フラグの正確な実装はContainerランタイムによって異なりますが、実質的にContainerにすべての権限を付与し、デバイスcgroupコントローラーによる制限を解除します。また、Linux Security Moduleの設定を変更し、Container内のプロセスがContainerから抜け出せるようにする可能性もあります。
Containerはホスト上のプロセスを分離します。そのため、Containerをrootとして実行していても、ContainerランタイムがContainerに付与しないcapabilitiesがあります。特権フラグを設定すると、Containerランタイムはシステムのrootが持つすべてのcapabilitiesを付与します。これにより、基盤となるホストシステムに完全にアクセスできるため、セキュリティ上、非常に危険です。
特権フラグの使用は避けてください。Containerに追加のcapabilitiesが必要な場合は、capabilities設定を使って必要なものだけを追加します。特定のハードウェアへのアクセスやネットワークの再構成など、ホストのカーネルにおけるシステムレベルの設定を制御する必要があり、かつホストのファイルシステムへのアクセスも必要な場合を除き、特権フラグは必要ありません。
特権Containerについてさらに詳しく知りたい方は、Mattのブログ記事をご覧ください。特権付きDocker Containerは本当に必要?
6. Linuxカーネルのcapabilities [C]
capabilitiesはカーネルレベルの権限で、すべての処理をrootとして実行する方法よりもきめ細かく、カーネル呼び出しの権限を制御できます。ファイル権限の変更、ネットワークサブシステムの制御、システム全体の管理機能の実行などがcapabilitiesに含まれます。securityContextでは、Kubernetesの設定によってcapabilitiesを削除または追加できます。個々のcapability、またはカンマ区切りの一覧を文字列配列として指定できます。あるいは、-allという省略形で、すべてのcapabilitiesを追加または削除できます。この設定はContainerランタイムに渡され、Container作成時にcapabilityセットが構成されます。securityContextにcapabilitiesセクションがない場合、ContainerにはContainerランタイムが提供するデフォルトのcapabilityセットが与えられます。
推奨される方法は、すべてのcapabilitiesを削除したうえで、アプリケーションが実際に必要とするものだけを追加することです。通常の動作ではcapabilitiesを必要としないアプリケーションも多くあります。すべて削除してテストし、問題が発生した場合は監査ログを確認して、どのcapabilitiesがブロックされたかを調べてください。
securityContextで削除または追加するcapabilitiesを指定する際は、カーネルがcapabilitiesの名前に使用するCAP_プレフィックスを取り除いてください。デバッグにはcapshツールが便利です。Containerで有効になっているcapabilitiesを、人が読みやすい形式で表示でき、多くのディストリビューションで利用できます。ただし、攻撃者が有効なcapabilitiesを簡単に特定できてしまうため、本番のContainerでは利用可能な状態にしないでください。ビットマップを読むのが得意な方は、/proc/1/statusファイルで有効なcapabilitiesを確認することもできます。
Containerのデフォルトcapabilitiesを削除してKubernetesのセキュリティを向上させる方法をご覧ください。
7. 読み取り専用ファイルシステムで実行する [C]
コンテナが侵害され、ファイルシステムが読み書き可能な状態だと、攻撃者は設定を変更したり、ソフトウェアをインストールしたり、さらなる攻撃を仕掛けたりできます。ファイルシステムを読み取り専用にすることで、攻撃者が実行できる操作を制限し、このような権限昇格を防ぎやすくなります。一般に、コンテナがコンテナのファイルシステムに書き込む必要はありません。アプリケーションにステートフルなデータがある場合は、データベース、ボリューム、その他のサービスなど、外部の永続化手段を使用してください。また、すべてのログが標準出力(stdout)やログ転送先に書き込まれ、中央で集約できるようにしてください。
8. procMount [C]
デフォルトでは、コンテナランタイムはセキュリティ上の問題を防ぐため、コンテナ内から/procファイルシステムの一部をマスクします。しかし、特にクラスター内のビルドプロセスでよく使われるネストされたコンテナを使用する場合など、/procの該当部分へのアクセスが必要になることがあります。この項目で指定できる値は2つだけです。Defaultはコンテナランタイムの標準動作を維持し、Unmaskedは**/proc**ファイルシステムのマスクをすべて解除します。
言うまでもなく、この項目は十分に理解したうえで使用してください。イメージのビルドに使用している場合は、使用しているビルドツールの最新バージョンを確認してください。多くのツールでは、すでにこの設定が不要になっています。ツールに適したデフォルトのprocMountに戻せるよう、アップグレードしてください。
また、この設定がどうしても必要な場合でも、ネストされたコンテナに限って使用してください。ホストシステムの/procファイルシステムをコンテナに公開することは、絶対に避けてください。
9. fsGroup / fsGroupChangePolicy [P]
fsGroup設定では、Podがボリュームをマウントするときに、Kubernetesがそのボリューム内のすべてのファイルのアクセス権を変更するグループを指定します。この動作はfsGroupChangePolicyでも制御され、onRootMismatchまたはAlwaysを設定できます。onRootMismatchを設定すると、コンテナのルートとアクセス権が一致していない場合にのみ、アクセス権が変更されます。
fsGroupの使用には注意が必要です。ボリューム全体のグループ所有権を変更すると、ファイルシステムの処理速度が遅い場合やサイズが大きい場合に、Podの起動が遅れることがあります。また、同じボリュームを共有する他のプロセスが新しいGIDへのアクセス権を持たない場合、それらのプロセスに悪影響を及ぼす可能性があります。そのため、NFSなどの共有ファイルシステムを提供する一部のプロバイダーでは、この機能が実装されていません。また、これらの設定はエフェメラルボリュームには影響しません。
10. sysctls [P]
sysctlは、管理者がカーネル設定を変更できるLinuxカーネルの機能です。通常のLinuxオペレーティングシステムでは、/etc/sysctl.confで定義し、**sysctl**ユーティリティを使って変更することもできます。
securityContextのsysctls設定では、コンテナ内で特定のsysctlを変更できます。カーネルで名前空間化され、コンテナごとに変更できるオペレーティングシステムのsysctlは、ごく一部に限られます。この中には安全と見なされるものもあります。一方、他のPodに影響を及ぼす可能性に応じて、より多くのsysctlが危険と見なされます。通常、危険なsysctlはクラスターで無効になっており、使用するにはクラスター管理者による明示的な有効化が必要です。
基盤となるオペレーティングシステムを不安定にするおそれがあるため、特別な要件がない限り、sysctlsによるカーネルパラメーターの変更は避けてください。また、そのような変更はクラスター運用担当者と確認してください。
実行時のsecurityContextについて
多くの場合、ここで説明したセキュリティ設定は、ポリシーベースのアドミッション制御と組み合わせて、コンテナをクラスターに起動する前に必要な設定が適用されていることを確認します。securityContext設定とPodSecurityPolicyを組み合わせれば、特定のsecurityContext設定を必須にすることで、ポリシーに準拠したコンテナのみが起動されるようにできます。また、Dynamic Admission Controlや変更用Webhookを使用して、起動時にコンテナ設定へsecurityContext設定を追加することもできます。
まとめ
securityContext設定を使ってアプリケーションのデプロイを強化する際には、考慮すべき点が数多くあります。適切に使用すれば非常に効果的なツールです。このリストが、チームでワークロードや環境に適したオプションを選ぶ際に役立てば幸いです。Snykは、KubernetesのYAMLファイルをスキャンして一般的な設定ミスを検出し、こうした選択を支援します。下のボタンから無料アカウントに登録してください。
ソースコードの段階からインフラを保護
Snykはワークフロー内のIaCセキュリティとコンプライアンスを自動化し、構成ドリフトや不足しているリソースを検出します。