LoginSignup
4
3

OWASP Kubernetes Top 10 を理解して Kubernetes クラスタを安全に運用しよう

Last updated at Posted at 2023-12-22

OWASP Kubernetes Top 10 とは

OWASP Kubernetes Top 10 は OWASP (Open Web Application Security Project, オワスプと発音) というソフトウェアのセキュリティを向上させることを目的とした団体が公開している、Kubernetes クラスタに対するセキュリティリスクの中で危険性が高いと判断された10項目をまとめたセキュリティレポートです。 レポートは定期的に更新されるようになっていて 2022 年版が最新となっています。Web アプリケーション向けのセキュリティレポートである OWASP Top 10 の方が有名かもしれません。

Kubernetes クラスタの運用方法を誤ると、アプリケーションやインフラで新たなリスクが発生する可能性がありますが、OWASP Kubernetes Top 10 でリスクと共に紹介されている防御策に従うことで、それらのリスクを突いた攻撃を防ぐことができます。

それでは、レポートに記載されている10個の項目を紹介していきます。各項目の説明は筆者なりの解釈で説明と補足をしているので、オリジナルの内容を知りたい方は公式ページをご参照ください。

K01: Insecure Workload Configurations (安全でないワークロードの設定)

Pod の権限やアクセス制御を定義する securityContext の設定ミスは、ワークロードとクラスタ全体の安全性に大きな影響を与えるため、定期的な監査と改善が重要になります。その中でも影響の大きいものとして以下があります。

アプリケーションプロセスは root で実行すべきではない

コンテナ内のプロセスを root で実行することは多くの環境でよく見られる設定ミスです。要件によって root 実行が必須な場合もありますが可能な限り避けるべきです。root で実行されたコンテナに侵入された場合、攻撃者が root レベルの権限を持つことになるので、一般ユーザーが許可されていないような悪意のあるプロセスを実行されるなど様々なオペレーションを許してしまうことになります。

apiVersion: v1  
kind: Pod  
metadata:  
  name: root-user
spec:  
  containers:
  ...
  securityContext:  
    # root ユーザーで実行
    runAsUser: 0
    # 非 root ユーザーで実行
    runAsUser: 5554

読み取り専用のファイルシステムを使用すべき

侵害されてしまったコンテナからの攻撃を制限するために、可能な限り読み取り専用のファイルシステムを使用することが推奨されています。読み取り専用のファイルシステムは Container Breakout を防ぐための重要な要素です。Container Breakout は侵入したコンテナ内から他コンテナや実行ホストに対して任意の処理を実行する攻撃で Container Escape と呼ばれることもあります。

apiVersion: v1  
kind: Pod  
metadata:  
  name: read-only-fs
spec:  
  containers:  

  securityContext:  
    # コンテナのファイルシステムを読み取り専用でマウント
    readOnlyRootFilesystem: true

特権コンテナは使用すべきではない

通常のコンテナは root で実行したとしてもコンテナランタイムがコンテナに付与する機能 (Linux Capabilities) を制限しますが、特権コンテナの場合はそれらの制限がされず、結果的に実行ホストの root と同等の権限を持つことになるため、侵害された場合に壊滅的な攻撃を受ける可能性があります。

apiVersion: v1  
kind: Pod  
metadata:  
  name: privileged-pod
spec:  
  containers:  
  ...
  securityContext:  
    # 特権コンテナとして実行 
    privileged: true
    # 非特権コンテナとして実行
    privileged: false

⭐️ 対応策

これらの誤設定を防ぐには CI でのソースコードレベルの検知 (例: conftest) と、Kubernetes クラスタにマニフェストを適用する際のバリデーション (例: Pod Security AdmissionGatekeeper) を導入するのが良いでしょう。

😈 攻撃例

公式での記載なし。

K02: Supply Chain Vulnerabilities (サプライチェーンの脆弱性)

コンテナイメージは数多くのサードパーティー製のソフトウェアに依存する可能性が高く、依存関係にあるソフトウェアの信頼性を確立することが非常に難しくなっています。このような依存関係を構成する要素群の連鎖は「(ソフトウェア) サプライチェーン」と呼ばれ、サプライチェーンの信頼性を高めるために以下が課題になってきます。

