GitHub Actionsのpermissionを粛々と整理した話

こんにちは、セキュリティチームの@sota1235です。

10Xのセキュリティチームではプロダクトに近い領域での権限管理に関して、リスク整理と対応を日々行なっています。

今回はその取り組みの一環であるGitHub Actionsのpermissionsに関しての取り組みをご紹介します!

なぜやるのか

そもそもこの取り組みを始めたWhyを軽く説明します。

10XではGitHubで日々の業務が行われており、守るべき資産の数多くがGitHub上で管理されています。

また、アプリケーションのデプロイや日々の運用などもGitHub Acitonsを利用しながら行われており、もしGitHub上で何かしらのセキュリティリスクが顕在化した時のダメージは大きいです。

例えば守るべき主な資産は以下が挙げられます。

  • Git管理されているソースコード
  • 業務上のやり取りが行われるIssue、Pull Request等の内容
  • GitHub Actionsで管理されるSecret情報
  • etc...

10Xではこのリスクを許容可能なレベルまで低減するためにいくつかの取り組みを行っています。

今回お話しするGitHub Actionsにおけるpermissionの整理もその取り組みのうちの1つです。

GitHub Actionsにおけるpermission

permissionとは

GitHub Actionsではpermissionを各jobに設定することができます。

docs.github.com

このpermissionはworkflow実行時に吐き出される GITHUB_TOKENにもそのまま設定されます。

もし permissionsが明示的に指定されていない場合、defaultのpermissionが振られます。

defaultで設定されるpermission

permissionに何がdefaultで設定されるかはGitHub organizationの設定やRepositoryの設定に依存します。

特に設定していなければ下記ドキュメントの Default access (permissive)に設定されているはずです。

docs.github.com

Default access (permissive)の設定を眺めてみるとほとんどの項目に関して read/writeが振られており、permissionsを指定しないと大抵のことができることがわかります。

defaultの権限を Default access (restricted)に変更したい

上記の仕様を踏まえて、社内で動いているGitHub Actionsのworkflowを調査するとほとんどのworkflowには permissionsが明示的に指定されておらず、権限が非常に強い状態でした。

この状態は最小権限の原則に反しており、万が一GitHub Actionsでセキュリティリスクが顕在化した際に小さくできるはずの被害範囲が広がる恐れがあります。

そこでセキュリティチームではOrganization全体でのdefault permissionの設定をDefault access (restricted)に変更する意思決定をしました。

設定変更の道のり

default permissionをOrganization全体で変更すること自体は5分もあれば完了しますが変更自体は後方互換性を大きく損なうものです。

というのも permissionsが明示されていないworkflow内のjobに関しては権限が設定変更によって暗黙的に落とされるため、落とされた権限に依存している処理があった場合には権限エラーで動作しなくなるためです。

どのような状態を目指して、そこと現状のギャップをどう埋めるかを考える必要がありました。

目指す姿を決める

最終的にこの施策が完了した後、このような状態になることを目指すことを定義しました。

  • 全てのrepositoryで定義されたGitHub Actions workflowにpermissionsが明示的に指定されている
  • Organizationの設定でGitHub Actionsのpermission設定がDefault access(restricted)に設定されている
  • 今後追加されるworkflowにpermissionsが設定されるようになる

まずは調査するRepository一覧を出す

まずはOrganization内でGitHub Actionsが利用されているRepositoryを全て洗い出す作業を行いました。

Organization内に存在するrepositoryはgh commandを利用することで全て取得することができます。

gh repo list {organization_name} -L {limit} --no-archived

-Lオプションには表示件数を入れますが、default値が30なので30以上repositoryがあると思われる場合は適当な値を設定します。また、archiveされたRepositoryではGitHub Actionsが動作しないので--no-archivedオプションをつけます。

私はこの結果を作業用のIssueにTODOリストとしてそのまま貼り付けました。出力結果はJSONにもできたりするのでRepository一覧からさらにfilterしたい場合等はそれらのオプションを活用すると良いです。

1つ1つ棚卸し

上記で出したRepositoryでGitHub Actionsが利用されているかを1つずつ見てまわります。

10Xの場合は見て回れる量でした。見て回れない分量の場合は各Repositoryの中身を見に行って .github/workflowsディレクトリが存在するか確認するscriptを書くのが良いと思います。

