PodSecurityPolicy について考えてみた

Shohei Masuyama

2022.10.24

話すこと

案件で Kubernetes のセキュリティについて調べることがあったので、各レイヤで何が必要かを検討しました。

  • Node レイヤ
    ・Inspector
    ・(falco)
  • Pod レイヤ
    ・(falco)
    ・セキュリティポリシー
  • Network レイヤ
    ・Egress Security

その中で今回は、Pod のセキュリティポリシーについてお話していきたいと思います。

前提(話さないこと)

コンテナイメージが CI 上で脆弱性対策されていること。
(例えば https://github.com/aquasecurity/trivy など)

検討したこと

  • Security Policy
    クラスタでどうやって Pod セキュリティを合わせるか
    Policy-as-Code(以下、PaC) ツールの比較
  • SecurityContext
    PaC ツールを利用した上でルールをどうするか
  • 振る舞い検知
    対策した上で実際になにか起きた場合、どうやって検知するか

PaC の比較

対象にしたものは、下記の3つで今回の比較の結果 Kyverno を採用したいと考えました。

  • OPA/Gatekeeper
  • PodSecurityAdmission(以下、PSA)
  • Kyverno

期待することは下記の通りです。

  • Pod Security Standards(以下、PSS) に則り、導入/運用しやすいこと 追加でポリシーをできること

Kubernetes のコミュニティ(https://github.com/kubernetes/community/blob/master/sig-auth/README.md)によって推奨されている、Podの設定になります。
PSSは種別ごとに用意されており、必要に応じて使い分ける必要があります。

# 特権 - 制限のかかっていないポリシーで、可能な限り幅広い権限を提供します。このポリシーは既知の特権昇格を認めます。
特権ポリシーは意図的に開放されていて、完全に制限がかけられていません。この種のポリシーは通常、特権ユーザーまたは信頼されたユーザーが管理する、システムまたはインフラレベルのワークロードに対して適用されることを意図しています。
特権ポリシーは制限がないことと定義されます。gatekeeperのようにデフォルトで許可される仕組みでは、特権プロファイルはポリシーを設定せず、何も制限を適用しないことにあたります。 一方で、Pod Security Policyのようにデフォルトで拒否される仕組みでは、特権ポリシーでは全ての制限を無効化してコントロールできるようにする必要があります。

# ベースライン、デフォルト - 制限は最小限にされたポリシーですが、既知の特権昇格を防止します。デフォルト(最小の指定)のPod設定を許容します。
ベースライン、デフォルトのプロファイルは一般的なコンテナ化されたランタイムに適用しやすく、かつ既知の特権昇格を防ぐことを意図しています。 このポリシーはクリティカルではないアプリケーションの運用者または開発者を対象にしています。 次の項目は強制、または無効化すべきです。

# 制限 - 厳しく制限されたポリシーで、Podを強化するための現在のベストプラクティスに沿っています。
制限ポリシーはいくらかの互換性を犠牲にして、Podを強化するためのベストプラクティスを強制することを意図しています。 セキュリティ上クリティカルなアプリケーションの運用者や開発者、また信頼度の低いユーザーも対象にしています。 下記の項目を強制、無効化すべきです。

  • PSA はできない
  • validateではなく、監査のみができること
    どれもできる
  • (mutate 機能があること)
    優先度は低
    PSA のみない

OPA/Gatekeeper

特徴

  • Kubernetes 以外のリソースでも利用可能
  • conftest と合わせて CI に組み込める
  • 学習コストが高い (Rego)

Kubernetes を目的とし、学習コストが高いため見送りとしました。

PSA

特徴

  • 導入しやすい
  • 1.22 からのみ利用可能
  • audit を利用した場合、監査ログを取り込む
  • カスタムポリシーができない
  • フィルタリングが弱い
    ・Usernames: 認証されていない(あるいは偽装された)ユーザー名を持つユーザーからの要求は無視されます。
    ・RuntimeClassNames: Pod とワークロードリソースで指定された除外ランタイムクラス名は、無視されます。
    Namespaces: 除外された名前空間の Pod とワークロードリソースは、無視されます。

Kyverno と比較し、audit 利用時の Keyverno の利用しやすさからこちらも見送りとしました。

Kyverno

特徴

今回は運用の効率面や設定の豊富さから Kyverno を採用しました。

Keyverno を触ってみる

Keyverno と PSS Policy のインストール

# kyverno

helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm search repo kyverno -l
# バージョンはKubernetesと合わせる必要がある https://kyverno.io/docs/installation/#compatibility-matrix
helm install kyverno kyverno/kyverno -n kyverno --create-namespace --set replicaCount=1 --set metricsService.create=true  --version v2.3.4

kubectl get po
NAME                       READY   STATUS    RESTARTS   AGE
kyverno-64fbd6cc7b-pf6bt   1/1     Running   0          18h

# policy

helm install kyverno-policies kyverno/kyverno-policies -n kyverno --version v2.5.4


# 例えば
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-host-process
  annotations:
    policies.kyverno.io/title: Disallow hostProcess
    policies.kyverno.io/category: Pod Security Standards (Baseline)
    policies.kyverno.io/severity: medium
    policies.kyverno.io/subject: Pod
    kyverno.io/kyverno-version: 1.6.0
    kyverno.io/kubernetes-version: "1.22-1.23"
    policies.kyverno.io/description: >-
      Windows pods offer the ability to run HostProcess containers which enables privileged
      access to the Windows node. Privileged access to the host is disallowed in the baseline
      policy. HostProcess pods are an alpha feature as of Kubernetes v1.22. This policy ensures
      the `hostProcess` field, if present, is set to `false`.
spec:
  validationFailureAction: audit # validate or audit
  background: true # 既存のリソースも参照するか
  failurePolicy: Fail # webhook(kyverno pod)が反応しない場合に、無視するか失敗とするか
  rules:
    - name: host-process-containers
      match:
        any:
        - resources:
            kinds: # namespace, labelなどでも可能
              - Pod
      validate:
        message: >-
          HostProcess containers are disallowed. The fields spec.securityContext.windowsOptions.hostProcess,
          spec.containers[*].securityContext.windowsOptions.hostProcess, spec.initContainers[*].securityContext.windowsOptions.hostProcess,
          and spec.ephemeralContainers[*].securityContext.windowsOptions.hostProcess must either be undefined
          or set to `false`.
        pattern:
          spec:
            =(ephemeralContainers):
              - =(securityContext):
                  =(windowsOptions):
                    =(hostProcess): "false"
            =(initContainers):
              - =(securityContext):
                  =(windowsOptions):
                    =(hostProcess): "false"
            containers:
              - =(securityContext):
                  =(windowsOptions):
                    =(hostProcess): "false"

特権コンテナたててみる

% kubectl apply -f privileged.yaml
% cat privileged.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    name: privileged
  name: privileged
  namespace: default
spec:
  containers:
    - image: nginx:latest
      name: nginx
      securityContext:
        privileged: true

% kubectl get policyreport -n default
NAME              PASS   FAIL   WARN   ERROR   SKIP   AGE
polr-ns-default   11     1      0      0       0      3m59s
% kubectl describe policyreport -n default
Name:         polr-ns-default
Namespace:    default
Labels:       managed-by=kyverno
Annotations:  <none>
API Version:  wgpolicyk8s.io/v1alpha2
Kind:         PolicyReport
Metadata:
  Creation Timestamp:  2022-09-07T05:44:16Z
  Generation:          7
    Manager:    kyverno
    Operation:  Update
    Time:       2022-09-07T05:44:16Z
  Owner References:
    API Version:     v1
    Controller:      true
    Kind:            Namespace
    Name:            kyverno
    UID:             d6d6c72d-c011-4ed6-be35-a822083029c1
  Resource Version:  3156193
  UID:               a9bfa0ff-801f-41a4-a0a5-25f8ed42fbba
Results:
  Category:  Pod Security Standards (Baseline)
  Message:   validation error: Privileged mode is disallowed. The fields spec.containers[*].securityContext.privileged and spec.initContainers[*].securityContext.privileged must be unset or set to `false`. Rule privileged-containers failed at path /spec/containers/0/securityContext/privileged/
  Policy:    disallow-privileged-containers
  Resources:
    API Version:  v1
    Kind:         Pod
    Name:         privileged
    Namespace:    default
    UID:          2d8be830-fc0f-410b-8714-0a8cdc5b4a90
  Result:         fail
  Rule:           privileged-containers
  Scored:         true
  Severity:       medium
  Source:         Kyverno
  Timestamp:
    Nanos:    0
    Seconds:  1662529458
    Seconds:  1662529468

...

Summary:
  Error:  0
  Fail:   1
  Pass:   11
  Skip:   0
  Warn:   0


NameSpaceごとにレポートがまとめられる
% kubectl get policyreport -A
NAMESPACE         NAME                      PASS   FAIL   WARN   ERROR   SKIP   AGE
default           polr-ns-default           12     0      0      0       0      19h
harness           polr-ns-harness           60     0      0      0       0      19h
kube-bench        polr-ns-kube-bench        10     2      0      0       0      19h
policy-reporter   polr-ns-policy-reporter   36     0      0      0       0      19h
prometheus        polr-ns-prometheus        69     3      0      0       0      19h

監視

  • 通知
    Slack, WebHook などが対応しています。
  • (オンコールほどではなく) policy reporter、AlertManager よる Slack 通知などは利用したい

他セキュリティ

  • NodeSecurity
    ・Inspector
    ・(falco)
  • EgressSecurity
    ・Istio
    EgressGateway でアプリケーションの通信を一部 Node に寄せて可能な限り制限する
  • ポスチャ管理(EKS)
    ・kube-bench
    kube-bench → SecurityHub → EventBridge → SNS → PagerDuty

まとめ

いかがでしょうか。
今回は、Pod のセキュリティポリシーについて PaC ツールの比較検討を交えながらお話してきました。
当記事が、Pod のセキュリティポリシーを検討する上での参考になれば幸いです。

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