🐧

CI/CD Test Night #7 で登壇しました

2024/04/06に公開

先日 DeNA 主催の CI/CD Test Night #7 というイベントで登壇しました。

https://testnight.connpass.com/event/311263/

https://swet.dena.com/entry/2024/04/02/113000

自分は普段あまり登壇とかはせずに主に OSS の開発やブログの執筆という形でアウトプットをすることが多いのですが、自分がメンテしている OSS などでいつもお世話になっている @ponkio_o さんからお声がけいただき、登壇することとなりました。
貴重な機会をいただきありがとうございました。

本記事では自分の発表について紹介したいと思います。

https://docs.google.com/presentation/d/1rN4kTtvErrheZ3SXNr49XUHbiGIoorpfiLGXjLt5vsc/edit?usp=sharing

自分は主に CI/CD のセキュリティ的なプラクティスや関連するツールの紹介をしました。
一つのことを掘り下げるというより、色々なトピックについて話をしました。

自分の発表の多くは自分が過去に書いたブログ記事に基づいています。
ブログ記事ではより詳しく解説していたりするので、是非そちらも参照してください。

Pull Request の CI の結果を Pull Request にコメントすることで CI の結果を分かりやすくする

まずは Pull Request の CI の結果を Pull Request にコメントすることで CI の結果を分かりやすくする方法について紹介しました。
CI が失敗した場合、 CI のログを見てもなんのコマンドが失敗したのか、関連するログはどこからどこまでなのか良くわからないことがあります。
特に大きめのシェルスクリプトなどを実行している場合なんかはそうですね。
そこで github-comment という CLI ツールを使うとコマンドの実行結果に応じてコマンドとその標準(エラー)出力をコメントすることが出来ます。
単にコマンドの出力をそのままコメントするだけではなく、関連するリンクを載せたりエラーを修正するためのガイドを載せたりすることも可能です。
古いコメントを非表示にすることでコメント欄が荒れるのを防ぐことが出来ます。
詳細は以前ブログに書いているのでそちらを読んでください。

https://zenn.dev/shunsuke_suzuki/articles/improve-cicd-with-github-comment

また tfcmt というツールを使うと terraform plan, apply の結果を分かりやすく Pull Request にコメントすることが出来ます。

https://zenn.dev/shunsuke_suzuki/articles/improve-terraform-cicd-with-tfcmt

複数リポジトリのメンテナンス性の改善

複数のリポジトリをメンテしているとメンテナンスコストが増大していき、メンテしきれなくなることがあります。
そういった問題を解決するために 2 つの方法を紹介しました。

  1. 共通コードを何らかの形 (GitHub Action, Reusable Workflow, CLI, etc) でパッケージングし、 Renovate で更新を自動化
  2. 複数のリポジトリを 1 つの Monorepo にまとめる

2 に関しては昔 5 つの Terraform のリポジトリを 1 つの Monorepo にまとめたことがあります。
中々大変でしたが十分やる価値のあることでした。

https://blog.studysapuri.jp/entry/2021/04/01/080000

GitHub Actions のセキュリティベストプラクティス

GitHub Actions の Security のベストプラクティスは色々あると思います。

https://github.com/suzuki-shunsuke/ghalint/discussions/234

その中でも機械的にチェックしやすい 3 つの基本的なプラクティスを紹介しました。

  1. job の permissions を必要最小限にする
  2. secret の公開範囲を最小の step, job に制限する
  3. Action のバージョンを full commit hash で固定する

ghalint という linter を使ってこれらをチェックすることが出来ます。

https://zenn.dev/shunsuke_suzuki/articles/github-actions-ghalint

上記の他に tibdex/github-app-tokenactions/create-github-app-token を使って GitHub App から GitHub Access Token を発行している場合に repositories や permissions を制限しているかどうかもチェックできます。
CI で ghalint を実行することでベストプラクティスが守られた状態を維持することが出来ます。

Action のバージョンの固定

