Sigstore Toolsを触ってみる

## はじめに

Z LabではこれまでSPIFFE/SPIREなどのソフトウェアを使ったIdentityの配布(Identiy Bootstrapping/Identity Provisioning)に取り組んできました。その中の1つとしてOpenStack環境でのVM Nodeを検証するためのプラグインを開発し、信用できるVMの上でアプリケーションを動作させることができるようになりました。

参考資料:
- https://www.slideshare.net/techblogyahoo/openstack-248310680
- https://speakerdeck.com/hiyosi/using-spire-as-identity-provider-for-athenz-at-yahoo-japan

しかし、そのうえで動作するアプリケーションは信用できるものと言えるでしょうか。
SPIREではアプリケーションがIdentityを受け取るためにはWorkload Attestaionが必要となっているため、アプリケーションのメタデータが事前に登録された情報と一致する必要があります。例えばバイナリやコンテナイメージのハッシュ値だったりといったものを登録しWorkload Attestation時に一致することを確認しています。
しかし、そのバイナリやコンテナイメージといった成果物が正しいものであるかどうかはどうやって判断すればよいでしょうか。

その手段の1つとしてデジタル署名とその検証という行為が有効です。
しかしデジタル署名は鍵の管理(配布、保管、失効など)が大変という課題があります。
今回はそれらの課題を解消できるSigstoreのツールを使って署名と検証の動作確認をしてみました。

## Sigstoreについて
Sigstoreでは現在4つのツールが公開されています。

https://www.sigstore.dev/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15164/b5eaeb9d-bfd9-ee94-149d-5a9dc354579f.png)

