Trivyのv0.17.0をリリースしました。
長い道のりでしたが、ようやくこれでGoバイナリの脆弱性検知に対応できました。夜中0時ぐらいからリリース作業を初めて気付いたら朝5時でした。
概要
Go言語で書かれたプログラムをビルドすると依存しているモジュールがバイナリに含まれます。現代のソフトウェア開発において利用しているOSSのライブラリが0ということはまれなので、何かしらのOSSライブラリが作成されたバイナリに同梱されます。これらのOSSの古いバージョンには既知の脆弱性が含まれる可能性があります。これを手動で調べて追うのは手間なので最近では脆弱性スキャナを用いて検知するのが普通です。自分が開発したTrivyというOSSの脆弱性スキャナではコンテナイメージやファイルシステム上のGoバイナリに含まれるモジュールを特定し脆弱性を検知します。
Goのバイナリからどうやって依存しているモジュール情報を取り出すのか?という部分は興味ない人が多い気がしますが一応ブログに書いているので、もし詳細を知りたい方がいればどうぞ。
使い方
Goのバイナリがあればいいので、scratchなどのコンテナイメージでも全く問題なしです。適当にマルチステージビルドを使ってGoのバイナリを含むイメージをビルドします。今回trivyをビルドしてtrivyでスキャンしているので少しややこしいですが、Goのプロジェクトなら何でも良いです。
まずGoのバイナリを作って、それをscratchにコピーします。
$ docker build -t test-image - <<EOF FROM golang:1.13 as builder RUN curl -sL https://github.com/aquasecurity/trivy/archive/refs/tags/v0.9.0.tar.gz | tar zx -C ./ RUN cd trivy-0.9.0 && CGO_ENABLED=0 go build -o trivy cmd/trivy/main.go FROM scratch COPY --from=builder /go/trivy-0.9.0/trivy /usr/local/bin/trivy ENTRYPOINT ["/trivy"] EOF ... Successfully built b79da5579cfd Successfully tagged test-image:latest
これで準備は終わりです。今回は test-image
というイメージ名にしています。あとはTrivyでスキャンするだけです。そもそもTrivyの使い方分からんという場合はドキュメントを見ていただければと思います。
コンテナイメージなので image
というサブコマンドを使います。
$ trivy image test-image 2021-04-30T04:57:06.244+0900 WARN OS is not detected and vulnerabilities in OS packages are not detected. 2021-04-30T04:57:06.245+0900 INFO Detecting gobinary vulnerabilities... usr/local/bin/trivy =================== Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) +-------------------+------------------+----------+-------------------+---------------+---------------------------------------+ | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE | +-------------------+------------------+----------+-------------------+---------------+---------------------------------------+ | golang.org/x/text | CVE-2020-14040 | HIGH | v0.3.2 | v0.3.3 | golang.org/x/text: possibility | | | | | | | to trigger an infinite loop in | | | | | | | encoding/unicode could lead to... | | | | | | | -->avd.aquasec.com/nvd/cve-2020-14040 | +-------------------+------------------+----------+-------------------+---------------+---------------------------------------+
/usr/local/bin
の下に配置したバイナリから脆弱性が検知されていることが分かります。特にバイナリがどこにあるとかパスを指定しなくても自動で見つけてくれます。今回はscratchにしているためOSパッケージが検知されずwarningが出ていますが無視で良いです。
これはコンテナイメージでしたが、ファイルシステム上にあってもよいです。その場合は fs
サブコマンドを使います。
$ go build -o /app/myweb $ trivy fs /app
こんな感じでスキャンできます。現在はディレクトリしか対応してないのでバイナリを直接指定するとエラーになります(いずれ対応します)。
データソース
GitLab Advisory Database
上のブログでGitLab Advisory Databaseが使えそうという話をしたのですが、実はよく見たらライセンス的に駄目でした。
第三者が使う場合はGitLabの同意が必要とはっきり書いてありました。ですが、GitLabは自社の提供するContainer Scanning機能をClairという別のOSSからTrivyに乗り換えることが決まっています。以下でもアナウンスされています。
これを交渉に使わない手はないということでGitLab社とミーティングしたりIssueで話したりしつつ交渉を重ねた結果、OSS向けに公開してくれることになりました。交渉には3ヶ月近くかかって大変でしたが基本的には前向きに考えてもらえたのでやりやすかったです。
ただしこのデータはマネタイズの観点で重要なのでそのまま公開することは出来ないということでgemnasium-dbで公開から一定期間経った脆弱性のみを公開してくれることになりました。それが以下のadvisories-communityです。
残念なことに今は6ヶ月遅れになっています。6ヶ月だと脆弱性公開されて攻撃終わっていると思うので、そこをなんとか...ということで現在交渉中です。その結果、6ヶ月はあまりにも長すぎるということを理解してもらえ、今は1ヶ月にする方向で話が進んでいます。こちらの対応が終わればもう少し新しい脆弱性が検知できるようになると思います。とはいえ0よりは良いだろうということで現在は6ヶ月遅れのデータを使っています。
Go Vulnerability Database
3ヶ月かけてGitLabと交渉している間にGoチームから彗星のごとく新たな脆弱性データベースが現れました。
うおおおお!!となったのですが、まだまだα版かなという完成度です。masterのテストがそもそも落ちていたり、flagの使い方が間違っていたり、相当雑にやったのかなという感想です。ですが忙しくて手が回らない時は助け合いの精神ということでPRを何件か出しました。
こちらがマージされれば検知精度は更に上がると思います。あまりレスポンスない感じなので、待ちきれなければforkしたデータでしばらく運用するかもしれません。
余談
Goの脆弱性DB構築に関連して、Russ Coxから共通の脆弱性用JSONフォーマットを策定しようという提案が出ています。
一応既にCVRFやOVALというXMLベースのものがあるのですが、本当に人間が考えたんかこれ?みたいなフォーマットですし新たなフォーマットの策定は大歓迎です。この辺りの知見はかなり豊富な方なので、自分もこの議論には入っておく予定です。
今後の予定
バイナリだけでなく go.sum
にも対応する予定です。 fs
を使ってGitHubなどのCIに組み込んでもらう想定です。バイナリをいちいち作らなくてもスキャンできるようになるので便利な場面は多いかと思います。重要な部分の実装はほぼ終わっているので今回のリリースに含めても良かったのですが、バイナリの方だけでも早めに出したいなと思い一旦リリースしました。
まとめ
TrivyでGoバイナリの脆弱性検知が出来るようになりました。検知精度はGoのデータベースを取り込むことで近い将来さらに改善されると思いますが、現時点でもある程度有用なので是非試してみてください。