Kubernetes ログマウントのリスク #AquaSecurity #DevSecOps #Container #Security #Kubernetes
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
本ブログは「Aqua Security」社の技術ブログで2019年8月1日に公開された「Kubernetes Pod Escape Using Log Mounts」の日本語翻訳です。
ログマウントを使用した侵入
Kubernetesには多くの構成でできており、それらを特定の方法で組み合わせると予期しないセキュリティリスクが生じることがあります。この記事では、rootとして実行され、ノード上の/var/logディレクトリへのマウントポイントを使用して実行されているpodが、ログにアクセスできるすべてのユーザにホストファイルシステムの内容全体にアクセスできる方法を説明します。クラスタでこの問題を軽減するためのオプションについても説明します。
Kubernetesはどのようにログを見るか
kubectl logs < pod_name >でpodからログを取得する方法について疑問に思ったことはありませんか?誰がコンテナからログを取得する責任があるのでしょうか?取得したログはどのようにあなたのマシンに届くのでしょうか?
以下はイメージ図となります。
kubeletは、podが動いているノード上の/var/logディレクトリ内にディレクトリ構造を作成します。podのディレクトリ内に0.logファイル(1) がありますが、これは実際には/var/lib/docker/containerディレクトリ内にあるコンテナログファイルへのシンボリックリンクです。
kubeletは、/var/logディレクトリ(3)内のHTTPファイルサーバを操作する/logs/endpoint(2)を公開し、ログファイルをAPIサーバからの要求でアクセスできるようにします。
hostPathを/var/logにマウントしたpodをデプロイしたらどうなるでしょうか。podは、そのホスト上のすべてのpodログファイルにアクセスできます。それだけで問題が発生する可能性がありますが、説明を続けます。 0.logファイルをシンボリックリンクに置き換えたとしたらどうなるでしょうか。たとえば/etc/shadowを対象にしてみたらどうなるでしょうか。
│
├── var
│ ├── log
│ │ ├── pods
│ │ │ ├── default_mypod_e7869b14-abca-11e8-9888-42010a8e020e
│ │ │ │ ├── mypod
│ │ │ │ │ ├── 0.log -> /etc/shadow
│ │ │ │ │ │
クライアントマシン上でkubectl logsを使ってログ取得を試みます。
$ kubectl logs mypod
failed to get parse function: unsupported log format: "root:*:18033:0:99999:7:::\n"
kubeletはシンボリックリンクをたどり、それが指しているファイル(ノード上の任意のファイル)の内容を読み取ります。
kubectlはjson形式を想定しているため、最初の行の後で失敗しましたが、–-tail=-<line_number>フラグを渡すことで、シャドウファイルの特定の行を簡単に読み取ることができました。
シンボリックリンクはkubeletによって追跡可能となるため、pod内にシンボリックリンクを作成するとノード上の任意のファイルを読み取るためのkubeletのroot権限を利用できます。
侵入
説明を続けます。Kubernetesでpodを実行すると、service accountのtokenがインストールされるので、service accountがログへのアクセスを許可している場合は、kubeletに直接アクセスしてノード上でrootに昇格することができます。
攻撃ベクトルを実証するために検証リストを作成しました。
- /var/log へのマウントポイントを持つpodをデプロイする
- ホスト上のrootフォルダへのシンボリックリンクを作成する
- ホスト上のユーザのSSH秘密鍵を読み取る
次のビデオは、一部pod内でカスタマイズしたコマンドを実行しています。
lsh==ls(ホスト上)
cath==cat(ホスト上)
この検証で使用しているすべてのファイルは、このGitHubリポジトリにあります。さらに同じリポジトリには、ホスト上で秘密鍵とKubernetesのservice accountのtokenを自動的に抽出するスクリプトがあります。
ディレクトリをマウントすると時にリスクとなる
これはKubernetesの脆弱性でしょうか、それとも単に悪い設定例なのでしょうか。
書き込み可能なhostPathを持つpodを/var/logにデプロイすることはめったにありません(機密性の高いホストディレクトリをpodにマウントすることを悪用する他の方法があります)。しかし、/var/logのマウントがリスクが高いことであることを知っていたとしても、おそらくそれが簡単にノードの乗っ取りへとつながる可能性があるとは思わなかったでしょう。
この記事を公開する前に、Aqua社のKubernetesセキュリティチームにこの問題を提起し、彼らがそれを脆弱性と見なしているかどうかを確認しました。彼らはそれが書き込み可能なホストディレクトリをマウントしたことによる単なる不幸な結果であると結論付けました。このリスクは文書化されています。とはいえ、これは悪用するのが比較的簡単です。現在このマウントを使用する多くのプロジェクトがあります。これらのプロジェクトのいずれかを使用している場合は、デプロイしたpodがホストの乗っ取りにつながるリスクがあることに注意してください。
このメソッドはKubernetes 1.15と1.13でテストされていますが、他のバージョンにも影響を与える可能性があります。
緩和策
この方法は、podがrootとして実行されている場合にのみ可能です。一般に、これは避けるべきであり、コンテナがrootで実行されないように、またはrootが必要とされる特定のグループのイメージのみをホワイトリスト化するためにAqua CSPでポリシーを設定するのは簡単です。
もう1つの対応策は、書き込み可能なhostPathを持つpodを/var/logに展開しないことです。これはデフォルトの設定や一般的な方法ではないため、慎重に定義する必要がありますが、それでも可能性はあります。しかし、どうやってそれをチェックしますか?
Kubernetes用のオープンソース負荷テストツールであるkube-hunterでpodがこのマウントを実行しているか否か確認することができます。
Aquaを利用している場合、RuntimePolicies(Aquaの機能の一つ)を使用して/var/logパスからボリュームマウントをブラックリストに登録することで、この種のリスクを防ぐこともできます。
まとめ
Kubernetesは、複雑なシステムであり、セキュリティリスクが一般的なユーザだけでなく、専門家でさえも常に明確であるとは言えません。特定の状況下では、潜在的に悪用につながる可能性があることを示しました。ほとんどの場合、これは不可能ですが、Kubernetesを利用している場合ユーザはセキュリティ全体の状態に影響を与える可能性がある多くのことを実行できます。このことを理解し、そのような間違いを防ぐために適切な管理策を講じることが重要です。