[レポート][CON318] Kubernetes のセキュリティ: Kubernetesの攻撃ベクトルに対処する方法 #reinvent

2022.12.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、CX事業本部、re:Invent 2022 現地参加組の田中孝明です。

セッション概要

CON318: Securing Kubernetes: How to address Kubernetes attack vectors

このセッションでは、Kubernetes アーキテクチャと一般的な攻撃ベクトルの基礎、それらに対処するために Amazon EKS によって提供されるセキュリティ制御、お客様がリスクを軽減するために実装できる戦略、およびオープンソース Kubernetes が改善できる機会について概説します。

セッション動画

セッション

今回のセッションで、遭遇する可能性のあるインシデントについてお話しします。また、一般的な脅威についても説明し、攻撃ベクトル緩和策について説明します。

あなたは Kubernetes を管理しています。ある時セキュリティ研究者が「私はこのエンドポイントを curl することができます。」と報告してきました。

これはどういう意味なのか?

誰かが curl を実行し、このエンドポイントに1つのウェブリクエストをしました。 そしてこのドメインにヒットしました。 このドメインとは、EKS Kubernetes クラスタのエンドポイントです。 リージョンが見えて、クラスタがあり、そこに一意な識別子がある。

Kubernetes は内部IDを持っており、そこにあなた自身の秘密を保存することができます。 それが SendGrid のような外部サービスの認証情報であろうと、なんであろうとです。

このリクエストは、認証がないのがわかりました。

誰かが curl を実行し、このエンドポイントに1つの request をしました。 単なる curl ですが、クラスター内の全てのシークレットを取得してる。

攻撃者はこれがクラスタであることを知っていて、クラスタの1つであるかどうかを確認しようとしています。 すべてのKubernetes、EKS クラスタを記述し、エンドポイント名とそれに対応するクラスタ名を取得します。 するとクラスタ名とエンドポイントのリストがダンプアウトされます。 あなたのクラスタにはこの問題があるということがわかります。

Kubernetesのクラスターロールバインディングを見てみましょう。 これはKubernetesの認証システムで ロールベースのアクセス制御を使ってユーザーを権限セットにバインドしています。

cluster-system-anonymous という怪しいものがあるのがわかります。 誰かがこれを作って、system anonymous ユーザーを cluster admin ロールにバインドしたようです。

このクラスタ管理者ロールは Kubernetes に組み込まれていて、未認証のユーザには自動的にsystem というユーザ名が割り当てらるが、誰かがクラスタのKubernetes APIでどんなことでもできるようにしてたら? これが本番クラスタでないことを祈ります。

まずは削除してしまったかもしれないことを突き止めましょう。

CloudTrail、CloudWatch Logs、CloudWatch Logs Insights を表示させます。 このクエリで、EKSクラスタのKube APIサーバの監査ログを見て、APIグループが戻ってきたかどうかイベントを探します。 ステータスコードが 300 未満であることを確認し、更新があったと言うことを確認します。

このイベントに遭遇しました。誰かがこのシステムを作成し、匿名ユーザーでクラスタロールadminにバインドしています。 下の方の UID は EKS の場合、IAM User に対応します。さらに下の方にアクセスキーIDがあります。 このアクセスキーから、誰がこの役割を引き受けたか CloudTrail から照会することができます。 AWS側で根本的な原因を特定し、追加で修正することができます。

脅威について説明しましょう。

開発者が Git リポジトリにコードをPushし、ビルドと CIシステム に送られます。 ECR のようなコンテナレジストリにアーティファクトをプッシュし、それらは Kubernetesクラスタ にデプロイされます。

運用エンジニアが Kubernetes にアクセスする必要があったり、プラットフォームエンジニアがその Kubernetesクラスタ にアクセスする必要があるかもしれません。

