2021年のK8S周りのCVEまとめて多層防御の重要性を再確認する

Kakeru Ishii
google-cloud-jp
Published in
24 min readDec 11, 2021

こんにちは、TSEのkakeruです.この記事は2021年Advent Calendarの12日目の記事です.

今回は1年の終わりに今年レポートされていたK8S周りの脆弱性を振り返り、どのような対策をしていたら仮に類似のゼロデイがあっても被害を食い止めることができるか考えていきます.

なお、私はセキュリティの専門家ではないのであくまで参考程度にとどめていただければ幸いです。K8Sクラスタを守るためになぜ多層防御が大事なのか実感していただけるきっかけになれば幸いです.

今回はこちらのWebサイトで`Kubernetes`でヒットする脆弱性を主なターゲットとして簡単に中身を確認して分類していきます.

https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=kubernetes

2021年のCVEでは22件のヒットが確認できました.それぞれについて見ていきます.

2021年のKubernetes関連CVE

CVE-2021–20218
(fabric8のkubernetes-clientにおける悪意のあるtarファイルの展開によるディレクトリトラバーサル)

#Kubernetesクライアント/SDK #悪意のあるコンテナの利用 #ディレクトリトラバーサル

fabric8という既に継続が終了したプロジェクト内で開発されていた、kubernetes-clientに潜んでいたディレクトリトラバーサル系の脆弱性です.悪意のあるコンテナが不正なtarファイルを出力し、それをkubernetes-clientを用いてファイルとして展開しようとすると指定しているフォルダの範囲外に展開されてしまうことがあったようです.

カスタムコントローラ等で自動的にコンテナの中身に対してコピー等の操作をしていた場合、コピー先としているフォルダの外にもファイルの書き込みが可能であったとみられ、例えばそこに「実行するバイナリが置いてあった」などの場合においては脆弱性を悪用される可能性がありました.

CVE-2021–21243
(onedev上のREST APIの認証バイパス)

#開発者ツール #認証バイパス

onedevというCI/CDやそれに伴うIssue Trackingツールなどをホスティングするためのプロジェクト上に存在した脆弱性のようです.ツール上のkubernetes APIに関連するエンドポイントが認証系を経由することなく実行することが可能になっていたようです.

CVE-2021–21303
(Helmチャート上の信頼できないリソースの文字列がサニタイジングされていない問題)

#開発者ツール

Helmに存在した脆弱性のようです.一部のケースでHelmが信頼されない場所からデータを取得し、十分にSanitizeしないまま実行していました.結果的にTerminalのスクリーン上の出力を不正に改変したり、隠したりすることができたようです.

直接何らかのコードを実行できた訳ではなく、あくまで表示上不正に隠すことができたりした問題なので開発者を騙すために利用はできたかもしれませんが、何らかの攻撃に発展した可能性は低いと思われます.

CVE-2021–21334
(containerdの同一イメージ実行時に環境変数が混同していた問題)

#ノードOS #悪意のあるコンテナの利用

containerdに存在した脆弱性で、同じイメージを別の環境変数の設定で起動していた場合に意図せず誤った環境変数を渡していた可能性がありました.例えば攻撃者があるPodで動作するコンテナに渡されている環境変数を盗み見たい時に、もし同じイメージを同一のクラスタ内で起動できる権限を持っていたとすると、この脆弱性を用いて秘匿情報を盗むことが可能だったと思えます.

CVE-2021–21661
(Jenkins K8s CLIプラグインにおける認証バイパス)

#開発者ツール #認証バイパス

JenkinsのKubernetes CLIプラグインに存在した脆弱性のようです.いくつかのHTTPエンドポイントに対してパーミッションのチェックを行っておらずKubernetesの認証情報のIDのリストを外部から列挙できた可能性がありました。この脆弱性ではシークレットの中身まで確認することはできず、あくまで存在しているシークレットの名前のリストを知ることができることに留まりました.

