Pod Security Policy(PSP)のサンプル

# はじめに
Pod Security Policy（PSP）は「セキュアにするために使おう」と言われている割には、プラクティスがあまりでまわっていない気がします。そこがまた始めるハードルを上げてしまうのだと思います。

なんと、v1.18の公式ドキュメントでPSPのサンプルが提供されていました。 :congratulations:
OpenShiftのポリシーと比べると、緩めでした。ただ、厳しくするということは、その分緩和して良いかなどを検討する際に詳しい知識を求められます。まず利用してみるのであれば良いサンプルなのではないかと思っています。

# コンテンツ概要
- 公式ドキュメント「[Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/)」をPSPの実装を前提に要約。
- 個人所感は{}で明記。

# 注意点
- 公式ドキュメント自体は実装に依存しない基本設計を示すことを目的にしています。当記事はPSPのサンプルを提供することが目的のため、基本設計とPSPの実装を同時に記載します。
- Pod Securityを実現する方法がPSP以外にも開発されており、今後実現方法は変わっていくかもしれません。
    - [OPA Gatekeeper](https://github.com/open-policy-agent/gatekeeper)
- PSPの機能についての詳細は当記事では説明しません。


# ポリシータイプ
ポリシーのタイプは３つに分類されている。

- Privileged 
    - 制限が完全にないポリシー。
- Default
    - 既知の権限昇格を防ぎながら、一般的なコンテナを採用するためのポリシー。
    - 基本的なPodの設定に利用する。
- Restricted
    - 厳しく制限したポリシー。現状のPod Securityにおいて、一番セキュアな設定。

## Privileged
- 制限が完全にないポリシー。
- 特権のある信頼できるユーザーによって管理されるシステムレベルおよびインフラストラクチャレベルのワークロードを対象とする。

```psp-privileged.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
```

## Default

- 既知の権限昇格を防ぎながら、一般的なコンテナを採用するためのポリシー。
    - {OpenShiftなどと比べるとデフォルトのポリシーは緩いです。}
       - `runAsUser` などが`RunAsAny` となっています。
       - 現状世の中に出回っているコンテナはroot実行を前提としているものが多く、現実的にはこのようなポリシーが最初はデフォルト設定に落ち着くと思います。
- 重要ではないアプリケーションのオペレーターおよび開発者を対象とする。

``` psp-default.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: baseline
  annotations:
    # Optional: Allow the default AppArmor profile, requires setting the default.
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
    # Optional: Allow the default seccomp profile, requires setting the default.
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default,unconfined'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'unconfined'
spec:
  privileged: false
  # The moby default capability set, defined here:
  # https://github.com/moby/moby/blob/0a5cec2833f82a6ad797d70acbf9cbbaf8956017/oci/caps/defaults.go#L6-L19
  allowedCapabilities:
    - 'CHOWN'
    - 'DAC_OVERRIDE'
    - 'FSETID'
    - 'FOWNER'
    - 'MKNOD'
    - 'NET_RAW'
    - 'SETGID'
    - 'SETUID'
    - 'SETFCAP'
    - 'SETPCAP'
    - 'NET_BIND_SERVICE'
    - 'SYS_CHROOT'
    - 'KILL'
    - 'AUDIT_WRITE'
  # Allow all volume types except hostpath
  volumes:
    # 'core' volume types
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
    # Allow all other non-hostpath volume types.
    - 'awsElasticBlockStore'
    - 'azureDisk'
    - 'azureFile'
    - 'cephFS'
    - 'cinder'
    - 'csi'
    - 'fc'
    - 'flexVolume'
    - 'flocker'
    - 'gcePersistentDisk'
    - 'gitRepo'
    - 'glusterfs'
    - 'iscsi'
    - 'nfs'
    - 'photonPersistentDisk'
    - 'portworxVolume'
    - 'quobyte'
    - 'rbd'
    - 'scaleIO'
    - 'storageos'
    - 'vsphereVolume'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  readOnlyRootFilesystem: false
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
```


## restricted
- 現状のPod Securityにおいて、一番セキュアな設定。
    - {ただし、root実行を前提とするようなコンテナは実行できなくなります。}
- セキュリティが重要なアプリケーションのオペレーターと開発者、および信頼性の低いユーザーを対象とする。

```psp-restricted.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant with non-root + disallow privilege escalation,
  # but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Require the container to run without root privileges.
    rule: 'MustRunAsNonRoot'
  seLinux:
    # This policy assumes the nodes are using AppArmor rather than SELinux.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false
```

# まとめ
公式ドキュメントに記載されている、PSPのサンプル実装を紹介しました。
ベストプラクティスとされるrestrictedポリシーは現状デフォルトで利用すると動かないコンテナが多いです。しかし、動かないからといってポリシーを緩くしておくのでは前に進まないと思います。個人的にはrestrictedをデフォルト設定にして、ポリシーを緩和する仕組み（運用）を検討することがよりセキュアにするために必要な重要な取り組みなのだと思っています。