この際、そのRepositoryの内容に合わせて以下のアクションを取りました。

  • GitHub Actionsを利用していない、かつ今後も利用しないRepositoryの可能性が高い(ドキュメント置き場等)
    • Repository単位の設定でGitHub Actions自体を無効化する
  • GitHub Actionsを利用していないが、今後利用する可能性がある
    • Repository単位の設定でGitHub ActionsのpermissionをDefault access (restricted)に設定する
  • GitHub Actionsを利用している
    • 要対応Repositoryとしてメモしておく

GitHub ActionsのPermission設定はRepository単位でも設定することができます。今回の取り組みを進める途中に新しくDefault access (permissive)のpermissionに依存するworkflowが増えると要対応リストが増えるため、後方互換性の観点で問題のないRepositoryはこのタイミングでRespotitory単位での設定を変更していきます。

GitHub Actionsを利用する必要のないRepositoryはGitHub Actions自体にまつわるリスクそのものを無くすために無効化してしまいます。必要であればすぐに有効化が可能です。

設定画面(repositoryは私個人のやつです)

ちなみにRepository単位での設定とOrganizationの設定が異なり、Repository側の設定の方が権限が多い場合はOrganizationの設定が優先されます。

Organizationの設定 Repositoryの設定 実際に付与される権限
Default access (permissive) Default access (restricted) Default access (restricted)
Default access (restricted) Default access (permissive) Default access (restricted)

実際の挙動としてはOrganizationで Default access (restricted)を設定するとRepository側の設定画面ではDefault access (permissive)が選択できなくなります。細かい挙動は実際に検証してみてください。

permissionsの適用をするPull Requestsを投げて投げて投げまくる

後方互換性が壊れうるもの、すなわち permissionsが指定されていないものに関しては1つ1つ確認し、修正するPull Requestを送りました。

Pull Requestをちぎっては投げちぎっては投げ

理想論で言うと修正方法をガイドライン化し、全体に共有。修正の担当者を開発チーム全体に割り振っていき対応を依頼するのがあるべき姿ですが今回は以下の理由から全てのworkflowに対して修正Pull Requestを送る方針をとりました。

  • 対応すべきRepositoryやworkflowの量が膨大ではなかったこと
  • セキュリティチームに知見を溜めたかったこと
  • Ownershipが不明なRepositoryやworkflowも多く実質、棚卸しに近かったこと

2点目に関しては、そもそもセキュリティチームとしてもどんなロジックにどんなpermissionが必要なのか。よく利用される3rd Party Actionではどんなpermissionが必要なのか等の知見が少ない状態だったのでそれを溜めるために自分で手を動かすことにしました。

3点目に関してはOwnershipが不明なことは一定、ネガティブではありつつも結果的には使用されていないworkflowの棚卸しやCode Ownerを明確にするアクションが取れた点は嬉しい副産物でした。

修正したRepositoryから順番にDefault access (restricted)を設定していく

permissionsの修正が完了したものから順にRepository単位で設定を変更し、TODOリストを粛々と消化していきました。

リリースのCI等、影響が大きいworkflowが動いてるRepositoryの設定変更はなるべく影響が少ないタイミングで行いました。

修正漏れがある場合、設定変更後にActionsのエラーが発生することがあるためです(N敗)。

Organizationの設定をDefault access (restricted)にする

全てのRepositoryの設定変更を完了し、変更漏れがないことを確認したらOrganizationの設定変更を行います。

Organizationの設定変更にはOwner権限が必要です。然るべき手順で設定を変更してください。

勝利宣言

ガイドラインを整備する

これで完了、ではありません!

序盤に述べた目指す姿のうち、3点目を達成するためにGitHub Actions利用者向けのガイドラインを整備します。

  • 全てのrepositoryで定義されたGitHub Actions workflowにpermissionsが明示的に指定されている
  • Organizationの設定でGitHub Actionsのpermission設定がDefault access(restricted)に設定されている
  • 今後追加されるworkflowにpermissionsが設定されるようになる

ここで数多のRepositoryにpermission修正のPull Requestを出してきた経験が生きてきます。

やりたいことから逆引きで必要なpermissionをガイドする

社内のworkflowのほとんどに目を通して気づくのは、write権限が欲しいユースケースの大体は共通しているということです。

それらに関してはやりたいことから逆引きできる形で必要なpermissionをガイドラインに記載しています。

ガイドラインを一部抜粋

よく使われる3rd Party Actionsに必要なpermissionをガイドする