CVE-2021–24109
(Azure AKS上の権限昇格)

#クラウド #PE

AzureのAKSの権限昇格関連の脆弱性のようです.詳細がないので説明は割愛します.

CVE-2021–25735
(ノード情報更新時に一部Admission Webhook上で認可バイパスが可能であった問題)

#Kubernetesクライアント/SDK #認可バイパス

ノードのアップデート時のAdmission Webhookをバイパスすることができた脆弱性です.例えば、変更前のK8Sオブジェクトのメタ情報をもとにAdmission Webhookが変更をReject, Acceptするような以下のようなコードがあったとします.

脆弱性のあるノードのAdmission Webhook

このコードでは、Nodeのlabelにprotectedという属性があった場合にAdmission Webhookが不許可のレスポンスを返すようになっています.OldObjectをチェックしているので書き換えようがなさそうですが、新しく与えられたメタデータの一部が含まれた状態で渡されてしまう脆弱性がありました.そのため、新しいノードのメタデータにprotected: “true”を入れていたらこのAdmission Webhookをバイパスできてしまいました.

CVE-2021–25737
(EndpointSlices APIをLink-localアドレスと利用することによりホストネットワークのハイジャックが可能であった問題)

#悪意のあるK8Sリソースの利用 #ネットワーク

K8S1.21でStableにあがるEndpointSlicesのAPIで、対象とするIPアドレスのフィルタリングが行われておらず、localhostもしくはlink-localなアドレスを入れることによってホストネットワークのハイジャックに用いることができる可能性がありました.

CVE-2021–25738
(K8S Java SDKにおけるYAMLパーサの機能により、任意クラスのコンストラクタが実行可能であった問題)

#Kubernetesクライアント/SDK #RCE

K8SのJavaクライアントのYAMLパーサーに脆弱性があったようです。

型の不明なYAMLをデシリアライズする際、対応するJavaの型がYAML中に!!をプレフィクスとして書かれていると、その型を使う機能が依存先のライブラリについていました.結果的に、任意のクラスの引数なしコンストラクタを実行できる状態になっていたようです.

CVE-2021–25740
(EndpointSlices, Endpoints APIを用いて、アクセス権のないLBに接続しているサービスを別の名前空間から読み出せる問題)

#悪意のあるK8Sリソースの利用 #認証バイパス

K8S 1.21でStableにあがるEndpointSlicesもしくはEndpointsのAPIを用いれば、名前空間の境界を超えて本来アクセス権のないサービスにアクセスすることができるのではないか?という脆弱性です.現時点で修正は行われておらず、修正にはユーザに見える部分の修正が含まれることから長期的な時間がかかる見込みになっています.

CVE-2021–25741
(ボリュームのSubpathとシンボリックリンクを用いたディレクトリトラバーサル)

#ディレクトリトラバーサル #悪意のあるK8Sリソースの利用

シンボリックリンクを用いたディレクトリトラバーサル系の脆弱性です.K8Sでボリュームをマウントする際、通常はボリュームのルートディレクトリが指定されたコンテナ内のパスにマウントされます.ボリュームマウント時の設定のsubPathを指定すると、ボリュームの一部のフォルダだけをマウントできます.完全に信頼できないユーザがsubPathを設定してPodを作ることを考えると、subPathに指定されている対象がシンボリックリンクだった際にそれを評価することは危険で、一度kubeletがPod作成時に必要なフォルダを全てマウントしてその中の領域をコンテナに割り振るため、このシンボリックリンクはホストのボリュームの領域を指すことが可能です.Podを好きに作成できるユーザがホスト内にある任意のフォルダを読める状態にあったということになります.

ただし、こちらの脆弱性を悪用するには元々のコンテナがrootで実行されている必要があり、例えば以下のような設定がPodに入っていたり、イメージ内の実行ユーザがrootのままになっている場合に問題がありました.

CVE-2021–25742
(nginx-ingressのServer snippetを用いたサービスアカウントの窃取)