イメージの完全性

コンテナイメージの依存関係にあるソフトウェアに悪意のある変更がされていないことを保証することが重要です。近年に発生した関連事件としては SolarWinds 事件著名な npm パッケージへのマルウェア混入 などが挙げられます。

イメージの構成

アタックサーフェスを減らすためにコンテナイメージに余計なソフトウェアを含めないようにするのがベターです。

既知の脆弱性

依存関係にあるソフトウェアに既知の脆弱性が含まれる場合は、本番環境などで実行しないようにしてリスクを軽減するのが良いでしょう。

⭐️ 対応策

対応策としては以下があります。

イメージの完全性の担保

イメージをビルドする際に in-toto Attestation を利用して依存関係にあるソフトウェアが "誰" によって "どんな手順" で生成されたものかを検証するなどして、SLSA レベルが高いビルドパイプラインを用意しましょう。SLSA (Supply-chain Levels for Software Artifact) はサプライチェーンの安全性を確立するためのセキュリティガイドラインなので、気になる方はどういったものかを調べてみるのが良いでしょう。

SBOM (Software Bill of Materials) の活用

イメージがどういったもので構成されているかを示す SBOM を提供してセキュリティチェックに活用しましょう。SBOM の標準的なフォーマットとしては OWASP 製の CycloneDX と Linux Foundation 製の SPDX が挙げられます。

イメージへの署名

サプライチェーンの各ステップで成果物への署名と検証を行い、成果物自体の改ざんを検出しましょう。コンテナイメージの検証を目的とした OSS としては Cosign が挙げられます。

イメージの構成の改善

アタックサーフェスを減らすために、イメージはワークロードの実行に必要最低限のもので構成しましょう。DistrolessScratch をベースイメージとして使用することでアタックサーフェスを減らせるだけでなく、脆弱性スキャンのノイズを大幅に減らしたり、イメージサイズの削減や CI/CD パイプラインでのビルド時間の短縮など様々なメリットがあります。ベースイメージに最新のセキュリティパッチが当たっているかの確認も重要です。また、Docker Slim のようなツールでイメージを最適化するのも良いでしょう。

既知の脆弱性の検知

イメージに含まれる既知の脆弱性を検知しましょう。脆弱性を分析する OSS としては ClairTrivy などが挙げられます。

ポリシーの適用

Kubernetes の Admission ControllerOpen Policy Agent のようなポリシーエンジン、Kyverno などを利用して、ポリシーを満たしていないイメージがクラスタで実行されることを防ぎましょう。ポリシーの例を挙げると、脆弱性スキャンがされている、許可されたベースイメージが使用されている、承認された SBOM を含んでいる、信頼したイメージレジストリから作成されたもの、など様々なのでセキュリティ要件によって独自に設計するのが良いでしょう。

😈 攻撃例

CI/CD パイプラインへの攻撃

通常の運用だと、CI/CD パイプラインでコンテナイメージのビルドとイメージレジストリへのアップロードが自動化された状態で、Kubernetes クラスタでマニフェストに定義されたイメージを元にコンテナが実行されますが、ビルドツールに侵入されてサービス用のイメージに悪意のあるソフトウェアが仕込まれた場合、そのソフトウェアが Kubernetes クラスタで実行されることになり、結果的に暗号資産のマイニングツールがインストールされたり、バックドアを仕込まれるようなケースがあります。

K03: Overly Permissive RBAC Configurations (過剰な権限を持った RBAC)

Kubernetes ではロールベースでアクセス制御を行う RBAC が認可の仕組みとして提供されていますが、過剰に権限を付与したりなど不適切な設定がされていると Kubernetes クラスタにとって大きなリスクになり、侵害された場合の Blast Radius (攻撃を受けた場合の影響範囲) が拡大する可能性があります。設定ミスの例として以下のようなものがあります。

cluster-admin の不要な使用