- [Cosign](https://github.com/sigstore/cosign)
    - コンテナイメージやバイナリに対して署名・検証ができるツール
    - 後述のFulcioと組み合わせることでOIDCによるIdentity指向の(Keyless)署名が可能
- [Gitsign](https://github.com/sigstore/gitsign)
    - GitのCommitに対する署名ツール
    - OIDCによるIdentity指向の(Keyless)署名が可能
- [Rekor](https://github.com/sigstore/rekor)
    - 署名データのTransparency Logを管理するツール
- [Fulcio](https://github.com/sigstore/fulcio)
    - Identity Providerとして署名用の短命な署名書を発行する
    - OIDCによる認証をサポート

Sigstoreプロジェクトはパブリックなインスタンスを提供してくれておりそれらを使うことも可能となっています。

- oauth2.sigstore.dev/auth
- rekor.sigstore.dev
- など


近年ではOSS等のソフトウェアサプライチェーンへの攻撃が急激に増えてきており、ソフトウェアサプライチェーンの保護が注目されています。

https://www.sonatype.com/resources/state-of-the-software-supply-chain-2021

先日行われたKubeconのコロケーションイベントでもSigstore Conが開催されるなど盛り上がりをみせています。

https://events.linuxfoundation.org/sigstorecon-north-america/


## 動作確認

K8sなどに簡単にデプロイできるかな？と思ったのですがまだ発展途上のようでしたので、今回はバイナリをダウンロードorビルドして検証環境を用意しました。ただFulcio・RekorのリポジトリではDocker Compose用のファイルがありましたのでそちらを使ってもらってもらうとより簡単に動作確認できると思います。

動作確認した環境は`Ubuntu 22.04`です。

### Rekor
https://docs.sigstore.dev/rekor/installation/

RekorはバックエンドにRedisとSQLサーバが必要とのことなので事前にインストールしておきます。
Rekorが提供するAPIのうち`SearchIndex`を無効化する場合はRedisは必要ないようですが、動作確認した感じではCosinから呼び出されていました。

```shell
# apt install redis mariadb-server
# service redis-server start
# service mariadb start
```

※ Rekorは何も指定しないとそれぞれのデフォルトIP、ポートに接続するようになっているので特に設定等はいじらずそのまま起動しています。

DBをセットアップします。
今回は動作確認用のセットアップスクリプトがリポジトリに含まれているものを使っています。

````shell
# git clone --depth=1 -b v1.0.1 https://github.com/sigstore/rekor.git
# cd rekor/scripts/
# sh createdb.sh
# cd -
````

またバックエンドに[google/trillian](https://github.com/google/trillian
)を使っているのでインストールします。GitHubにバイナリがアップロードされていないので自分でビルドしました。

```shell
# git clone --depth 1 -b v1.5.1 https://github.com/google/trillian.git
# cd trillian
# cd cmd/trillian_log_server
# go build
# cp trillian_log_server /usr/local/bin/
   
# cd -
# cd cmd/trillian_log_signer/
# go build
# cp trillian_log_signer /usr/local/bin/
    
# cd -
# cd cmd/createtree/
# go build
# cp createtree /usr/local/bin/
```

各コンポーネントを起動します。

````shell
# cd $HOME
# trillian_log_server \
  -log_dir=/tmp \
  -log_file=trillian_log_server.log \
  -logtostderr=false &
    
# trillian_log_signer \
  -log_dir=/tmp \
  -log_file=trillian_log_signer.log \
  -logtostderr=false \
  -force_master \
  -rpc_endpoint=localhost:8190 \
  -http_endpoint=localhost:8191 \
  -batch_size=1000 \
  -sequencer_guard_window=0 \
  -sequencer_interval=200ms &
    
# createtree --admin_server=localhost:8090
````

次に https://docs.sigstore.dev/rekor/installation  に従って`rekor-cli`と`rekor-server`をインストール

````shell
# rekor-cli version
  ____    _____   _  __   ___    ____             ____   _       ___
 |  _ \  | ____| | |/ /  / _ \  |  _ \           / ___| | |     |_ _|
 | |_) | |  _|   | ' /  | | | | | |_) |  _____  | |     | |      | |
 |  _ <  | |___  | . \  | |_| | |  _ <  |_____| | |___  | |___   | |
 |_| \_\ |_____| |_|\_\  \___/  |_| \_\          \____| |_____| |___|
rekor-cli: Rekor CLI
 
GitVersion:    v1.0.1
GitCommit:     d3162350e96098ca8a24adfdbee42057e43b5de6
GitTreeState:  clean
BuildDate:     2022-11-10T15:26:56Z
GoVersion:     go1.19.3
Compiler:      gc
Platform:      linux/amd64
    
# rekor-server version
2022-12-16T04:31:50.669Z        DEBUG   app/root.go:114 pprof enabled false
  ____    _____   _  __   ___    ____            ____    _____   ____   __     __  _____   ____
 |  _ \  | ____| | |/ /  / _ \  |  _ \          / ___|  | ____| |  _ \  \ \   / / | ____| |  _ \
 | |_) | |  _|   | ' /  | | | | | |_) |  _____  \___ \  |  _|   | |_) |  \ \ / /  |  _|   | |_) |
 |  _ <  | |___  | . \  | |_| | |  _ <  |_____|  ___) | | |___  |  _ <    \ V /   | |___  |  _ <
 |_| \_\ |_____| |_|\_\  \___/  |_| \_\         |____/  |_____| |_| \_\    \_/    |_____| |_| \_\
rekor-server: Rekor signature transparency log server

GitVersion:    v1.0.1
GitCommit:     d3162350e96098ca8a24adfdbee42057e43b5de6
GitTreeState:  clean
BuildDate:     2022-11-10T15:26:56Z
GoVersion:     go1.19.3
Compiler:      gc
Platform:      linux/arm64
````


rekor-serverの起動

````shell
# rekor-server serve &
# rekor-cli loginfo --rekor_server http://127.0.0.1:3000
No previous log state stored, unable to prove consistency
Verification Successful!
Active Tree Size:       0
Total Tree Size:        0
Root Hash:              e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Timestamp:              2022-12-08T02:17:10Z
TreeID:                 7472319177902149289
````

### Fulcio
https://github.com/sigstore/fulcio/blob/main/docs/setup.md

Rekorとは別に発行した証明書のためのCT Logのシステムが必要らしいのでCT Serverをインストールします。
trillianと同様にバイナリはアップロードされていないので自分でビルドします。

````shell
# git clone --depth 1 -b v1.1.4 https://github.com/google/certificate-transparency-go.git
# cd certificate-transparency-go
# go build ./trillian/ctfe/ct_server
# cp ct_server /usr/local/bin/ 
# cd -
````

Fulcioは https://github.com/sigstore/fulcio/releases に従ってインストールします。

```shell
# fulcio version
 _____   _   _   _        ____   ___    ___
 |  ___| | | | | | |      / ___| |_ _|  / _ \
 | |_    | | | | | |     | |      | |  | | | |
 |  _|   | |_| | | |___  | |___   | |  | |_| |
 |_|      \___/  |_____|  \____| |___|  \___/
fulcio: Fulcio

GitVersion:    v1.0.0
GitCommit:     unknown
GitTreeState:  unknown
BuildDate:     unknown
GoVersion:     go1.19.2
Compiler:      gc
Platform:      linux/amd64
````

Fulcio Serverを起動します。

````shell
# mkdir /etc/fulcio-config
    
> 以下の設定は認証にSigstoreプロジェクトがメンテナンスしているProvider(DEX)を使います。
# cat << EOF > /etc/fulcio-config/config.json
{
  "OIDCIssuers": {
    "https://oauth2.sigstore.dev/auth": {
      "IssuerURL": "https://oauth2.sigstore.dev/auth",
      "ClientID": "sigstore",
      "Type": "email"
    }
  }
}
EOF
    
# fulcio serve \
  --config=/etc/fulcio-config/config.json \
  --host=0.0.0.0 \
  --port=5555 \
  --grpc-port=5554 \
  --ca=ephemeralca \
  --ct-log-url=http://127.0.0.1:6962/test \
  --metrics-port=12112 > /tmp/fulcio.log &
      
# curl -s http://127.0.0.1:5555/api/v1/rootCert -o root.pem
# mkdir /etc/config
# mv root.pem /etc/config/root.pem
````

CT Serverを起動します。設定ファイルやセットアップはfulcioリポジトリのものを流用しています。

- https://github.com/sigstore/fulcio/blob/main/config/ctfe/ct_server.cfg

※ 今回はFulcioを`--ca=ephemeralca`で起動しているので、CT Serverを後で起動していますが別のバックエンドを使う場合などは先に立ち上げても大丈夫だと思います。

see: https://github.com/sigstore/fulcio/blob/main/docs/setup.md#signing-backend

````shell
> log-idの取得
# curl http://127.0.0.1:8091/metrics |grep "^quota_acquired_tokens{spec=\"trees"|head -1|awk ' { print $1 } '|sed -e 's/[^0-9]*//g' > /tmp/logid
# cat /tmp/logid
156741710383120662
    
> 設定ファイルの生成
# cat ct_server.cfg | sed -e "s/%LOGID%/"`cat /tmp/logid`"/g" > /etc/config/ct_server.cfg
    
> テスト用鍵ペアの準備
# openssl ec -in <(openssl ecparam -genkey -name prime256v1) -out key.pem -des
# openssl ec -aes256 -in key.pem -out privkey.pem -passout pass:foobar
# openssl ec -in privkey.pem -pubout -out pubkey.pem
# cp *.pem /etc/config/.
    
# ct_server \
  -log_config=/etc/config/ct_server.cfg \
  -log_rpc_server=127.0.0.1:8090 \
  -http_endpoint=0.0.0.0:6962 \
  -log_dir=/tmp \
  -log_file=ct_server.log \
  -logtostderr=false &
````

### Cosign

Cosign は https://docs.sigstore.dev/cosign/installation に従ってインストールします。

````shell
# cosign version
  ______   ______        _______. __    _______ .__   __.
 /      | /  __  \      /       ||  |  /  _____||  \ |  |
|  ,----'|  |  |  |    |   (----`|  | |  |  __  |   \|  |
|  |     |  |  |  |     \   \    |  | |  | |_ | |  . `  |
|  `----.|  `--'  | .----)   |   |  | |  |__| | |  |\   |
 \______| \______/  |_______/    |__|  \______| |__| \__|
cosign: A tool for Container Signing, Verification and Storage in an OCI registry.
    
GitVersion:    v1.13.1
GitCommit:     d1c6336475b4be26bb7fb52d97f56ea0a1767f9f
GitTreeState:  clean
BuildDate:     2022-10-17T18:00:05Z
GoVersion:     go1.19.2
Compiler:      gc
Platform:      linux/amd64
````

ここまできてようやくバイナリへの署名です。

````shell
# echo "my first artifact" > artifact
# export SIGSTORE_CT_LOG_PUBLIC_KEY_FILE=/etc/config/pubkey.pem 
# export COSIGN_EXPERIMENTAL=1 
# cosign sign-blob \
  artifact \
  --fulcio-url "http://127.0.0.1:5555" \
  --rekor-url "http://127.0.0.1:3000" \
  --output-certificate cert.pem \
  --output-signature sig  
````

コマンドを実行すると認証画面が出てきます。Dex経由でいずれかのProviderで認証できます。

![スクリーンショット 2022-12-16 15.03.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15164/9df6456a-4f78-77ef-dff8-7299d48a441f.png)

認証に成功すると証明書と署名が出力されます。

````shell
Successfully verified SCT...
using ephemeral certificate:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
    
tlog entry created with index: 1
Signature wrote in the file sig
Certificate wrote in the file cert.pem
    
> 署名と証明書がファイルに書き出されています
# echo "$(cat sig)";
MEYCIQCSBw0H9plaCcc9T1YT1aSxVJRFWEASOOyIWqbURl65cQIhAJK9XCAIA9lTVXkY+Zeog6nsiUqOyN03YlwYU4qCDbww
    
# echo "$(cat cert.pem)";
    LS0tLS1CRUdJTiBDRVJUS....FURS0tLS0tCg==
````

今回は証明書のSANにはGitHubに登録されているEmailアドレスが設定されています。このようにSANの値によって誰が署名したかわかります。

```
# cat cert.pem | base64 -d | openssl x509 -in /dev/stdin -noout -text |grep "emai"
                email:xxx@example.com (例)
```

またこの証明書は署名用であるため有効期間は10分間と短命なものになっています。

```
        Validity
            Not Before: Dec 16 05:49:12 2022 GMT
            Not After : Dec 16 05:59:12 2022 GMT
```

続いてバイナリの検証です。
先ほど出力された署名と証明書を使って検証します。

````shell
# /usr/local/bin/cosign verify-blob \
  artifact \
  --signature sig \
  --cert cert.pem \
  --cert-chain /etc/config/root.pem \
  --rekor-url=""

Verified OK
````
実行結果を見るにどうやら成功してそうです。

ここで、

> Transparency log support for blobs is experimental, and occasionally an entry isn't found even if one exists.
> 
> We recommend requesting the certificate/signature from the original signer of this blob and manually verifying with cosign verify-blob --cert [cert] --signature [signature].

RekorのTransparency Logを使ってバイナリの署名検証を行うのはまだexperimentalとのことで、失敗することがあったので今回は手動で検証しました。

以下を見てもログはありそうですが、、、実装の詳しい部分まで踏み込めていません。

````shell
# rekor-cli search --artifact artifact --rekor_server http://127.0.0.1:3000
Found matching entries (listed by UUID):
23f7d8c93e53dc3687de1312f266a77157343b2eabf6dc74bbb3a34003d00d038830fcc0a46eff0b
    
# rekor-cli get --uuid 23f7d8c93e53dc3687de1312f266a77157343b2eabf6dc74bbb3a34003d00d038830fcc0a46eff0b --rekor_server http://127.0.0.1:3000
LogID: 7c5d1a8c66ea519207ef3e16ac2a30575a7972a0bf97bdd1ced103043dacefcc
Index: 1
IntegratedTime: 2022-12-16T05:49:12Z
UUID: 23f7d8c93e53dc3687de1312f266a77157343b2eabf6dc74bbb3a34003d00d038830fcc0a46eff0b
Body: {
  "HashedRekordObj": {
    "data": {
      "hash": {
        "algorithm": "sha256",
        "value": "c69d72c98b55258f9026f984e4656f0e9fd3ef024ea3fac1d7e5c7e6249f1626"
      }
    },
    "signature": {
      "content": "MEYCIQCSBw0H9plaCcc9T1YT1aSxVJRFWEASOOyIWqbURl65cQIhAJK9XCAIA9lTVXkY+Zeog6nsiUqOyN03YlwYU4qCDbww",
      "publicKey": {
        "content": " LS0tLS1CRUdJTiBDRVJUS....FURS0tLS0tCg=="
      }
    }
  }
}
````

このあたりはまた時間があるときに追いかけてみたいなと思います。

## まとめ

以上でSigstoreツールをローカルで動かして簡単な動作検証をしてました。
例えばこの検証ロジックをデプロイのWebhookで呼び出してみたり、SPIREのWorkload Attestationに組み込んだりすることで信用できるアプリケーションのみを実行することができるようになります。

今回は認証にパブリックインスタンスのDexを使いましたが、このままでは署名のプロセスに人が介在しなければなりません。
システムが署名するための手段の1つとしてFulcio/CosinはSPIFFE Workload APIをサポートしており、SPIREとも連携することが可能です。
Workload API経由で取得した[JWT SVID](https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md)をFulcioの認証に使うことができます。次回はSigstoreの認証にJWT SVIDを使ったKeyless署名を試してみたいと思います。