Kubernetes: Image Policy Webhookとは

Kubernetesの1.4で追加されそうなImage Policy Webhookというイメージの制御ができる機能が面白そうだったので概要をまとめてみました。

## Image Policy (Image Provenance)とは

Image PolicyとはKubernetesクラスタ上で動かすコンテナイメージを独自に制御したい場合に使える仕組みです。[Proposalのドキュメント](https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/image-provenance.md)によると下記のようなユースケースを想定しているようです。

- 既知の脆弱性を含まないことを確認されたイメージだけを動かしたい
- 特定のベースイメージを使ったイメージだけを動かしたい
- レビュー済みのコードでビルドされたイメージだけを動かしたい
- 署名されたイメージだけを動かしたい

Image Policy Webhookは、ユーザが定義するWebAPIに対してそのPodに含まれるコンテナイメージの情報を渡し、実行可否をチェックする仕組みです。コンテナイメージ制御自体は自前のWebAPIで行う必要があります。Admission Controllerプラグイン`ImagePolicyWebhook`として実装されています。参考: [Kubernetes: Admission Controlとは何か](http://qiita.com/tkusumi/items/f71882f47e2d87701223)

## WebhookへのRequest/Response

webhookへのリクエスト、レスポンスには`ImageReview`構造体のJSON形式が使われます。参考: [pkg/apis/imagepolicy/types.go](https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/imagepolicy/types.go)

```go
type ImageReview struct {
	unversioned.TypeMeta
	api.ObjectMeta
	Spec ImageReviewSpec       // チェックするイメージの情報
	Status ImageReviewStatus   // チェック結果
}

type ImageReviewSpec struct {
	Containers []ImageReviewContainerSpec // Podに含まれるコンテナ(複数)
	Annotations map[string]string         // Podに定義できるアノテーション `*.image-policy.k8s.io/*`
	Namespace string
}

type ImageReviewContainerSpec struct {
	Image string // image:tag or image@SHA:012345679abcde のような形式
}

type ImageReviewStatus struct {
	Allowed bool  // 許可するかどうか
	Reason string // 否認の場合の理由
}

```

### Request

リクエストのJSONは以下のようなイメージなります。`spec.containers[].image`にはPodのコンテナのイメージタグ(`image:tag`形式)またはイメージID(`image@SHA:012345679abcde`形式)が入ります。

```json
{
  "kind": "ImageReview",
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "containers": [
      {
        "image": "nginx:latest" # チェック対象のコンテナ
      }
    ],
    "namespace": "namespace"
  }
}
```

[ProposalのImage tags and IDs](https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/image-provenance.md#image-tags-and-ids)で説明されていますが、初期の実装では複雑さを避けるため、PodTemplateのイメージ情報をそのままWebhookへ渡す形になっています。そのため、イメージタグで指定された場合はWebhookでチェックしたときのイメージと、クラスタがイメージをPullしたときのイメージの同一性を厳密に保証することは現状は難しいようです。ProposalではPodTemplateでイメージIDだけを許可するような形にするなどの対策などが提案されていました。


### Response

許可する場合のレスポンスは以下のようなイメージになります。

```json
{
  "apiVersion": "imagepolicy.k8s.io/v1alpha1",
  "kind": "ImageReview",
  "status": {
    "allowed": true, # 許可する
    "reason": ""     # 否認する場合は理由が入る
  }
}
```

## キャッシュ

Image Policy Webhookにはレスポンスは一定時間キャッシュをする仕組みがあります。`ImageReviewSpec`構造体のJSON表記をキャシュのキーにしているようです。現時点のデフォルトはAllowが5分、Denyが30秒になっていました。参考: [plugin/pkg/admission/imagepolicy/config.go#L32-L33](https://github.com/kubernetes/kubernetes/blob/520cd7e6f386980d062505d6028f102037304a7a/plugin/pkg/admission/imagepolicy/config.go#L32-L33)

## ソース

- [pkg/apis/imagepolicy](https://github.com/kubernetes/kubernetes/tree/master/pkg/apis/imagepolicy) 型定義
- [plugin/pkg/admission/imagepolicy](https://github.com/kubernetes/kubernetes/tree/master/plugin/pkg/admission/imagepolicy) Admission Controllerの実装

## 参考

- https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/image-provenance.md
- [Ensuring only images are from approved sources are run #22888](https://github.com/kubernetes/kubernetes/issues/22888)
- [Container Image Policy #59](https://github.com/kubernetes/features/issues/59)
- [ImagePolicyWebhook Admission Controller #30631](https://github.com/kubernetes/kubernetes/pull/30631)