サービスアカウント、ユーザー、グループに cluster-admin 権限が付与されている場合、それらのクライアントはクラスタ内の任意のリソースに対して任意のアクションを実行できるため、侵害された場合のリスクがとても大きくなります。Kubernetes 関連の OSS ではサンプルマニフェストが提供されていることが多いですが、適切な RBAC が設定されていない可能性もあるので、社内環境に適用する際には内容を確認することが重要です。

LIST 権限の不要な使用

Kubernetes API の LIST レスポンスにはリソースの名前だけでなく全ての情報が含まれるため、GET 権限を付与していない場合でも LIST 権限が付与されているとリソースを GET できてしまいます。これを避けるために GET 権限を持たせていないロールに LIST 権限を付与してはいけません。

WATCH 権限の不要な使用

Kubernetes API の WATCH レスポンスには更新差分だけでなく全てのリソース情報が含まれます。WATCH 権限を持つクライアントは API から特定のリソースの GET や LIST は不可能ですが、WATCH 操作で更新があった場合は全てのリソースを GET できてしまいます。これを避けるために GET/LIST 権限を持たせていないロールに WATCH 権限を付与してはいけません。

⭐️ 対応策

対応策としては以下があります。

最小特権の原則に従う

RBAC の設定を悪用されるリスクを減らすためには、設定を継続的に分析して最小特権の原則に常に従うことが重要です。他にも以下の運用を推奨します。

  • クラスタ利用者によるクラスタへのアクセスを可能な限り減らす
  • クラスタ外部でサービスアカウントトークンを使用しない
  • デフォルトのサービスアカウントトークンの使用を避ける
  • サードパーティ製のマニフェストに含まれる RBAC を監査する
  • リスクの高い RBAC パーミッションを検出してブロックする仕組みを導入する
  • Cluster スコープの RBAC ではなく、Namespace スコープの RoleBinding を活用する
  • Kubernetes が公式に公開している RBAC Good Practices に従う

必要な場合のみ LIST 権限を付与する

全てのリソースの GET を許可する場合のみ LIST 権限を付与しましょう。

必要な場合のみ WATCH 権限を付与する

全てのリソースの GET/LIST を許可する場合のみ WATCH 権限を付与しましょう。

😈 攻撃例

cluster-admin の不要な使用

OSS のクラスタ観測ツールがプラットフォームエンジニアリングチームによって Kubernetes クラスタにインストールされたと仮定します。そのツールはトラフィックのデバッグや分析のために Web UI が含まれていますが (実行中のコンテナ上でシェルを実行できる)、マニフェストの誤設定によってインターネットに公開されてしまいました。

このツールには以下のように過剰な権限を付与している RBAC が設定されていたため、攻撃者に Web UI 経由で実行中のコンテナに侵入されて、結果的に admin 権限で Kubernetes API を呼び出して kube-system ネームスペース内の Secret が参照されて権限昇格されてしまいました。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: default-sa-namespace-admin
  namespace: prd
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:serviceaccount:prd:default

LIST 権限の不要な使用

ここでは、GET 権限を持っていないにも関わらず LIST 権限を付与したことで Secret を GET されてしまう攻撃例を紹介します。

default ネームスペースの Secret を LIST できるサービスアカウント only-list-secrets-sa を作成します。

$ kubectl create serviceaccount only-list-secrets-sa
$ kubectl create role only-list-secrets-role --verb=list --resource=secrets
$ kubectl create rolebinding only-list-secrets-default-ns \
  --role=only-list-secrets-role --serviceaccount=default:only-list-secrets-sa

Kubernetes API にアクセスするためにローカルにプロキシを起動します。

$ kubectl proxy &

適当な Secret を作成します。

$ kubectl create secret generic abc --from-literal=secretAuthToken=verySecure123

only-list-secrets-sa の権限では Secret を GET できないことを確認します。

$ curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets/abc \
  -H "Authorization: Bearer $(kubectl -n default get secrets -ojson | jq '.items[]| select(.metadata.annotations."kubernetes.io/service-account.name"=="only-list-secrets-sa")| \
  .data.token' | tr -d '"' | base64 -d)"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
  },
  "status": "Failure",
  "message": "secrets \"abc\" is forbidden: User \"system:serviceaccount:default:only-list-secrets-sa\" cannot get resource \"secrets\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "abc",
    "kind": "secrets"
  },
  "code": 403
}