Kubernetesクラスタ は、ロードバランサーからトラフィックを取り込むインスタンスを管理し、それらのインスタンスには、S3 にオブジェクトを読み書きするアプリケーションが搭載されています。 キャッシュには Amazon ElastiCache を、ストレージには RDS を使うかもしれません。CloudWatch にログを送信しているかもしれません。また、S3オブジェクト の前に CDN があるかもしれません。

このアーキテクチャを元に、クラスターにとって何が脅威なのかを考えてみましょう。

ロードバランサーが1つの Pod にトラフィックを送っているような場合です。 その Pod でデータベースにアクセスしたり、別の Pod で別の EC2インスタンス 上のサービスにアクセスしたりします。 これらの Pod は、ディスクからファイルを読み書きしています。 Kubernetesエージェント、Kubelet、コンテナランタイム があり、これらは ECR から取得されます。DaemonSet もあるかもしれません。これらのログを CloudWatch にアップロードしています。

情報セキュリティの3本柱は機密性、完全性、可用性です。 これらを使って、脅威モデルや、Kubernetesクラスターをどのようにセキュアにするかという、脅威モデリングに関する質問を考えてみたいと思います。

セキュリティの観点から、アプリケーションへのロードバランサーにアクセスする必要があるのはどのネットワークでしょうか?

  • API?
  • VPC内?
  • インターネットから操作する?
  • どのようなアクターやプロセスがデータにアクセスする必要がある?
  • そのデータは S3 にある?
  • そのデータは RDS にある?
  • VPC内の他のアプリケーション?

コンテナレジストリや、コンテナレジストリに登録されるデータもそうです。

機密性については

  • どのようなアクターがそのデータにアクセスする必要があるのか知っていますか?
  • どのようなプロセスなのかを知っていますか?
  • それらは信頼するものだけですか?
  • 信頼できるコードを実行しているか?
  • 任意のコードを実行しているのか?

Kubernetes のすべてがネットワーク化されているので、常に脅威やセキュリティについて考えます。

  • コンテナ内で任意のコードを実行することはありますか?
  • セキュリティの境界線として、コンテナを信用しますか?

Linuxカーネルをセキュリティの境界として信頼しているかということです。

  • アプリケーションは、任意のネットワークにアウトバウンドリクエストを行っていますか?
  • 自分がコード化したものを呼び出すだけですか?
  • あなたのアプリケーションがアクセスするURLを、アプリケーションで指定することができますか?

それができれば、彼らはKubernetes APIに入ることができます。

  • 彼らは、クラスタ内の他のドメインに入ることができるか?
  • これらの外部呼び出しはすべて既知か未知か?
  • どのネットワーク、ユーザープロセスが Kubernetes API にアクセスする必要があるか?

セキュリティについて考えているのなら、どうすればアクセスを制限することができるでしょうか?

今回は、Kubernetes 自体への脅威に主に焦点を当てたいと思います。

OWASP (Open Web Application Security Project) のトップ10があります。数年おきに、調査に基づいた10のカテゴリーのセキュリティ問題のリストを発表しています。

これらはアプリケーションに現れるセキュリティ脆弱性の種類です。今回は、Kubernetes に影響を与えるものについて掘り下げます。

まず、アクセスコントロールです。

アクセス制御について考えるとき、OWASPの観点では、最小特権の侵害について考えることになります。

Kubernetesにアクセスするアクターやエンティティは、Kubernetesを実行している人たちでしょうか? 彼らが必要のない特権を持っている場合、攻撃ベクトルの源となります。

ユーザーや Pod に対する Kubernetes API のパーミッションはAPIにアクセスするユーザがいれば、それがエントリーポイントになります。Kubernetes の Pod が Kubernetes API と話す必要がある場合、あなたは Pod が何をするのかわかりますが、人間のユーザーは別のことをするかもしれません。

Pod にメタデータを提供するための拡張権限を付与することはどうでしょう。 Kubernetes には、環境変数や DNS を使って、クラスタで動作している他のサービスを問い合わせることができるサービスディスカバリーが組み込まれています。 オンにする場合は、必要以上の権限である可能性があります。