業務でworkflowを書く際は3rd Part Actionsを利用することがほとんどだと思います。

一方で3rd Party Actionsのドキュメントを読みにくと体感として8-9割のケースで必要なpermissionの記載がありません。

自身の趣味やOSS等、高いセキュリティレベルが求められないケースにおいては特に問題ないのですが今回のようにdefaultの権限を制限している場合、権限不足が頻繁に発生したため社内で利用されている3rd Party Actionsに関しては設定が必要なpermissionを全てガイドラインに書き起こしました。

ガイドラインを一部抜粋

この際、メンテナンスが止まっていたり別のActionsへの移行がアナウンスされているものに関しては非推奨とし、このガイドラインを見ることで非推奨のActionsを避けつつ必要なpermissionを設定できるようになりました。

今後出てくる課題として、この表自体がメンテナンスされないという可能性は否めませんが現状の業務で達成したいことのほとんどはこの表にある3rd Party Actionsで達成できるためそこまで大きな問題にはならないと考えています。

余談: どんなpermissionが必要かはactions-permissionsでできるのか

実はどんなpermissionが利用されているかを静的/動的に調べるためのactionsがGitHub Security Labからリリースされています。

github.blog

タイミング的に渡りに船だったので利用できるか検証しましたが、以下の理由から今回の施策では利用が難しいと判断しました。

  • GraphQL APIとの通信には対応していない
  • 3rd Party Actionsに対応できていない

実装を読めばわかりますが内部的に行なっているのはproxy serverを起動してGitHubのREST APIと通信する場合にそのURLを解析して必要なpermissionsを列挙する、というものになっておりまだまだPublic Beta感が否めないなーというのが所感です。

振り返り

この取り組みは6月から始めてちょうど2ヶ月ほどで完了しました。これ以外の取り組みも並行していたのでこれだけに集中すれば1ヶ月で完了できたかもしれませんがレビュー等の待ち時間を考えると他タスクと並行しつつ1~2ヶ月で完了が妥当かなという感覚です。

間違いなく今だからできた

10Xはまだ若い会社であり、開発者も40人ほどです。それゆえにRepositoryの数も多くなく、愚直にworkflowファイルを読んで回ることが可能な規模感だったので今回のアプローチが取れました。

これに取り組むのが1, 2年後であれば難易度が跳ね上がっていただろうなと思っています。

現役で稼働しているworkflowを修正する難しさ

完了後に振り返ってみて見ると、作業時間の6割は特定の10~20 workflowの修正に時間を割いていました。

というのも現役で稼働している、かつ本番リリース等のproduction影響が大きいものは慎重に修正せざるおえず、また動作確認も困難でした。

特に自動でタグを切る、Pull Requestを生成/mergeするようなworkflowは動作させるだけで副作用があるのでかなり苦労しました。

今思えば検証用のRepositoryを作成してそこに特定部分のstepだけを切り出し、動作確認をしていた方が圧倒的に効率が良かったと反省しています。急がば回れ。

これでGitHub Actionsのpermission周りは安心か?

答えとしてはNoかなと考えています。

というのもdefaultのpermissionが厳しくなったとしても明示的に permissionsを付与すれば依然として、過大なpermissionの付与は可能な状態です。また、readにしか依存していないworkflowの場合はpermissionsを定義し忘れても動作してしまうので定義漏れは発生しうる状態です。

一方でwrite権限が必要なworkflowを実装した際はpermissionsを定義しないと権限エラーになるため、開発者の意識が権限回りに自然と向くようになることは期待できるかなと考えています。

permissionsの定義忘れや過大なpermission付与に関しては機械的に検知が可能なはずなのでレビューするような仕組みの実装を検討しています。

また、脅威が顕在化した場合の検知に関してはAudit logを利用した監視の仕組みが必要だと考えておりそれらとセットとなって始めてリスクが低い状態が実現できると考えています。

GitHubの治安維持はまだまだこれから

今回はGitHub ActionsのpermissionにFocusした取り組みを紹介しましたが、これ以外にもセキュリティ面でやるべき取り組みは多く残っています。

それらにも絶賛取り組んでいるのでまたブログを書ければなと思っています。

また、それらに一緒に取り組んでくれる仲間も募集してるので興味があればJob Descriptionだけでも読んでやってください :pray:

セキュリティエンジニア(Product Security) / 株式会社10X

ソフトウェアエンジニア(Product Security Ops) / 株式会社10X

それではまた!