しかし、default ネームスペースの Secret を LIST すると、GET 権限を持っていないにも関わらず Secret を GET できてしまいます。

$ curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets?limit=500 -H \
  "Authorization: Bearer $(kubectl -n default get secrets -ojson | jq '.items[]| select(.metadata.annotations."kubernetes.io/service-account.name"=="only-list-secrets-sa")| \
  .data.token' | tr -d '"' | base64 -d)"
{
  "kind": "SecretList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/namespaces/default/secrets",
    "resourceVersion": "17718246"
  },
  "items": [
  REDACTED : REDACTED
  ]
}

検証を終えたら以下のコマンドで後処理をしてください。

$ kubectl delete serviceaccount only-list-secrets-sa
$ kubectl delete role only-list-secrets-role 
$ kubectl delete rolebinding only-list-secrets-default-ns 
$ kubectl delete secret abc
$ kill "%$(jobs | grep "kubectl proxy" | cut -d [ -f 2| cut -d ] -f 1)"

WATCH 権限の不要な使用

ここでは、GET/LIST 権限を持っていないにも関わらず WATCH 権限を付与したことで Secret を GET/LIST できてしまう例を紹介します。

default ネームスペースの Secret を WATCH できるサービスアカウント only-watch-secrets-sa を作成します。

$ kubectl create serviceaccount only-watch-secrets-sa
$ kubectl create role only-watch-secrets-role --verb=watch --resource=secrets
$ kubectl create rolebinding only-watch-secrets-default-ns --role=only-watch-secrets-role --serviceaccount=default:only-watch-secrets-sa

Kubernetes API にアクセスするためにローカルにプロキシを起動します。

$ kubectl proxy &

適当な Secret を作成します。

$ kubectl create secret generic abcd --from-literal=secretPassword=verySecure

only-watch-secrets-sa の権限では Secret を GET/LIST できないことを確認します。

$ curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets/abcd \
  -H "Authorization: Bearer $(kubectl -n default get secrets -ojson | jq '.items[]| select(.metadata.annotations."kubernetes.io/service-account.name"=="only-watch-secrets-sa")| \
  .data.token' | tr -d '"' | base64 -d)"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
  },
  "status": "Failure",
  "message": "secrets \"abc\" is forbidden: User \"system:serviceaccount:default:only-watch-secrets-sa\" cannot get resource \"secrets\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "abcd",
    "kind": "secrets"
  },
  "code": 403
}

$ curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets?limit=500 \
  -H "Authorization: Bearer $(kubectl -n default get secrets -ojson | jq '.items[]| select(.metadata.annotations."kubernetes.io/service-account.name"=="only-watch-secrets-sa")| \
  .data.token' | tr -d '"' | base64 -d)"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "secrets is forbidden: User \"system:serviceaccount:default:only-watch-secrets-sa\" cannot list resource \"secrets\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "kind": "secrets"
  },
  "code": 403
}

しかし、default ネームスペースの Secret を WATCH すると、GET/LIST 権限を持っていないにも関わらず Secret を GET/LIST できてしまいます。

$ curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets?watch=true \
  -H "Authorization: Bearer $(kubectl -n default get secrets -ojson | jq '.items[]| select(.metadata.annotations."kubernetes.io/service-account.name"=="only-watch-secrets-sa")| \
  .data.token' | tr -d '"' | base64 -d)"