Pod のLinuxパーミッションも同じです。読み取りと書き込みができるようにするのはやめましょう。これは最小特権の侵害になりえます。

アクセス制御が破たんしている分野として、特権の拡大があります。 Pod で、ノードに意図した以上の権限を持たせるように特権をエスカレートさせることができます。

Kubernetes の脆弱性があることを考慮に入れましょう。

いくつかのアプリケーションは、予期せぬ方法で必要以上の権限を持つことがあります。

Pod を動かしていて、それがAWSのサービスにアクセスする必要があるとします。サービスアカウント用の IAMロール です。Kubernetes が JSONウェブトークン を発行しジョブとして実行します。ノード上の Kubelet が実際にAPIサーバーからそれを要求し、Kubelet はそのジョブを Pod にマウントし、ポッドが起動すると、AWS SDKはディスクからそのファイルを読み、STSにコールアウトしてロールを引き受けるパターンです。

同じロールやタイプのアクションを多くの異なる Pod で実行する必要がある場合、すべての Pod にこのトークン取得をさせなければならないのです。

CSI Sidecar にこれをやらせましょう。 すべての Pod に異なる IAMロール を持たせてるのではなく、CSI の Pod に異なる IAMロール を持たせて、その IAMロール のすべてにパーミッションを追加するのです。

CSI DeamonSet は、トークンを取得してディスクにファイルとしてマウントするのではなく、API サーバーからトークンを取得するようなことを行う。

CSIドライバ にサービスアカウント・トークンを作成する許可を与える必要があります。 Kubernetes でそれを行う方法は、ロールベースのアクセス制御です。これは属性ベースのアクセスではありません。ロールベースのアクセスです。クラスタロールは、サービスアカウントトークンを作成するためのものです。

これはクラスタ上のすべての Pod、すべての DeamonSet Pod に適用されることに注意してください。 どの CSI も、マウントを作成しようとしている Pod だけでなく、クラスタのどのサービスアカウントに対してもサービスアカウントトークンを取得することができるということです。

Kubernetes API には多くのサービスアカウントが組み込まれており、スケジューラやコントローラマネージャで使用され、Pod を作成するための多くのパーミッションを持っています。 Sidecar が同じノード上の Pod のサービスアカウントを取得できる代わりに、クラスタ内のどの Pod のサービスアカウントを取得できてしまうのです。

この緩和策として、Kubernetes で新しい機能トークン要求CSI機能が出ました。Kubelet が Pod用 にスコープされたサービスアカウントトークンを CSIドライバ に実際に発行するものです。この機能の安全なところは Kubelet はロールベースのアクセス制御をパーミッションに使わないことです。

Kubernetes には別の認可者が組み込まれています。ユーザーが設定できるものではありませんが、ノード制限の許可プラグインがあり、kubelet がそのノードに割り当てられていないリソースにアクセスすることを制限します。

開発者が Pod を作成する権限を持ち、オペレーターが カスタムリソース を作成する権限を持つような場合です。

Kubernetes には、カスタムリソースという考え方があります。Kubernetes API で作成可能な新しいタイプのレコードを動的に定義することができます。

AWSでは AWS Controllers for Kubernetes (ACK) プロジェクトがあります。

カスタムリソースを通してAWSリソースを定義することができる、異なるプロジェクトのHUBです。

オペレーターに CRD、S3バケット、リレーショナルデータベース、ロードバランサーなど、様々なものを作成できるようにしたいと思います。

このクラスタロールで、あらゆるリソースを作成することができます。これを Operatorグループ にバインドします。 そして開発者には、Kubernetes のコアグループで何でもできる権限が与えられます。 オペレーターはAWSのリソースにアクセスでき、開発者はクラスタで Pod を作成するだけというある意味分離されたものです。