#悪意のあるK8Sリソースの利用 #PE

nginx-ingressの脆弱性です.nginx-ingressをデフォルト設定でインストールすると以下のようなClusterRoleが作成されます.

このCluster Roleはsecretsに対してのリスト権限を認めています.こちらの権限が強いので、攻撃者にとってはnginx-ingressのサービスアカウントの認証情報が良いターゲットになりえます.

ところで、nginx-ingressではデフォルトで`スニペット`を設定することができます.例えば以下のような設定ができます.

こうすると、`/fuga`にアクセスすることで200 OKが帰ってくるわけですが、このようなnginx設定の一部をIngress内の設定で書き換えることができます.さて、ここでIngressの利用者が必ずnginx-ingressと同等の権限を持っているわけではないかもしれません.

例えば、以下のような設定を用いるとnginx-ingressが用いているサービスアカウントの認証情報を読み込むことができてしまいます.

対策を行うためには`ingress-nginx-controller` ConfigMapに設定できる`allow-snippet-annotations`オプションを`false`に設定することでこちらのスニペットを利用しないことが可能です.

CVE-2021–28448
(VSCodeのK8Sプラグインのワークスペース設定を用いた信用していないバイナリを実行してしまう問題)

#開発者ツール #RCE

VSCodeのKubernetesツールの脆弱性のようです.ワークスペース設定のtoolPathをそのまま利用されてしまう危険性があったようです.

CVE-2021–31938
(VSCodeのK8Sプラグインによってインストールされるkubectlが書き換えうる問題による権限昇格)

#開発者ツール #PE

別のVSCodeのKubernetesツールの権限昇格の脆弱性のようです.VSCodeによってインストールされるkubectlバイナリが0777モードで保存されていました.kubectlの書き換えによって少ない権限しか持っていない攻撃者がユーザの権限を用いてプログラムを実行できる危険性がありました.以降、0755で保存されるように修正が入っています.

CVE-2021–32690
(Helmレポジトリが外部レポジトリを参照する際にユーザ名/パスワードが漏洩する問題)

#開発者ツール #シークレット漏洩

Helmの脆弱性のようです.レポジトリからチャートを取得する際にユーザ名やパスワードを用いている際、Helmレポジトリのindex.yamlに含まれていた別のドメインへのアクセス時にも同じユーザ名とパスワードが渡されていたようです.

CVE-2021–32783
(ContourのEnvoy管理者ページに対してExternalNameタイプのサービスを利用すると不正にアクセスできた問題)

#悪意のあるK8Sリソースの利用 #認証バイパス #ネットワーク

ContourというEnvoyベースのKubernetesのingress-controllerに存在した脆弱性です.通常アクセスできないはずのEnvoyの管理者ページに対してExternalNameタイプのサービスを細工して作成することによってアクセスができてしまったようです.

シークレットの内容等を確認することはできませんが、どのようなシークレットが利用されているか確認することができました.また、Envoyのシャットダウンやメタデータ上のシークレット等を見ることができていたようです.

CVE-2021–3499
(OVN Kubernetes環境下で外向きファイアウォールが正常に設定されなかった問題)

#ネットワーク

OVN(Open Virtual Network) Kubernetesに存在した脆弱性です.複数のDNSルールが存在した際に外向きのファイアウォールが正常に適用されなかった問題でした.

CVE-2021–39159
(binderhub上のシークレットが見える権限が付いたままリモートコード実行可能であった問題)

#RCE #シークレット漏洩

jupyterhub傘下のプロジェクトであるbinderhubに存在した脆弱性です.これはGitレポジトリからDockerイメージをビルドしてJupyterHub上で実行できるようにするためのものですが、不正な入力を渡すことで実行しているkubernetesサービスアカウントのトークン、Jupyter HubのAPIトークン、Dockerレポジトリの認証情報を取得することが可能でした.