{
  "type": "ADDED",
  "object": {
    "kind": "Secret",
    "apiVersion": "v1",
    "metadata": {
      "name": "abcd",
      "namespace": "default",
      "selfLink": "/api/v1/namespaces/default/secrets/abcd",
      "uid": "725c84ee-8dc7-41ef-a03e-193225e228b2",
      "resourceVersion": "1903164",
      "creationTimestamp": "2022-09-09T13:39:43Z",
      "managedFields": [
        {
          "manager": "kubectl-create",
          "operation": "Update",
          "apiVersion": "v1",
          "time": "2022-09-09T13:39:43Z",
          "fieldsType": "FieldsV1",
          "fieldsV1": {
            "f:data": {
              ".": {},
              "f:secretPassword": {}
            },
            "f:type": {}
          }
        }
      ]
    },
    "data": {
      "secretPassword": "dmVyeVNlY3VyZQ=="
    },
    "type": "Opaque"
  }
}
REDACTED OTHER SECRETS

$ echo "dmVyeVNlY3VyZQ==" | base64 -d
verySecure

検証を終えたら以下のコマンドで後処理をしてください。

$ kubectl delete serviceaccount only-watch-secrets-sa
$ kubectl delete role only-watch-secrets-role 
$ kubectl delete rolebinding only-watch-secrets-default-ns --role=only-list-secrets-role --serviceaccount=default:only-list-secrets-sa
$ kubectl delete secret abcd
$ kill "%$(jobs | grep "kubectl proxy" | cut -d [ -f 2| cut -d ] -f 1)"

K04: Lack of Centralized Policy Enforcement (中央管理されていないポリシー)

マルチクラスタ/マルチクラウドのような異種環境全体にセキュリティポリシーを適用するのは難しい作業です。設定ミスの検出、修正、防止を一元管理できていなければクラスタが侵害される可能性があります。

一貫性のあるセキュリティを敷いていくためには、ソフトウェアのデリバリーライフサイクルにおけるビルドやデプロイなど様々な段階でポリシーを適用していく必要があります。これらの複数の段階でポリシーを適用することで、組織は環境全体のワークロードに対してガバナンス、コンプライアンス、セキュリティの要件を適用できるようになります。

⭐️ 対応策

ワークロードに設定ミスがあることを検出するだけでは不十分で、セキュリティポリシーを満たしていないワークロードがクラスタにデプロイされることをブロックすることが重要です。これを実現するためには Kubernetes の標準機能である Pod Security Admission を利用するか、OSS でも GatekeeperKyvernoKubewarden など似た仕組みが公開されているので要件に合わせて採用するのが良いでしょう。

😈 攻撃例

Container Breakout

以下のコマンドを対応策が敷かれていないクラスタに対して実行すると、どんな悪さも働ける特権的なコンテナをクラスタで起動できてしまいます。hostPID を true にすることでコンテナの Namespace の分離が解除されて、ホスト上にいるかのようにプロセスを参照できるようになり、nsenter コマンドで pid 1 が実行されている異なる Mount Namespace でシェルが起動します。privileged も true になっているので権限によって操作が制限されることもなく、悪意を持った操作がなんでもできる状態になります。

$ kubectl run r00t --restart=Never -ti --rm --image lol \
  --overrides '{"spec":{"hostPID": true, 
  "containers":[{"name":"1","image":"alpine", 
  "command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"], 
  "stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent", 
  "securityContext":{"privileged":true}}]}}' \
  /

K05: Inadequate Logging and Monitoring (不十分なロギングと監視)

Kubernetes を構成するコンポーネントには様々なレベルでログを作成する機能があります。これらのログの収集や監視がされていない場合、悪意を持った攻撃が発見されない可能性があり、仮に発見できたとしてもインシデントの調査と対応作業が難しくなるため、以下のような運用は避けるべきです。

  • 認証の失敗、Secret へのアクセス、手動でのリソースの削除と更新などをロギングしていない
  • 実行中のワークロードのログとトレーシングから怪しい挙動を監視していない
  • アラートが適切に設定されていない
  • ログが一元管理されておらず改ざんが防止されていない
  • ロギングインフラが完全に無効になっている

⭐️ 対応策

以下のログリソースを有効にして適切な設定をすると良いでしょう。

Kubernetes の監査ログ

Audit logging は API 実行を始めとしたクラスタで発生した様々な操作をログとして記録する機能です。記録された情報は監査に役立つため、こちらを有効にして異常な API 実行を監視するようにしましょう。特に認証の失敗は攻撃者が認証を不正に試みている可能性があるため注意が必要です。

