GCPのBinary Authorization (Container Image署名のApproval)を試してみた

チュートリアルベースでの評価メモ。

- GCP (Google Cloud Platform)が提供するBinary Authorizationのうち、Attestor(証人 / 署名者)によるContainer Image署名の検証機能を確認。
- GKE上でのContainer Deploy(`kubectl run`)において、署名を登録したImageが許可され、署名されていないImageは拒否されることを確認。

## 概要

- Binary Authorizationは、GKE上で実行するContainer Imageを、信頼されたものに限定するための仕掛け。以下の2種類の制限機能がサポートされている(本記事は、Attestorについて記載)。
    - Policy
        - 特定のレジストリ(Domainに対するパターンマッチング)から取得したContainer Imageに限定する。
    - Attestors
        - AttestorによるPGP署名が事前登録されたContainer Imageに限定する。

### 環境
- OS
    - Debian GNU/Linux 9 (GCP Cloud Shell)
- GKE Version
    - v1.13.11-gke.14

### 特記事項
- 本評価時点で`Beta`のため、以下で言及している機能は今後変更される可能性あり。
- Attestor機能を有効にするためには、GKE Clusterを、`--enable-binauthz`オプションを指定して作成する必要がある。
- ポリシーの制限は、デフォルトルール(Project単位)、と除外ルール(Cluster単位)の2種類で指定できる。
- Attestorに関連付けて登録したImage署名の削除コマンドが見当たらない。
    - `gcloud beta container binauthz attestations create`で署名を登録できるが、`delete`操作が見当たらない (Attestor自体を再作成する必要がある?)。

## 手順
### APの有効化

前提となるCloud APIの有効化。

```shell
gcloud services enable \
    container.googleapis.com \
    containeranalysis.googleapis.com \
    binaryauthorization.googleapis.com
```

### 前提設定(環境変数の設定)

```shell
PROJECT_ID="$(gcloud config get-value project)"
NOTE_ID="Sample-Attestor-Note"
ATTESTOR="Sample-Attestor"
ATTESTOR_EMAIL="$(gcloud config get-value core/account)"
PGP_PUB_KEY="key.pgp"
```

### Attestor Noteの作成

まずは、Attestor情報を関連付けるためのNoteを作成する必要がある。

```shell
cat > note.json << EOF
{
  "name": "projects/${PROJECT_ID}/notes/${NOTE_ID}",
  "attestation_authority": {
    "hint": {
      "human_readable_name": "SampleName"
    }
  }
}
EOF
```

```shell

curl -X POST \
       -H "Content-Type: application/json" \
       -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
       --data-binary @note.json \
"https://containeranalysis.googleapis.com/v1beta1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"
```

### 署名用PGP Keyの作成

(十分なエントロピーを確保するための)乱数生成用パッケージをインストール。

```shell
sudo apt install rng-tools -y

sudo rngd -r /dev/urandom
```

署名用のPGP鍵を生成し、ファイルにエクスポートする(パスワードを求められるが、今回は空のパスワードを指定)。

```shell
gpg --quick-generate-key --yes ${ATTESTOR_EMAIL}
```

```shell
gpg --armor --export "${ATTESTOR_EMAIL}" > ${PGP_PUB_KEY}
```

### Attestorの作成&PGP鍵登録

上記で作成したAttestor Noteを指定して、Attestor情報を登録。

```shell
gcloud --project="${PROJECT_ID}" \
     beta container binauthz attestors create "${ATTESTOR}" \
     --attestation-authority-note="${NOTE_ID}" \
     --attestation-authority-note-project="${PROJECT_ID}"
```

登録したAttestorに対して、PGP鍵を登録。

```shell
gcloud --project="${PROJECT_ID}" \
     beta container binauthz attestors public-keys add \
     --attestor="${ATTESTOR}" \
     --pgp-public-key-file="${PGP_PUB_KEY}"
```

### GKE Clusterの設定

クラスタを作成。`--enable-binauthz`オプションを有効にする必要がある。

```shell
gcloud container clusters create sample-cluster --zone us-central1-a --enable-binauthz
```

Attestationを有効にするポリシーを定義(Attestorによる署名が存在しないイメージを拒否するように設定する)。

```shell
cat > policy.yaml << EOF
    defaultAdmissionRule:
      evaluationMode: REQUIRE_ATTESTATION
      enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
      requireAttestationsBy:
        - projects/${PROJECT_ID}/attestors/${ATTESTOR}
    name: projects/${PROJECT_ID}/policy
EOF
```

```shell
gcloud container binauthz policy import policy.yaml
```

対象のContainer ImageのDigestの情報を取得。

```shell
IMAGE_PATH="gcr.io/google-samples/hello-app"
IMAGE_DIGEST="$(gcloud container images list-tags --format='get(digest)' $IMAGE_PATH | head -1)"
```

### デプロイ確認(失敗)

このコンテナの署名は未登録のため、この段階で実行するとエラー終了する(上記で設定したPolicyにより拒否される)。

```shell
kubectl run hello-server --generator=run-pod/v1 --image ${IMAGE_PATH}@${IMAGE_DIGEST} --port 8080
```

> Error from server (Forbidden): pods "hello-server" is forbidden: image policy webhook backend denied one or more images: Denied by default admission rule. Denied by Attestor. Image gcr.io/google-samples/hello-app denied by projects/qwiklabs-gcp-01-351cba835b68/attestors/Sample-Attestor: Attestor cannot attest to an image deployed by tag


### 署名済みイメージの作成

署名用のPGP鍵のFinger Printを取得。

```shell
PGP_FINGERPRINT="$(gpg --list-keys ${ATTESTOR_EMAIL} | head -2 | tail -1 | awk '{print $1}')"
```

署名を作成。

```shell
gcloud beta container binauthz create-signature-payload \
    --artifact-url="${IMAGE_PATH}@${IMAGE_DIGEST}" > payload.json
```

```shell
gpg --local-user "${ATTESTOR_EMAIL}" \
      --armor \
      --output signature.json \
      --sign payload.json
```

署名情報をAttestationとして登録。

```shell
gcloud beta container binauthz attestations create \
    --artifact-url="${IMAGE_PATH}@${IMAGE_DIGEST}" \
    --attestor="projects/${PROJECT_ID}/attestors/${ATTESTOR}" \
    --signature-file=signature.json \
    --public-key-id="${PGP_FINGERPRINT}"
```

### デプロイ確認(成功)

署名したイメージを指定してデプロイすると正常終了する。

```shell
kubectl run hello-server --generator=run-pod/v1 --image ${IMAGE_PATH}@${IMAGE_DIGEST} --port 8080
```

## 参考資料

- [Google Cloud - Binary Authorization 信頼できるコンテナのみを Kubernetes Engine にデプロイ](https://cloud.google.com/binary-authorization/?hl=ja)
- [Google Cloud - Binary Authorization Documentation Guides Tutorials - Getting started using the CLI](https://cloud.google.com/binary-authorization/docs/getting-started-cli)