CVE-2021–41137
(Minioの特定のバージョンでポリシーが正常に動作していなかった問題)

#認可バイパス

Minioの特定のバージョンでポリシーによる制限が正常に動作していなかったようです.発生したのは`RELEASE.2021–10–10T16–53–30Z`のバージョンのみで、ダウングレードするか、アップデートすることによって問題が解決しています.

CVE-2021–41254
(Kustomize controllerのコード実行機能を利用した権限昇格)

#RCE #PE

kustomize-controllerはKubernetes上でKustomizeで作成されたK8Sオブジェクト等をKubernetesに適用するためのCDパイプラインツールです.Kubernetesのシークレットにシェルスクリプトのコードを埋め込むことによってkustomize-controllerのサービスアカウントを利用したkubectlを実行することができていました.このサービスアカウントはCDパイプラインツールなので一般的にクラスタ全体へのアクセス権を持っており、アクセス権のないユーザがこれを用いることによって権限を昇格させることができていました.

CVE-2021–41266
(Minioが特定の条件で認証をバイパスできていた問題)

#認証バイパス

Minioのコンソール(Minioそのものではない)で外部のIDPが有効になっている際に認証をバイパスできた問題のようです.

CVE-2021–43979
(OPA/Gatekeeperのポリシー評価結果が並列性によって一貫した結果にならない問題)

#認可バイパス #仕様(?)

OPA(Open Policy Agent) Gatekeeperに存在する並列性の取り扱いが時によって正しくないアクセスコントロールの結果になってしまう問題です.

データリプリケーションという機能によってKubernetesクラスタのステートをOPA/Gatekeeperは用いることができますが、リプリケーションをOPA/Gatekeeperが待たないため一貫した結果になりません、

ただし、最終的にK8Sオブジェクトは一貫性を持つため、脆弱性ではないという意見もあり議論を呼んでいます.

まとめと対策

できる限り信用できないコンテナを実行しない、コンテナにルート権限を与えない

#悪意のあるコンテナの実行と記載されているCVEは、細工したコンテナの実行が必要な脆弱性です.当然ですが、信用できないコンテナの実行はそのままリスクに直結します.Kubernetesは思想上は、十分に権限が制限されたPodであれば安全にコンテナを実行することができます.

しかし、今回紹介したディレクトリトラバーサル系の脆弱性などは、攻撃者に悪用されてしまうとこの境界を超えてアクセスできる可能性を残してしまいます.また、CVE-2021–25741(ボリュームのSubpathとシンボリックリンクを用いたディレクトリトラバーサル)では、脆弱性の悪用にはコンテナ自身がroot権限で実行されている必要性がありました.特権コンテナの利用は、そのコンテナへの侵入を許した場合問題を深刻にする可能性があります.

OPA/Gatekeeper等を用いたAdmission WebhookでsecurityContextのrunAsUser:0を禁止するなど、root権限を用いたコンテナの実行の禁止をクラスタ全体のポリシーとして導入することを検討してください.

特に、K8Sにおいては現状ではコンテナ内のユーザID空間とグループID空間は、その外のホストと分けられていません.(ユーザ名前空間の活用については積極的に議論はされていますが、導入までにはまだ時間がかかるでしょう) そのため、例えば以下のようなシンプルなDockerfileでもrootで実行されることとなります.

Dockerfileにユーザ指定を追加し、コンテナ内でもrootでの実行を避けましょう.

また、ゼロデイの対策とは趣旨がずれますが、コンテナの実行環境そのものの脆弱性などは、CVE-2021–21334(containerdの同一イメージ実行時に環境変数が混同していた問題)などのようにノードプール側のOSのアップデートが必要であったり、k8sそのもののセキュリティパッチによって解決する場合があります.GKEの場合には、リリースチャンネルを活用し、普段のクラスタの運用のライフサイクルそのものにクラスタのアップデートを必ず含めるようにすることが重要と考えます.危険な脆弱性が来たのでアップデートするというフローでは見落とし等がある可能性もありますので、常日頃からアップデートが継続的に適用されるような環境を作っていく必要があります.