Kubernetes の Events

Kubernetes の Event から Resource Quota の超過や Pending 中の Pod などのリソースの状態変化やエラーなどを把握することができます。Event の概要や管理方法に関しては CNCF のブログ "Extracting value from the Kubernetes events feed" が参考になります。

アプリケーションとコンテナのログ

アプリケーションとコンテナのログはセキュリティ観点からも有用です。Kubernetes で実行するアプリケーションは、ログをファイルに書き出すのではなく、標準出力と標準エラー出力に書き出して、Kubrnetes のロギングの仕組みを利用するのが一般的です。

OS のログ

Kubernetes ノードの OS と systemd デーモンのログも有用です。

Cloud Provider のログ

AWS、Azure、GCP などの Managed Kubernetes サービスを利用している場合、Cloud Provider 固有の認証サービスなどのログも参照することができます。一例として AWS では Authenticator という機能が利用できます。

ネットワークのログ

ネットワークのログは Kubernetes 内の多くのレイヤーで取得でき、詳細な調査をする際に有用です。eBPF 関連のプロジェクトでは、セキュリティ観点でのクラスタのオブザーバビリティを強化するものが OSS として公開されているので、気になる人は調べてみるのが良いでしょう。

😈 攻撃例

以下が記載されていましたが、詳細な記載はなし。

  • 削除イベントの異常数
  • サービスアカウントトークンの漏洩

K06: Broken Authentication Mechanisms (壊れた認証の仕組み)

Kubernetes の認証は柔軟性が高いため様々な環境で効果的に機能しますが、クラスタとクラウドのセキュリティ態勢に関しては課題もあります。Kubernetes の API にアクセスするにはいくつかのエンティティが認証を必要としていて、リクエストが認証できない場合は HTTP ステータス 401 で返されます。認証を行う主体としては人間とサービスアカウントが存在しますが、認証の運用方法を誤るとクラスタが侵害されるリスクが大きくなるため注意が必要です。

⭐️ 対応策

認証の運用方法としては以下が有効です。

エンドユーザーによる認証で証明書認証を使用しない

現時点ではクライアント証明書を失効させる機能が Kubernetes の API に実装されていないため、秘密鍵の漏洩があった場合は証明書チェーンを作り直すなど対応がとても大変です。また、署名や配布なども面倒な作業なので、エンドユーザーによる認証で証明書認証は使用しない方が良いでしょう。

独自の認証メカニズムを使用しない

必要でない場合、広く採用されている認証メカニズムを使用するのが良いでしょう。

可能な限り多要素認証を強制する

どのような認証メカニズムが採用されたとしても、人間相手には多要素認証 (通常は OIDC の一部) を強制しましょう。

クラスタ外からサービスアカウントトークンを使用しない

クラスタ内でサービスアカウントトークンを使用する場合、TokenRequest API を使用してトークンを発行して、Projected Volume を使用して Pod にマウントされます。クラスタ外で使用する場合、トークンは Secret を介して手動でプロビジョニングする必要があり、有効期限が設定されていません。

クラスタ外から有効期限の長いトークンを使用することはクラスタにとって大きなリスクになるため、トークンベースの認証が必要な場合は TokenRequest API を使用するか、--duration オプションを付けた kubectl create token コマンドを使用して短命のトークンを発行しましょう。

人間と外部サービスとの認証には短命のトークンを使用する

認証トークンは可能な限り短命のものを使用するのが良いでしょう。こうすることで、トークンが流出した際の被害を小さくできる可能性があります。

😈 攻撃例

GitHub に認証情報をプッシュしてしまうアクシデント

ある開発者が誤って自身のノート PC から認証情報が含まれる .kubeconfig ファイルを GitHub にプッシュしてしまい、それが攻撃者に見つかり (不幸なことにインターネットに公開されていた) Kubernetes クラスタにアクセスされてしまいました。クラスタは証明書を使用して認証するよう設定されていたため、流出したファイルには認証に必要な情報がすべて含まれていました。

