本文の内容は、2022年11月10日にJason Umikerが投稿したブログ(https://sysdig.com/blog/multi-tenant-isolation-boundaries-kubernetes/)を元に日本語に翻訳・再構成した内容となっております。
Kubernetes を実行する利点の多くは、組織内の複数のサービスやチーム間でクラスター、つまりクラスターが管理する基礎的な計算およびネットワーク・リソースを共有することで得られる効率性から生まれます。クラスターを共有するこれらの主要なサービスやチームはそれぞれクラスターのテナントであり、したがってこのアプローチはマルチテナンシーと呼ばれます。
課題は、Kubernetes のセキュリティ課題のほとんどが、クラスター (その制御プレーンと API、基盤となるノードと Linux カーネル、およびネットワーク) をこれらの複数のテナント間で共有することに起因しているということです。この記事では、Kubernetesのマルチテナンシーにおけるセキュリティの課題、特にデフォルト設定の問題点、およびそれらのリスクを軽減するために環境を堅牢化する方法について説明します。
Kubernetesにおける3つのマルチテナント分離境界
前述の通り、Kubernetesに関しては3種類の分離境界を考える必要があります。
- Kubernetes Control PlaneとAPI – あるテナントはKubernetesネームスペースとロールベースアクセスコントロール(RBAC)により別のテナントから隔離されます。
- ホスト – あるテナントのワークロードがコンテナによって別のテナントから分離され、そのためにコンテナが活用するLinuxネームスペースとcgroup機能が、Kubernetesによってクラスターの全ノードにわたってオーケストレーションされる場所です。
- ネットワーク – テナントのワークロードがKubernetes NetworkPoliciesによって別のテナントから分離され、クラスターの全Nodeにわたって各ホストのファイアウォールがオーケストレーションの基礎となる管理がなされます。
Kubernetesを全体的に保護する方法を理解するためには、これらの分離境界がすべてどのように機能するかを理解することが重要なだけでなく、マルチテナンシーのための保護に固有のさまざまな課題を、それぞれがどれに関連しているかによって枠組みづけることが最も簡単です。
では、それぞれの境界とそれに関連する課題を詳しく見ていきましょう。
境界その1 – KubernetesのネームスペースとRBAC
Kubernetes CLIのkubectl
を使用したり、別の方法でAPIを呼び出したりすると、そのリクエストはKubernetesのロールベースアクセスコントロール(RBAC)により承認されます。それは、順番に以下のような仕組みになっています。- Kubernetesはネームスペースと呼ばれる論理的な境界/仮想クラスターに分割されています。
- 各ユーザーやグループはRoleやClusterRoleにマッピングされ、Roleはネームスペースへのアクセス、ClusterRoleはクラスター全体へのアクセスを提供します。
- ロールとクラスタロールの両方は、ネームスペースやクラスター内で許可するルールによってさらに制限することができます。これらは以下の要素で構成されています。
つまり、各サービスやチームにはネームスペースを与え、そのネームスペース内ではClusterRoleではなくRoleのみを持たせることで、Kubernetesの最小特権を実現することができます。
Namespace team1にadminというRoleを定義する最もシンプルな例は、ルールにワイルドカード/スターを使用して次のようになります。
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: admin namespace: team1 rules: apiGroups: - '*' resources: - '*' verbs: - '*'
このRoleは、team1ネームスペース内でしかできないように、マッピングされた人を制限します。簡単そうでしょう?問題は、Kubernetes RBACで最小特権を実現するのは少し面倒だということです。なぜなら、簡単なワイルドカードを越えて移りましょうということを、Denyの恩恵を受けずにそれぞれ明示的に宣言する必要があるからです。このRoleにマッピングしているUserができるようになることとしては、以下のようなものがあります。
- このネームスペースに他のロールやロールマッピングを作成する。
- そのネームスペースをカバーするNetworkPolicies(ファイアウォールルールなど)の作成と変更。
- ネームスペース内のすべてのアプリケーションのランタイムシークレットにアクセスする。
- ネームスペースとその中のすべてのものを削除する。
- などなどです。
* を超えて移動するために必要なことの良い例は、built-in admin ClusterRole です。これは、名前空間内でほぼ完全な管理アクセスを許可する例ですが、ワイルドカードを使用する代わりに明示的に許可します。 これはコマンド
kubectl get clusterrole admin -o yaml
で取得できます。私の場合、324 行の YAML が出力されます。 ただし、許可したくないリソース/verbsを削除して、最小限の特権ロールにスリム化することは良い出発点です。Roles/ClusterRolesを作成するときに注意すべきもうひとつの点は、Verbsにワイルドカードを指定したときに、少なくとも3つの不要なもの、つまり escalate, bind 、および、 impersonateが含まれるということです。
- Escalateは、あなたのロールよりも高い権限を持つロールを作成することを可能にします。
- Bindは、あなたのユーザーを、あなたのロールよりも高いパーミッションを持つロールにバインドすることを可能にします。
- Impersonateでは、他のRoleにスワップすることができます(Linuxのsuコマンドに似ています)。
built-in admin ClusterRoleは、ワイルドカードを使用するのではなく、すべてのverbsを明示的に指定することで、ほとんどの場合これらを回避していることに注意してください。
最後に、Kubernetesのすべてが「ネームスペース」できるわけではありません。kubectl api-resourcesコマンドを実行することで、どのようなものが可能で、どのようなものができないかを確認することができます。もしユーザーがネームスペースでないものと定期的にやりとりする必要があり、それをマネージド/コントロールパイプラインなどにオフロードできない場合、ClusterRoleが必要になります – 最小特権を実現するのはかなり難しくなります。したがって、これはそのユーザー/チームに独自のシングルテナントクラスターを与えるきっかけとなることの一つです。
kubernetes APIリソースのコンソール出力
Kuberntesのコントロールプレーン分離境界の問題をFalcoで検出する
最小特権によるKubernetesアクセスの構成(ネームスペースやワイルドカードでないRolesなどの活用)に加えて、Kubernetesの監査証跡を監視して疑わしいアクティビティも監視する必要があります。Falcoは、SysdigがCNCFにコントリビュートしたオープンソースプロジェクトで、Kubernetesの監査証跡、Linuxカーネルのシステムコール、さらにはクラウドプロバイダーの監査証跡(AWS CloudTrailなど)といった実行時情報のストリームを監視し、リアルタイムでイベントとしてユーザーに表示される実行時の脅威に焦点を置いています。Falcoによるランタイムイベント監視のイメージ図
Falcoは、sidekickという別の関連サービスを持っています:
- お客様の環境内のすべてのFalcoからのイベントを1つの場所に集約します。
- あなたが望むすべての場所(あなたのSIEM、Slack/Teams、AlertManager/PagerDutyなど)にそれらをファンアウトします。
- イベントを視覚化するための基本的なUIがあります(FalcoからのJSONログ出力を見る必要はありません)。
Falcoをインストールすることで、Kubernetes Audit Trailをデプロイと一緒に見ることができ、Helmチャートを通してどんな疑わしい活動を探すかについての素晴らしいスタータールールのセットがここにあります。
マネージドKubernetesの場合、コントロールプレーン上の監査ログに直接アクセスすることはできませんが、クラウドプロバイダーのロギングシステムからFalcoにイベントを転送するプラグインがあります。ここでは、EKS、AKS、GKE用のプラグインを紹介します(これらはSysdigエージェント用と書かれていますが、オープンソースのFalcoでも同様に動作するはずであることに注意してください)。
Helmチャートに含まれるデフォルトのルールがイベントとしてアラートするアクティビティの例をいくつか挙げます。
- 非ServiceAccount(つまり、アプリケーションではなく管理者)からシークレットを取得すること
- 機密性の高いホストディレクトリ(例:/proc)からボリュームをマウントするPod
- 安全でないオプション(HostPID、HostNetworkなど)で起動されるPod
- Roles、ClusterRoles、ServiceAccountsなどの作成または変更。
- などなど。
境界その2 – ホストの分離 – コンテナ、Linuxのネームスペースとcgroups
Kubernetesはコンテナをオーケストレーションし、そのコンテナは各ホスト/ノード内の分離のためにこれらのLinuxの機能を使用します。- ネームスペース(Kubernetesのネームスペースと混同しないでください)は、あるプロセスセットにはあるリソースセットが見え、別のプロセスセットには別のリソースセットが見えるように、カーネルリソースをパーティション化します。
- cgroups(コントロール グループの略) は、プロセスのコレクションのリソース使用量 (CPU、メモリ、ディスク I/O、ネットワークなど) を制限し、構成し、分離します。
各Kubernetes Podは独自のLinuxネームスペースにプロビジョニングされ、他のPodやホスト上で実行されているものなど、そのネームスペースの外側で何が起こっているかを見ることができなくなる。しかし、複数のコンテナが動作しているPod(サイドカーコンテナなど)は、すべて同じLinuxネームスペースとネットワークを共有しており、基本的に同じセキュリティ境界/コンテキストを共有しています。
cgroupsについては、リソースの使用を予約および/または制限するためのものです。これは、マルチテナントの場合に重要です。あるテナントが「うるさい隣人」になって、同じノードにスケジュールされている他のテナントを混乱させないようにする必要があります。これは、ここで説明したように、Kubernetesのリソース管理アプローチの一部として活用されています。
KubernetesのHost/Node分離境界の主な問題は、PodSpecで以下のようなパラメータを使って、デフォルトでそれらを損なうようにKubernetesに依頼できることです。
- hostPID Linuxのネームスペースに入らないように要求する(アイソレーションを解除する)
- hostNetwork ホストのネットワークを使用するように要求する(アイソレーションを解除する)
- hostPath ホスト上の任意のパスをマウントすることをPodに許可するVolumes
- NET_ADMIN (ホストのファイアウォールを管理できる)などのLinux機能
- などです。(もっとあります!)
解決策案
Open Policy Agent (OPA) Gatekeeper
以前は、Pod Security Policies (PSP) と呼ばれる、これを制限するビルドインのメカニズムがありました – しかし、これは面倒で広く採用されなかったため、非推奨となり削除されました。PSPの代わりにPod Security Admissionというビルトインの仕組みがありますが、これはKubernetes 1.25でGA化されました。しかし、その間に多くの人が、PodSpecでこの種の安全でないパラメータを要求する能力を制限するために、汎用のアドミッション・コントローラ-通常はOpen Policy Agent(OPA)のGatekeeper-に移っています。OPAはオープンソースであり、Kubernetesと同様にCNCFのプロジェクトとして活動しています。
Admissionコントローラのアイデアは、Kubernetesクラスターに与える新しい/更新されたYAMLドキュメント(PodSpecsなど)に対するファイアウォールのようなものです。
OPAとKubernetesの関係図
クラスターに新しいYAMLや更新されたYAMLを与えると、それをOPAで実行してOKかどうかを聞いてから実行し、OKでない場合はOPAでエラーメッセージを返します。
KuberntesにOPAをGatekeeperでインストールするには、こちらの手順で簡単にインストールすることができます。
そして、彼らがGitHubで公開しているOPAポリシー例のライブラリー(https://github.com/open-policy-agent/gatekeeper-library)があります。特に、Podのセキュリティに関する最も一般的な問題をカバーする旧来のPod Security Policy(PSP)ルールを複製する方法の例が、このライブラリに含まれています。OPA GatekeeperをPod Security Admissionよりも使用する主な利点の1つは、厳密にセキュリティ関連ではないものの、運用面で非常に優れたポリシーを適用することができることだと思います。例えば、次のようなものを追加することができます。
- 各コンテナにはCPUとメモリのRequestを定義する必要があります(Kubernetesがワークロードを効果的にスケジュールし、優先順位付けする方法なので、それ自体のパフォーマンスが低下したり、うるさい隣人となることはない)。
- イメージは、特定の信頼できるプライベートレジストリから取得する必要があります。
- サービスが正常に開始されたときと、不健全になって取り替える必要があるときをKubernetesが認識できるように、すべてのコンテナにProbe(Kubernetesのヘルスチェック)を定義する必要があります。
- その他、多数…
Falcoでホストの分離境界の問題を検出する
OPAは、コンテナの実行方法についてKubernetesに宣言するYAML命令に、安全でない、または不適切なパラメータが含まれていないことを確認するのに適しています。しかし、それが役に立たないのは、そのコンテナの実行時における振る舞いです。そこでFalcoの出番です。Falco は、各ノード上のすべての Linux システムコールを監視するように設定することができ、疑わしい多くの一般的なランタイムイベント/振る舞いを検出し、コンテナが未パッチの脆弱性を持ち、活発に悪用されているために何か不正なことをしている場合などにアラートを出すことができます。
Falcoは、Sidekickと一緒にDaemonSet(Kubernetesが各Nodeのローカルカーネルのシステムコールを監視するために実行されていることを保証する)として簡単にインストールすることができ、そのHelmチャートを通じてどのような疑わしい活動を探すべきかについての素晴らしいスタータールールのセットはここにあります。
このHelmチャートに含まれるデフォルトのルールがイベントとしてアラートするアクティビティの例をいくつか挙げます。
- 機密性の高い領域への書き込み(バイナリディレクトリ以下、/etc以下、など)
- chmod によってコンテナ内に新しい実行ファイルが作成された。
- 起動後や信頼できないプログラムによる信頼された機密ファイルの読み取り
- ログやシェル履歴のクリア
- コンテナ内でdockerクライアントやターミナルシェル、怪しいネットワークツールが起動される
- などなど。
境界その3 – ネットワーク
ネットワークポリシー
Kubernetesクラスターで最後に必要となるのはネットワークの分離です。KubernetesではこれをNetworkPoliciesによって行います。- どのようなトラフィックを許可するか、または許可しないかを示す宣言的な文書です。
- IP/CIDRではなく、Kubernetesのラベルに基づいてルールを定義できます(よりダイナミックで定義やメンテナンスが容易になる)。
- そして、Kubernetesの多くのものと同様に、プラグイン可能なNetworkPolicyProviderによって強制され、CalicoやCiliumなどのいくつかのオプションから選択することができます。これらはしばしば、Kubernetes ネットワーク、または CNI ドライバとしても機能します。これは、すべての Pod ネットワークのプロビジョニングを担当するサービスが、プロビジョニングされたネットワークのセキュリティ確保にも最適な位置にあることから、理にかなっていると言えます。
しかし、NetworkPoliciesには、以下のようないくつかの課題があります。
- EKS、AKS、GKEなどの多くの一般的なKubernetesサービスは、デフォルトでNetworkPolicyProviderを含んでいません。NetworkPoliciesを使用できるようにするには、オプションパラメータで要求するか、現在のEKSの場合、自分でインストールして管理する必要があります。
- NetworkPolicyProviderがインストールされると、クラスター上のすべてのPodはデフォルトで互いに通信できるようになります(つまり、常にデフォルトのdenyではなくallowになる)。
- OSIモデルのアプリケーション層ではなくネットワーク層で動作します(7層ではなく3層)。つまり、特にクラスター外のものに対するルールは、IPとCIDRに基づいている必要があります。これは、IPアドレスが動的な場合(パブリック・クラウドのマネージド・サービスではよくあること)、データベースなどクラスターの外部のものを独自のサブネットに置く必要があることを意味します。
したがって、どの NetworkPolicyProvider を選択するか、そして、そのプロバイダーが追加した非標準の拡張を利用して、自分自身で物事を簡単にするかどうか(ただし、そのプロバイダに自分自身をロックするかどうか)は、今日 NetworkPolicies を実装するときに行うべき重要な決定の1つです。
一般的な NetworkPolicies の素晴らしい例と関連する図がいくつかあります。
Falco によるネットワーク分離の問題の検出
上記の OPA と Falco の違いと同様に、NetworkPolicies はネットワーク トラフィックをブロックまたは許可できます。 しかし、Falco は、関連する Linux カーネル syscalls を介してそのネットワーク トラフィックを可視化します。したがって、各ノードで行った同じ DaemonSet インストールにより、その可視化も有効になります。これらのデフォルトルールがイベントとしてアラートするアクティビティの例をいくつか挙げます。
- ローカルでないサブネットまたは信頼できない宛先へのアウトバウンドネットワーク接続(ip、ネットワーク、ドメイン名の事前定義された許可セット)。
- 一般的なコマンド&コントロールサーバーへのアウトバウンド接続
- SSHサーバーへの接続
- などです。
Helmチャートのこれらのデフォルトを超えて、ネットワークトラフィックの監視に関してFalcoのルールで可能なことの良い例は、このブログポストで説明されています。
まとめ
今回は、Kubernetesのセキュリティコンセプトについて、Kubernetで遭遇する3つの主な隔離境界と、デフォルトの設定/強化方法、およびOPAやFalcoなどの追加オープンソースツールを使ってクラスターをより安全にする方法を中心に、概要を紹介をしました。これは、クラスターをチーム間で共有する場合(マルチテナンシー)、あるチームから他のチームが自分たちの環境/ワークロードに干渉しない/できないことを期待されている場合に特に重要なことです。特に、Falcoは上記3つの隔離の境界すべてに関連している事をご理解されたと思います:
- Kuberentesの監査イベントのストリームと、すべてのNodeのシステムコール(ネットワークトラフィックを含む)の監視
- この3つすべてにおいて、実行時に脅威を探します。Falcoのデフォルト設定は良いスタートですが、Falcoを最大限に活用するには、あなたの環境やニーズに合わせて、独自のルールや例外を調整したり書いたりすることが必要です。
Sysdigは、あなたが本当に始めるために無料のFalco 101トレーニングコースを提供しています。
また、日本でFalcoの基本を紹介しているオンデマンドウェビナーをこちらからご登録の上、ご視聴いただけます。