AnthosではBinary Authorization機能を提供しています.特定のレポジトリのみからしかイメージのプルを許さない、あるいはあらかじめ規定したCI/CDパイプライン上でビルド+署名したイメージしか実行をさせないことが可能です.Container Registryに含まれているContainer Analysis機能を用いれば、ベースとなっているイメージに脆弱性が含まれている場合にも検知しそのバージョンのイメージが意図せず実行されてしまうことを防ぐことができます.

特に信用できないイメージの実行をどうしても必要とするケースなどで、コンテナのブレイクアウトやホストカーネルへの影響がある脆弱性を気にする場合にはGKE Sandboxを有効にしたノードプールを用いるのも良いと思います.アプリケーションとホストOSの間にgVisorが入ることにより、カーネルへのアクセスに一段の層が入ることによって、こういった攻撃を困難にします.

粒度の細かいRBACの設定を行う

#悪意のあるK8Sリソースの利用と記載されているCVEは、何らかのK8Sオブジェクトの作成もしくは更新が必要な脆弱性です.適切なRBACの設定によりリスクを小さくすることができます.

内部の攻撃者を想定するだけでなく、Podに与えているサービスアカウントが奪われた際に幅広く権限を許可していると、副次的に権限の昇格の脆弱性が狙われてしまいます.例えば、Podを作成するだけのワークロードのサービスアカウントが奪われた場合に、仮にそれが名前空間内のリソースの作成しか許可していないとしても、CVE-2021–25742(nginx-ingressのServer snippetを用いたサービスアカウントの窃取)を経由してクラスタ全体へのアクセス権を得られてしまう可能性があります.

また、CVE-2021–25740(EndpointSlices, Endpoints APIを用いて、アクセス権のないLBに接続しているサービスを別の名前空間から読み出せる問題)を考慮すると、EndpointやEndpointSliceリソース作成への権限はデフォルトでは持たないような構成にした方が無難と思われます.

単にRBAC的にシークレットが読めないから気をつけなくても良い訳ではなく、許可するものが一つ無駄に多ければ多いほど、将来的な脆弱性の踏み台に使われうるということに気をつけてください.

単純なRBACの設定のみで難しい場合には、OPA/Gatekeeperなども併せて活用すると良いでしょう.Anthos Config ManagementのPolicy Controllerを使えば簡単にクラスタにOPA/Gatekeeperを導入することが可能です.

GKEのクラスタのセキュリティ強化にあたってはこちらの記事が参考になります.RBACに限らず、ネットワークセキュリティやその他の設定等で多層防御に努めることをお勧めします.

強いサービスアカウントを与えているワークロードへの設定権を把握する

CI/CDパイプラインには大きめの権限が与えられがちです.多くの場合、仕方ない範囲ではありますが少なくともどのワークロードに何のアクセス権を与えているかは深く把握する必要がありそうです.特に、Helm等で簡単にデプロイができると、nginx-ingressの例のように知らず知らずのうちにClusterRoleで強い権限を渡していたワークロードができがちです.

どうしても強い権限を渡さなければならない際には、そもそもそのページを外部から認証必須にするなど、二重三重に守るにはどうしたら良いか検討してください.

また、GKEにおいてはGateway APIを用いてロードバランサの設定ができるようになってきました.今までのIngressでは物足りなかった機能面もいくつか増えたほか、クラスタベースで管理する部分と、名前空間ごとに各アプリに任せる部分を別々に定義できるようになりRBACと組み合わせてより管理しやすくなります.こちらも活用を検討してください.

開発者環境の認証情報の取り扱いをさらに注意する

CI/CDパイプラインに関連して、開発者自身が使っている認証情報も大きめの権限が与えられがちです.また、開発時には様々なツールを用いることがあり、開発者によって用いているVSCodeのプラグインが違ったりすることだってあると思います.