問題となるのは権限です。

この開発者のリソースはAPIネームスペースにアクセスできます。Kubernetes には、なりすまし機能があります。それはロールベースのアクセスコントロールによって管理されています。verbs や resources を指定しなければ、すべてが含まれます。開発者にオペレーターになりすます能力を与えることができました。

最小限の権限でバックルールを使用することで、緩和することができます。実際に監査ログからポリシーやバックポリシーを生成することは、オープンソースのプロジェクト audit2rbac で可能です。

CSIを使うときは、ドライバを使うか、使うときはそれがトークン・リクエストをサポートしているか調べてみてください。 CSIドライバー に対するクラスタ全体のパーミッションを制限するもう一つの方法です。

バック・ポリシーを書くときは、verbs と resources を明示的に列挙してください。そうすれば、誤って impersonate を許可してしまうことを防げるでしょう。 Kubernetes には、標準的な read write get delete patch 以外にも、特殊な verbs がいくつかあります。listwatch です。

セキュリティの設定ミスについてです。

  • 認証の設定ミス
  • 不要な機能を有効にしてしまうこと
  • 安全でないデフォルト

Dockerソケットをポッドにマウントしようとした場合です。「なんでこんなことしてるのだ」と思うかもしれません。Jenkinsポッドとか、コンテナをビルドする必要があって、コミュニティソケットにアクセスする必要がある場合です。

これは Kubernetes のコンテナ作成に関するセキュリティ制御をすべてバイパスしています。 このソケットを Pod にマウントすることで、基本的にあらゆるレベルのプロセスを実行するためのフルランタイムパーミッションを付与していることになります。セキュリティ上の誤設定ですので絶対に行わないでください。

もう一つは、安全でないデフォルトです。

デフォルトの設定値では、 anonymousenablefalse になっています。認可モードは常に allow がデフォルトに設定されています。

kubelet は Kubernetes のエージェントです。Pod を作成し、APIサーバと通信して Pod を探し、ランタイムと通信して実際に Pod を起動します。 kubectl コマンドでログを見る場合も実際は Kubernetes API サーバー を叩いています。

認可モードは常に kubelet server へのリクエストを常に許可します。

EKS optimized AMI を使用している場合、Kubelet設定ファイル を読み出すと、Kubernetes のデフォルトを使用しない認可と認証セクションが表示されるでしょう。

aws-auth の ConfigMap の system:masters にユーザーを追加しない。

必要なホストアクセスはロギング以外の一般的なアプリケーションポッドでは制限する。

プロバイダのデフォルトを使用する。(この場合は EKS のデフォルト)

過去5年間の脆弱なコンポーネントと古いコンポーネントについてです。

オレンジ色のボックスは Amazon Linux の CVE を表し、青いボックスは Kubernetes の CVE を表しています。 過去5年間の CVE の数ですが増加傾向にあります。2022年の今年だけでも、Kubernetes と Linuxカーネル を合わせた問題がいくつかあることがわかります。

100件以上あるので、平均して週に2件は起きていることになります。

ノードが1ヶ月間動いていて、カーネルのライフパッチをやっていなければ、おそらく古く、脆弱なコンポーネントを持っている可能性があります。

CVE が増えるほど、コンテナやマシン・イメージを最新の状態に保ちたいと思うのは当然です。しかし、運用上の問題が発生する可能性のある変化や変更も導入することになります。 目標は必ずしも報告される CVE をゼロにすることではなく、その CVE が我々に影響を与えるかどうかを実際に確認する必要があります。

できる限りアプリケーションを最新の状態に保つようにしましょう。

もうひとつは、Kubernetesクラスタをサポートされたバージョンに保つことです。

このリンクは、Amazon EKS でサポートされている Kubernetes のバージョンです。Kubernetes の特定のバージョンが EKS でいつサポートされるかのカレンダーを表示しています。そして実際にアップデートする必要があるのはいつなのか、Kubernetes のバージョンを最新に保つための計画を立てておくことは、皆さんにぜひやっていただきたいことです。

