2020年6月7日日曜日

GitHub Actionsでシークレットを扱うときの注意

先日、Install SSH Keypull requestが届きました。ありがたいことです。

READMEに鍵形式の変換方法の追記とtypo修正なので特に問題ないだろうと思ってapproveしたんですが、そこで走ったCIがことごとくぬっころされていました

え、READMEの修正だけなのになんで?と思って調べたところ、接続テスト用のSSH鍵が渡されておらず、シークレットが空っぽの状態になっていました。

別にシークレットを間違えて削除したというわけではなく、フォーク先からのイベントで起動されるGitHub Actionsにはシークレットが渡されない仕様のようです。

つまり、実質的にシークレットをCIに使っているリポジトリは外部からのプルリクを受け付けられないことになります。ご注意あれ。

理由

ていうか何でこんな仕様になってるんだろう。

フォーク先のリポジトリにシークレットが引き継がれるわけでもないし、フォーク先からのプルリクは普通にシークレットを使ってくれていいのに。

…と思っていろいろ考えたところ、多分↓こんな感じ↓でシークレットが外部に漏れることを想定しているのかなという結論に至りました。
  1. 悪意のあるユーザーが、CIにシークレットを使っているリポジトリをフォーク
  2. そのユーザはフォーク先でCIのワークフローファイルを編集して、「プルリクが作成されたら特定のシークレットを別サーバーに送信する」という処理を入れる
    • 例えばcurlで外部にPOSTするなりクエリストリングに含めてGETするなりURLパスに含めるなりいろいろ方法があります
  3. 編集したワークフローファイルをプルリクにしてフォーク元に送りつける
  4. 送りつけられたプルリクに対して自動的にCIが走るため、リポジトリオーナーが気づいたときにはもうシークレットは流出している
怖い怖い。盲点だった。

システムを作るときはこのあたりまで考えておかないと悪い子にいたずらされちゃいますね。

回避方法

じゃあどうすればいいのかというと、まだいい感じの方法は思いついていません

とりあえず考えたのはこんな手順。
  1. フォーク元に受け皿用の一時ブランチを作る。このブランチではシークレットを必要とするCIは走らせない
  2. フォーク先(外部)からのプルリクは一旦上記の受け皿ブランチにマージする
  3. あらためて受け皿ブランチからプルリクを作る。ここではシークレットが使えるので、エラーはここでチェックする。
今回はこの方法で取り込みましたが、受け皿ブランチを作らないといけないので二度手間なのと、CIでエラーが起きたときの差し戻しもちょっと面倒。

なにかいい方法はないものか。

0 件のコメント:

コメントを投稿