GitHub Actions の Action や Reusable Workflow のバージョンを Full Commit Hash で固定することでセキュリティや CI の信頼性を改善することが出来ます。
Full Commit Hash を指定する場合、コードコメントで semver を記載することで人間にとって分かりやすくなるだけではなく Renovate で semver に従って update を自動化することが出来ます。
この際、バージョンが v3.0.0 のような semver に従ったフォーマットではなく v3 のようにメジャーバージョンしかないようなフォーマットだと正確なバージョンが分からないだけでなく Renovate が semver に従って update することが出来ません。
なので Full Commit Hash で固定しつつ semver に従ったバージョン文字列をコメントで記載することが望ましいですが、既存のコードを全てそのように手動で修正するのは大変です。
そこで pinact というツールを使うとこの修正を自動化出来ます。

https://zenn.dev/shunsuke_suzuki/articles/pinact-pin-github-actions-version

CI で使うツールのバージョン管理

aqua という CLI ツールを使うことで CI で使うツールをバージョン管理する方法について紹介しました。

https://zenn.dev/shunsuke_suzuki/books/aqua-handbook

一歩先のセキュリティ

ここからはもう少し突っ込んだセキュリティの話をしました。

CI を書き換えられないようにする

GitHub Actions で pull_request event の代わりに pull_request_target を用い、 workflow の改竄を防いでより安全に CI を実行する方法について紹介しました。
スライドでは詳細については説明できなかったので是非下記の記事を参照してください (OIDC による Secret へのアクセス制御もこの記事で解説しています) 。

https://zenn.dev/shunsuke_suzuki/articles/secure-github-actions-by-pull-request-target

CI で実行するスクリプトを書き換えられないようにする

workflow だけではなく CI で実行するスクリプトや設定ファイルなどを書き換えられないようにする方法について軽く触れました。
スライドでは詳細については説明できなかったので是非下記の記事を参照してください。

https://zenn.dev/shunsuke_suzuki/articles/prevent-tamper

https://github.com/suzuki-shunsuke/simple-sync-action

他のリポジトリからの攻撃

特定のリポジトリで CI を書き換えられなくしたりしたとしても、そのリポジトリにアクセスできる GitHub Access Token や GitHub App の管理が杜撰だと、外部から Pull Request を作成したり出来てしまうという話です。
強めの権限を持った Personal Access Token や GitHub App の private key が GitHub Actions Secret から取得できたり、社内の人間であれば簡単に取得できてしまう状態にあるということはあったりすると思います。
そういった Secret は早めになんとかするべきというのはそれはそうなんですが、そうと分かっていても他にも色々やるべきことがあって後回し・見ないふりされているということもあるかもしれません。
そういった場合でもまずは特に攻撃されると不味いようなリポジトリに関してはアクセスできないようにするのが重要でしょう。

古い revision の CI のリトライを防ぐ

古いリビジョンを基に実行された CI がリトライされると、古いコードがデプロイされたりリソースが変更・削除されたりするリスクがあるので危険です。
なので CI を実行する際には CI でリビジョンが最新かどうかチェックするようにしましょう。
Monorepo の場合はサービスごとに最新のリビジョンかチェックするようにすると余計にリトライがブロックされずに済むでしょう。
Terraform の CI では terraform plan で生成された plan file を使って terraform apply を実行するようにすると、古い plan file では terraform apply が失敗するので安全に terraform apply を実行することが出来ます。

本イベントで紹介できなかったもの

時間の都合上以下の内容には触れられませんでしたが、是非読んでください。

https://zenn.dev/shunsuke_suzuki/articles/tfprovidercheck-introduction

https://github.com/suzuki-shunsuke/tfaction

下記の記事はタイトルに Renovate と入っていますが、 Renovate 関係なく参考になる部分もあるかと思います。

https://zenn.dev/shunsuke_suzuki/articles/renovate-auto-merge-github-actions

さいごに

以上、自分が発表した内容について簡単に紹介しました。
懇親会では @ponkio_o さんや @Kesin11 さんなど、色々な方と直接お会いすることが出来ました。
また、登壇を通じて pinact や ghalint のようなこれまであまり知られていなかった自分の OSS を知っていただくことが出来たのも良かったです。

イベントの開催に携わってくださった方々、本当にありがとうございました。

Discussion