はじめに
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つのツールが公開されています。
-
Cosign
- コンテナイメージやバイナリに対して署名・検証ができるツール
- 後述のFulcioと組み合わせることでOIDCによるIdentity指向の(Keyless)署名が可能
-
Gitsign
- GitのCommitに対する署名ツール
- OIDCによるIdentity指向の(Keyless)署名が可能
-
Rekor
- 署名データのTransparency Logを管理するツール
-
Fulcio
- Identity Providerとして署名用の短命な署名書を発行する
- OIDCによる認証をサポート
Sigstoreプロジェクトはパブリックなインスタンスを提供してくれておりそれらを使うことも可能となっています。
- oauth2.sigstore.dev/auth
- rekor.sigstore.dev
- など
近年ではOSS等のソフトウェアサプライチェーンへの攻撃が急激に増えてきており、ソフトウェアサプライチェーンの保護が注目されています。
先日行われたKubeconのコロケーションイベントでもSigstore Conが開催されるなど盛り上がりをみせています。
動作確認
K8sなどに簡単にデプロイできるかな?と思ったのですがまだ発展途上のようでしたので、今回はバイナリをダウンロードorビルドして検証環境を用意しました。ただFulcio・RekorのリポジトリではDocker Compose用のファイルがありましたのでそちらを使ってもらってもらうとより簡単に動作確認できると思います。
動作確認した環境はUbuntu 22.04
です。
Rekor
RekorはバックエンドにRedisとSQLサーバが必要とのことなので事前にインストールしておきます。
Rekorが提供するAPIのうちSearchIndex
を無効化する場合はRedisは必要ないようですが、動作確認した感じではCosinから呼び出されていました。
# apt install redis mariadb-server
# service redis-server start
# service mariadb start
※ Rekorは何も指定しないとそれぞれのデフォルトIP、ポートに接続するようになっているので特に設定等はいじらずそのまま起動しています。
DBをセットアップします。
今回は動作確認用のセットアップスクリプトがリポジトリに含まれているものを使っています。
# git clone --depth=1 -b v1.0.1 https://github.com/sigstore/rekor.git
# cd rekor/scripts/
# sh createdb.sh
# cd -
またバックエンドにgoogle/trillianを使っているのでインストールします。GitHubにバイナリがアップロードされていないので自分でビルドしました。
# 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/
各コンポーネントを起動します。
# 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
をインストール
# 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の起動
# 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
Rekorとは別に発行した証明書のためのCT Logのシステムが必要らしいのでCT Serverをインストールします。
trillianと同様にバイナリはアップロードされていないので自分でビルドします。
# 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 に従ってインストールします。
# 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を起動します。
# 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リポジトリのものを流用しています。
※ 今回はFulcioを--ca=ephemeralca
で起動しているので、CT Serverを後で起動していますが別のバックエンドを使う場合などは先に立ち上げても大丈夫だと思います。
see: https://github.com/sigstore/fulcio/blob/main/docs/setup.md#signing-backend
> 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 に従ってインストールします。
# 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
ここまできてようやくバイナリへの署名です。
# 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で認証できます。
認証に成功すると証明書と署名が出力されます。
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
続いてバイナリの検証です。
先ほど出力された署名と証明書を使って検証します。
# /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とのことで、失敗することがあったので今回は手動で検証しました。
以下を見てもログはありそうですが、、、実装の詳しい部分まで踏み込めていません。
# 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をFulcioの認証に使うことができます。次回はSigstoreの認証にJWT SVIDを使ったKeyless署名を試してみたいと思います。