ログの記録とモニタリングの失敗についてです。

多くは運用上のアラートに長けていて、ダウンタイムは避けたいと考えています。しかし、セキュリティ・イベントのログ記録と監視はしばしば後回しにされがちです。

もしあなたが EKS log を使っているなら、コントロールプレーンログをオンにすることをお勧めします。

EKS は自動的に監査ログをあなたの CloudWatch に提供します。そして、Kubernetes のデータプレーンについても同様です。自己管理ノードや EKS、管理ノードグループを使用しているのであれば、システムログをホストから取得するためにロギングをセットアップしてください。フォレンジック分析が必要な場合、あるいはこれらのログに対してセキュリティ警告を行いたい場合に設定することができます。

最後の1つは、サーバーサイドリクエストフォージェリです。

デプロイメントを適用するのに kubectl を使うかもしれません。 APIを作成し、コントローラ・マネージャがレプリカセットを作成し、Pod を作成し、スケジューラがその Pod をノードに割り当てます。 Pod は kubelet によって作成され、kubelet は CNI コンテナネットワークインターフェースドライバを呼び出し、CNI ドライバは IP アドレスを返信します。 kubelet は APIサーバー に Pod の IP を返します。Pod の IP は 192.168.56.145 です。

その Pod にポートフォワードしたい場合、kubectl では nginx ポート80 にポートフォワードするだけです。 Kubernetes API はすでにそれを知っているので、IP を伝える必要さえありません。

APIサーバー は、kubelet から報告されたIPに直接ネットワーク接続し、TCPプロキシを送信することで、kubelet から報告されたIPにアクセスします。TCPプロキシ を送信して、ローカルでウェブブラウザから Pod をヒットできるようにします。

しかし、Pod のステータスを変更する権限を与えると、その人は kubectl proxy ができるようになります。 バックグラウンドでローカルに APIサーバー への認証接続を開き、curl を呼び出してこのポッドのエンドポイントをヒットし、異なる IP を設定するためにステータスにパッチを適用しているのです。

169.254.169.254 はAWSのメタデータサービスで、インスタンスのクレデンシャルを返してくれます。

Kubernetes では、これは CVE で修正されました。

この方法では Kubernetesコントロールプレーン のメタデータ認証情報を取得することはできませんが、攻撃者はこの Pod IP を VPC内 の別のものに更新することができればどうなるでしょうか。

Pod IP ではなく、RDSデータベース や Elasticache for Redis など。Kubernetes APIサーバー に対してパッチとポートフォワードの両方に十分な権限があれば、攻撃者は VPC を調査することができます。

Kubernetes を使って穴を開け、アクセスすべきでないものにアクセスする方法です。

これに対して何ができるでしょうか?

Kubernetes のログを有効にしましょう。そうすればノードにパッチが当たっていない Pod の状態についてアラートを出すことができます。

Kubernetes APIサーバー が Elasticache for Redis や RDS、VPC内 の他のものに直接アクセスすべきでない場合は、そのセキュリティグループを制御してアクセスさせないようにしましょう。

そして、クラスタを最新の状態に保つことです。クラスタのアップデートは常に行ってください。これは、コンテナやコンテナイメージ、ノードグループにも当てはまります。

Kubernetes secrets を KMS で暗号化するのもいい方法です。KMSキー を渡すと、Kubernetes secrets を暗号化することができます。

Kubernetes APIサーバーへのアクセス、インターネット向けエンドポイントが不要な場合は、これを無効にしてVPC経由でのみアクセスするようにしましょう。

EKS のセキュリティベストプラクティスガイドを紹介します。

所感

業務で EKS や Kubernetes に触れることがないのですが、攻撃手法、気をつけるべき点、防御の観点などを学ぶきっかけになりました。