K07: Missing Network Segmentation Controls (ネットワークセグメンテーション管理の欠如)

マイクロサービスやマルチテナントで Kubernetes を運用する場合、ネットワークトラフィックの制御は重要な懸念事項になります。Kubernetes のネットワークはデフォルトだとフラットなので、どのワークロードも別のワークロードに通信できてしまいます。そのため、特定のワークロードが侵害された場合、攻撃者に内部ネットワークを調査されて別のネットワークに横断されたり、プライベートな API を呼び出されてしまいます。

⭐️ 対応策

Kubernetes 内のネットワークセグメンテーションの目標は、コンテナが侵害された場合の Blast Radius を最小化して、有効なトラフィックが期待通りにルーティングされるようにしながらラテラルムーブメント (攻撃者によるネットワークの横移動) を防止することです。有効な対応策としては以下のようなものがあります。

マルチクラスター構成

クラスタ分離が適切な場合はマルチクラスター構成を組むことが有効です。密度の高いマイクロサービスを構成している場合は複雑さが増すことになりますが、リスクを減らすためにテナントを分離することは有効な手段です。

NetworkPolicy

Kubernetes では Pod に対して Pod 間の通信や外部のエンドポイントへの通信を制御する NetworkPolicy という機能が提供されています。この機能を利用してクラスタ内の通信を制御することは有効です。

Service Mesh

Istio、Linkerd、Consul などの Service Mesh プロジェクトでは、クラスタ内のネットワークトラフィックを制御する方法が提供されているので、Service Mesh を導入している場合はそういった組み込みの機能を利用するのが良いでしょう。

CNI プラグイン

Container Network Interface (CNI) はネットワークリソースへのアクセスを設定するためのオープンな仕様です。CNI は Kubernetes 内でのワークロードのネットワーキングを制御するためのメカニズムで、サポートされているプラグインは多岐にわたります。

Project CalicoCilium などではネットワークトラフィックを分離するための様々な機能が提供されているので、要件に応じて採用を検討するのも良いでしょう。なお CNI を使用する際には、セキュリティ観点の要件とプラグインを使用した場合のリソースのオーバーヘッドやメンテナンス性を整理することが重要です。

😈 攻撃例

Pod に侵入した後で内部ネットワークの API を実行

ネットワークのセグメンテーションがされていないクラスタにある WordPress の Pod に侵入されて、攻撃者により dig や curl といった組み込みのコマンドで (ベースイメージが Ubuntu というのも被害を広げている) ネットワークを探索されてしまい、結果的に内部的にアクセスが可能な Redis が見つかり、API 経由でデータが盗まれてしまいました。

他の例として、Cloud Provider の Managed Kubernetes で作成したクラスタの Pod に侵入されると、Metadata Service (169.254.169.254) からノードをクラスタに追加するために必要な認証情報を盗まれてしまい、そのノードとして振る舞われて権限昇格のために別の認証情報が盗まれる可能性もあります。

K08: Secrets Management Failures (シークレット管理の失敗)

Kubernetes ではパスワードやトークンなどの機密情報を Secret に保存して使用しますが、値は Base64 エンコードされているだけで暗号化はされていないので、マニフェストファイルをバージョン管理システムなどで管理するのは非常に危険です。管理方法とアクセス制御の取り扱いには細心の注意を払いましょう。

⭐️ 対応策

Secret の流出を防ぐために以下のような方法があります。

etcd に保存された Secret を暗号化する

etcd には Kubernetes の API 経由でアクセス可能な様々な情報が含まれているため、クラスタが攻撃を受けた際にクラスタの状態を広くチェックされてしまう可能性があります。そのためバックアップの暗号化はもちろんのこと、可能であればフルディスクの暗号化を検討するのが良いでしょう。

また、Kubernetes ではベータ機能として encryption at rest という機能が提供されています。この機能を利用することで etcd 内の Secret リソースを暗号化できるので、第三者に内容を参照されることを防ぐことができます。

クラスタ全体でセキュリティ観点で適切な設定をする

