buildahを使用したコンテナイメージの暗号化

## 概要と環境

今回はbuildahを使って自身のコンテナイメージを暗号化する。
buildahとはコンテナのイメージビルドとレジストリへのプッシュを行うことができるツール（[公式GitHub](https://github.com/containers/buildah)）。今回コンテナイメージの暗号化を行う環境は以下。

※ホストOSである`mac OS`上で`Ubuntu 22.04`のコンテナを動かし、コンテナ内でbuildah、skopeoを使用している。

 <table>
    <tr>
      <td>OS</td>
      <td>mac OS Monterey</td>
    </tr>
    <tr>
      <td>CPU</td>
      <td>Apple M1 Pro</td>
    </tr>
    <tr>
      <td>Host OS Container Runtime</td>
      <td>Podman 4.1.1</td>
    </tr>
    <tr>
      <td>Container signing tools</td>
      <td>skopeo 1.8.0</td>
    </tr>
    <tr>
      <td>Container build tools</td>
      <td>buildah 1.23.1</td>
    </tr>
    <tr>
      <td>Encryption Method</td>
      <td>公開鍵暗号方式</td>
    </tr>
    <tr>
      <td>Container Registry</td>
      <td>IBM Cloud Container Registry</td>
    </tr>
 </table>

## 暗号化手順

前提として以下ソフトウェアはインストール済みの想定で進めていく。

- buildah
- skopeo
- openssl

### 1. 秘密鍵と公開鍵のペアを生成

任意の場所に生成

↓秘密鍵
```sh
> openssl genrsa --out private.pem
```

↓公開鍵
```sh
> openssl rsa -in private.pem -pubout -out public.pem
```

### 2. コンテナイメージを暗号化してpush

```sh
> buildah push --encryption-key jwe:./public.pem jp.icr.io/namespace/sample:encrypted
```

`--encryption-key`オプションには`jwe:{公開鍵のファイルパス}`を指定する

## 暗号化確認

復号化せずにpullすると怒られることが確認できる

```sh
> buildah pull jp.icr.io/namespace/sample:encrypted
Trying to pull jp.icr.io/namespace/sample:encrypted...
Getting image source signatures
Copying blob 56c2ae88d5cb [--------------------------------------] 0.0b / 460.0b
Copying blob 1ab766dfd54a [--------------------------------------] 0.0b / 2.4MiB
Copying blob a41872b65c84 [--------------------------------------] 0.0b / 24.7MiB
Copying blob 760faab46f0d [--------------------------------------] 0.0b / 2.8MiB
Copying blob 5159bf515ee0 [--------------------------------------] 0.0b / 90.4MiB
decrypting layer sha256:760faab46f0d775c1baa956919b59844a08736d108c1a5afc0660ed33aca81eb: missing private key needed for decryption
```

復号化を行うにはpullコマンドに`--decryption-key`オプションを付与する。`--decryption-key`オプションには暗号化した公開鍵と対になる秘密鍵のファイルパスを指定する

```sh
> buildah pull --decryption-key ./private.pem  jp.icr.io/namespace/sample:encrypted
Trying to pull jp.icr.io/namespace/sample:encrypted...
Getting image source signatures
Copying blob 56c2ae88d5cb done  
Copying blob 5159bf515ee0 done  
Copying blob 760faab46f0d done  
Copying blob a41872b65c84 done  
Copying blob 1ab766dfd54a [======================================] 2.4MiB / 2.4MiB
Copying config df8ad46840 done  
Writing manifest to image destination
Storing signatures
df8ad46840163cb65d63bdf09999cbbfd55f356012f12803448783d99e42bd81
```

## コンテナイメージへの署名と併用するには

コンテナイメージの安全な運用方法の手段として暗号化の他に署名も挙げられる。この署名と暗号化を併用するには **先に暗号化を行う必要がある。** 実際の動作を確認していく。

### 署名→暗号化

#### 1. 非暗号化かつ未署名のコンテナイメージを用意する

```sh
> buildah push jp.icr.io/namespace/sample:no-enp
```

#### 2. 署名を行う

署名にはskopeoを使用する。具体的な手順は[こちらの記事を参照](https://qiita.com/tegc2012/items/a599b850a552800a33e4)

```sh
> skopeo copy --sign-by user@domain docker://jp.icr.io/namespace/sample:no-enp docker://jp.icr.io/namespace/sample:no-enp-signed
Getting image source signatures
Copying blob f43264d372e8 skipped: already exists  
Copying blob 76fecb13890c skipped: already exists  
Copying blob 26adfc839732 skipped: already exists  
Copying blob 8ecab674fb13 skipped: already exists  
Copying blob d064b39eb698 skipped: already exists  
Copying config 52353c9401 done  
Writing manifest to image destination
Signing manifest
Storing signatures
```

#### 3. 署名済みのイメージを暗号化する

まず署名済みのイメージをpullする

```sh
> buildah pull jp.icr.io/namespace/sample:no-enp-signed
```

pullしたイメージを暗号化し再度pushする

```sh
> buildah push --encryption-key jwe:./public.pem jp.icr.io/namespace/sample:no-enp-signed
Getting image source signatures
Checking if image destination supports signatures
error pushing image "jp.icr.io/namespace/sample:no-enp-signed" to "docker://jp.icr.io/namespace/sample:no-enp-signed": Copying this image requires changing layer representation, which is not possible (image is signed or the destination specifies a digest)
```

`Copying this image requires changing layer representation, which is not possible (image is signed or the destination specifies a digest)`のように怒られてしまう。

### 暗号化→署名

先程作成した暗号化コンテナイメージを使用する

#### 1. 署名を行う

```sh
skopeo copy --sign-by user@domain docker://jp.icr.io/namespace/sample:encrypted docker://jp.icr.io/namespace/sample:encrypted-signed
Getting image source signatures
Copying blob 760faab46f0d skipped: already exists  
Copying blob 1ab766dfd54a skipped: already exists  
Copying blob 56c2ae88d5cb skipped: already exists  
Copying blob a41872b65c84 skipped: already exists  
Copying blob 5159bf515ee0 skipped: already exists  
Copying config df8ad46840 done  
Writing manifest to image destination
Signing manifest
Storing signatures
```

こちらは正常に署名が完了する。

### 余談

イメージpullの際に`--remove-signatures`を指定することで一度署名したコンテナイメージの署名を剥がすことができる。

```sh
buildah pull --remove-signatures  jp.icr.io/namespace/sample:no-enp-signed
```
上記を行うことで暗号化が可能となる