開発者の環境に製品環境への全権アクセスの認証情報があったりしないでしょうか.100%騙されない人間というのは存在しないもので、開発者もいつか騙されて意図しないコマンドを実行してしまうかもしれないといった前提に立った方が良いでしょう.

開発用やステージングのクラスタまでしか、アクセス権をできる限り持たずにいる事を心がけたいところです.複数クラスタ存在すると設定が面倒になってRBAC等の設定が疎かになりがちですが、このようなマルチクラスタ環境の設定のベストプラクティスをまとめて提供しているのがAnthos Config Managementですので、このような際にはぜひAnthosのConfig SyncやConfig Controllerなどをご活用ください.

また、GKEではPrivate Clusterを利用することにより、コントロールプレーンにアクセスできるネットワークを絞ることが可能です.ユースケースによっては利用できない場合もあると思いますが、利用可能な場合には攻撃機会そのものを減らすことができ、より堅牢な構成になると考えます.

シークレットとサービスアカウントを保護し、万が一サービスアカウントが漏れた際の被害も小さく設計する

シークレットは攻撃者にとっても狙いやすい対象です.サービスアカウントのトークンと比べ、例えばDBのパスワードやユーザ名などが記録されていることが多く、認証情報としても長生きなものを保持しやすいリソースであるといえます.サービスアカウント類で代用可能な際には、パスワードやトークン等をシークレットに保存するのではなく、サービスアカウントを用いる事を検討してください.

例えば、GKEの認証情報はデフォルトでshort-livedな認証情報をOpenID Connectの仕組みを用いて生成します.このため、万が一認証情報を盗み取られていても、攻撃者がそのユーザそのものとして認証できない限り、その認証情報は短命で更新できません.一方、ワークロード中で用いるサービスアカウントは、デフォルトでは有効期限が設定されておらず、一度盗まれてしまえば無期限に悪用されてしまう可能性があります.K8S 1.20からStableになるService Account Volume Projectionを用いればサービスアカウントのトークンを一定間隔でローテーションすることができます.ローテーションをするサービスアカウントを用いることで、ワークロード中で用いるトークンの有効期限を必要最小限に保つことができ、これは万が一トークンが窃取された際のリスクへの対策として非常に有効です.

どうしてもサービスアカウントで管理できない場合のみシークレットの利用を考えるべきです.例えば、GCPのサービスアカウントのJSONキーをシークレットとして保存して利用することは絶対にお勧めできません.Workload Identityを利用することを検討してください.これはGCPのサービスアカウントのJSONキーが一般的に無期限になりがちで、利用するワークロードがわかりにくくなりやすいことなどから避けるべきことです.

同様に他クラウドの操作をGKE上から行う際なども、OIDCベースで取得したshort-livedな認証情報を用いることを心がけるべきで、シークレットにアクセスキー等を含めるべきではありません.(AWSの例)

それでもシークレットが必要な際には、k8s自体のシークレットに必ずしも頼るのではなく、GCPのSecret Managerなどを用いてシークレットを管理することも一つの選択肢に入れてください.ExternalSecret CRDを用いて、自動的に外部のこういったシークレット管理サービス側にシークレットの管理を任せることにより、よりK8Sと共に利用しやすい形で安全性の高いシークレット管理を利用できます.

最後に

K8Sはユーザ自身で管理しなければならないところがどうしても多くなりがちなシステムです.GKE等のマネージドなシステムを用いたとしても、ユーザ側のセキュリティ対策の努力無くして安全なシステムは成り立たず、一般的な他のマネージドなサービスに比べてユーザ側の責任範囲になるセキュリティの項目も多くなります.

是非、今回の記事をきっかけに関連するCVEに日頃から目を通したり、自分がデプロイしているサードパーティのアプリケーションが内部的にどんな権限を持って、何を行っているか等、一段深く目を通していただくようになっていただければ幸いです.

--

--