機密情報を安全に保護し続けるためには、クラスタ全体でセキュリティ観点での適切な設定 (他の項目で紹介している対応策) をすることが重要です。アプリケーションに侵入されると機密情報が漏洩するリスクが高まります。RBAC 設定も最小特権の原則に従うのが基本ですが、特に Secret の参照権限の扱いには十分注意しましょう。

ロギングと監査を確実に実施する

Kubernetes の Audit Logging 機能を有効にして、Secret への悪意のある操作や異常な操作を検知できるようにしましょう。

😈 攻撃例

暗号化されていない Secret の参照

クラスタ内のコンテナに侵入されて /var/run/secrets/kubernetes.io/serviceaccount に配置されているサービスアカウントトークンが奪取されると、攻撃者にそのサービスアカウントとして振る舞うことを許してしまい、RBAC が適切に設定されていない、かつ Secret が暗号化されていない場合は Secret が参照されてしまいます。

K09: Misconfigured Cluster Components (コンポーネントの設定ミス)

Kubernetes クラスタは様々なコンポーネントで構成されていますが、以下のようなコンポーネントでの設定ミスがあると、クラスタが攻撃を受けるリスクが高まります。

  • kubelet が --anonymous-auth=true オプションで実行されていて認証なしのリクエストが許可されている
  • kubelet が --authorization-mode=AlwaysAllow オプションで実行されていて認可なしのリクエストが許可されている
  • etcd に安全でない設定がされている
  • kube-apiserver がインターネットに公開されている (過去には90万以上のクラスタがインターネットに公開されていたというニュースもあった)

⭐️ 対応策

コンポーネントの誤設定を検知する CIS Benchmark の利用を推奨します。さらに Kubernetes の設定を Infrastructure as Code で一元管理することでクラスタを保守しやすくなります。

😈 攻撃例

公式での記載なし。

K10: Outdated and Vulnerable Kubernetes Components (脆弱性が含まれる古い K8s コンポーネント)

Kubernetes だけでなく ArgoCD や Istio など他のエコシステムでも脆弱性が出てくる可能性があるため、クラスタ運用者は適切に対応していく必要があります。近年見つかった脆弱性としては以下のようなものがあります。

  • ArgoCD: 攻撃者が悪意のある Helm チャートをロードして機密情報へのアクセスを可能にする脆弱性 (CVE-2022-24348 )
  • Kubernetes (ingress-nginx): Ingress オブジェクトを作成更新する権限を持つユーザーがクラスタ内の全ての Secret を取得できてしまう脆弱性、単にバージョンアップするだけでは対処できず対応が難しいものだった (CVE-2021-25742)
  • Istio: 認証がバイパスされて保護されたパスへの不正アクセスされてしまう脆弱性 (CVE-2020-8595)

また、複数のクラスタのセキュリティを担保するためには、各クラスタの構成を正確に把握できる仕組みを用意した上で、Kubernetes が公式にサポートしているバージョンに追随していくことが重要です。

⭐️ 対応策

Kubernetes クラスタ内で実行されるサードパーティ製のコンポーネントは非常に多いため、以下のように多方面から対応していくことが重要です。

既存の脆弱性の検知

Kubernetes と関連コンポーネントの脆弱性スキャンは実施するようにしましょう。また、Gatekeeper のようなツールを利用することで、クラスタ内の脆弱なコンポーネントを発見するためのポリシーを作ることができます。

依存関係を最小化する

依存関係を最小化することが重要です。サードパーティ製のソフトウェアを導入する必要がある場合には、過度な RBAC が設定されていないか、脆弱性が含まれていないかなど、ちゃんと監査するようにしましょう。

😈 攻撃例

先述したような脆弱性を悪用されると、クラスタ全体が危険にさらされる可能性があります。

さいごに

この記事では OWASP Kubernetes Top 10 を紹介させてもらいました。こちらで挙げられている Kubernetes を利用する際のセキュリティリスクを理解した上で、安全にクラスタを運用していきましょう。すべてのリスクに対して対応策を敷くことが難しくても、1つでも多く実施するのに越したことはないかと思います。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3