Octo STSで実現する"いい塩梅"のGitHub App Token運用

10X Product Blog読者の皆さん、こんにちはこんばんは、セキュリティチームのyagihashです。
この記事は、10X 新春ブログリレー 2026の1月31日の記事です。

本日は掲題の通り、10Xで導入したOcto STSというGitHub App Tokenの管理のための仕組みについて、背景にあった課題や使用感などを交えて紹介します。

9月から寝かしていた記事を締め切り駆動で書き切ってしまおうと思い立ち、まだ白紙の状態だったので締め切りは先の方がいいな…と思っていたらトリを務めることになってしまいました。緊張して声がうまく出なくてどうしようと思いましたが、ブログなので関係ありませんでした。どうぞ最後までお楽しみください。

github.com

Octo STSについて

Octo STSは、OIDCのID Tokenを取得・提示できるGitHub Actionsなどの環境向けに、GitHub AppにひもづくGitHubのアクセストークン(以下、GitHub App Token)を発行するアプリケーションです。

OSS利用時のサプライチェーンのセキュリティ強化に取り組んでいるChainguard社が開発・公開しており、同社が運用しているManagedのGitHub Appを使えば簡単に利用できるほか、ソースコードが公開されているため自身で用意した環境でホストすることもできます。

開発の背景に長命なCredentialによるGitHub APIへのアクセスをなくしたいというのがあるようで、そのあたりの話は『The end of GitHub PATs: You can’t leak what you don’t have』で紹介されています。

実際に試してはいないのですが、後で紹介するTrust Policyでは任意のIssuerを指定できるので、ID Tokenを入手できる環境で、Octo STSがID Tokenの検証に用いる鍵を取得する方法(JWKS)さえあればGitHub Actions以外にも任意の環境でGitHub App Tokenの発行を受けられるんじゃないかと思います。よくできていますね。

Trust Policyに基づくGitHub App Token運用

Octo STSはリポジトリまたは組織にひもづくTrust Policyに基づいて、GitHub App Tokenの発行可否を判断します。

Octo STSでのGitHub App Token発行フロー

大まかな流れは上記の通りですが、図中で言及されているTrust Policyの実体は以下のようなYAMLファイルです。

issuer: https://token.actions.githubusercontent.com
subject: repo:<OWNER>/<REPO>:environment:production

permissions:
  contents: read
  issues: write

これを、各リポジトリの.github/chainguard/*.sts.yamlとして配置することで、Octo STSがTrust Policyとして読み込んでくれます。各リポジトリのうち、.githubというリポジトリ(ディレクトリ名ではなくリポジトリ名)だけは特別で、このリポジトリに配置したTrust PolicyはOrganization全体で共有され、リポジトリを跨ぐ権限を持つGitHub App Tokenに対するTrust Policyはこのリポジトリに配置する必要があります。この仕組みにより、(後述しますがセルフホストする場合においては)あるリポジトリに閉じたGitHub App Tokenの運用についてはそのリポジトリの管理者に完全に委任することができ、セキュリティ管理者にとっても開発者にとってもストレスフリーな運用を実現できます。

また、Trust Policyは常にDefault branchのものが参照されるためDefault branchが適切に保護されていれば、未レビューのTrust Policyが信頼されることはありません。これによって、例えばMerge前のPull Requestに含まれるTrust Policyを使ってGitHub App Tokenを生成することなどはできません。

Repository SecretsをはじめとしたSecretsに格納したGitHub AppのPrivate keyとactions/create-github-app-tokenなどの組み合わせでGitHub App Tokenを生成している場合、任意のブランチ上でのワークフロー改変からPrivate keyを守るためには、例えばEnvironmentの活用などが必要で、取り回しも悪く利便性が下がる一方、Octo STSの場合はPrivate keyはGitHubの外にあり、Default branchと.githubリポジトリの保護にさえ気をつけておけば比較的容易にGitHub App Tokenの取得経路を守ることができるのが大きな利点です。

octo-sts/actionを利用したGitHub App Token取得

公式のactionであるocto-sts/actionを用いて以下のようにOcto STSからGitHub App Tokenを取得できます。

permissions:
  id-token: write

steps:
- uses: octo-sts/action@f603d3be9d8dd9871a265776e625a27b00effe05 # v1.1.1
  id: token
  with:
    scope: ${{ github.repository }}
    identity: hogehoge

- env:
    GH_TOKEN: ${{ steps.token.outputs.token }}
  run: |
    gh repo list

scopeはアクセス対象のリポジトリまたは組織を指定します。組織名だけを指定した場合は、先述の<OWNER>/.githubリポジトリがTrust Policyの配置場所として認識されます。

identityは参照したいTrust Policyの.github/chainguard/<この部分>.sts.yamlを指定します。

また、GitHub ActionsのID Tokenを利用するため、Workflowにはid-token:writeのパーミッションが必須です。

導入の背景

product.10x.co.jp

10Xでは、2024年にPATの利用を最小化する取り組みの一環として組織内で共有のGitHub Appを使い始めました。当時の経緯は上記の記事で紹介しています。

このGitHub AppのPrivate keyはOrganization wide secretsに格納され、ユースケースに合わせて、希望ベースでセキュリティチームがSecretのアクセスを許可する形で運用していました。2025年のサプライチェーン攻撃が大きな話題となったことをきっかけに、このGitHub Appの悪用が大きな懸念となり何らかの対処が必要となりました。

多岐にわたるユースケースをひとつのGitHub Appでカバーしているため、このAppは多くのパーミッションを持っている上に、例えばあるリポジトリのユースケースではcontents:readがあれば十分なところ、別のあるリポジトリではcontents:writeが必要なために、GitHub App自身はcontents:writeを持たざるを得ないなど、必要最小限の権限を必要最小限のスコープに付与するといった理想形とはほど遠い状況でした。

また、ユースケースをあまり限定していないため、(そもそもOrganization wide secretsにアクセス元ブランチを限定するような機能はないものの)当然のことながら任意のブランチからPrivate keyにアクセス可能で、例えば何らかの経路でGitHubの私たちのOrganizationへのアクセスが掌握された結果、このGitHub Appを経由して様々なリポジトリにそこそこの権限を持ってアクセスされてしまうというのが大きな懸念でした。

選択肢

ユースケースを調査・整理した結果、主要なユースケースを以下のように分類できました。これら以外にもいくつか少数のユースケースがありましたが、GitHub Appの分解自体は徐々に進めることもできるので、まずはこれらの主要なユースケースにどう対処するかを検討しました。

  1. 他のリポジトリのコンテンツを取得したい
  2. 自分自身にPRを立てたい
  3. CIでPRを修正したい

また、そもそもGitHub App Tokenを必要とせず、secrets.GITHUB_TOKENで代替可能なユースケースも見受けられ、よくわからずGitHub App Tokenを使ってしまうことに関する課題も明らかになりました。Octo STSの導入に際してGitHub App Tokenを必要としないユースケースもすべて是正しました。

さて、上記ユースケースへの対処法として、Octo STS以外にもいくつかの選択肢がありました。

  1. 適切な粒度で分割したユースケースごとにGitHub Appを用意する
  2. autofix.ci
  3. securefix-action
  4. Octo STS的なものを内製する

順に紹介していきます。

1. 適切な粒度で分割したユースケースごとにGitHub Appを用意する

組織の広い範囲に対して権限を持つAppが広く使われていると守りにくいというのが当初の問題意識であり、このやり方で対応するのであれば最低限リポジトリごとのAppを分離するのに加えて、リポジトリ内でもユースケースによってはAppを分離する動機も生じます。現実的にそこまで大きな数にはならないとは思われるものの、無限に増えていくユースケースに対して無限にGitHub Appを用意し続けるのは困難と判断して、このやり方ですべてをカバーするのは早々に断念しました。

その他の選択肢との合わせ技で、管理するGitHub Appの数が現実的な量になるのであれば再度検討することとして、他の選択肢について検討しました。

2. autofix.ci

先述のユースケースのうち、autofix.ciはCIでPRを修正したいケースを概ねカバーできそうでした。ただカバレッジとしてはそれほど広くなく、autofix.ci自身がPull Requestを作成することなどはできず、今回の私たちのニーズを満たすことはできませんでした。また、Organizationのプライベートリポジトリでの利用は有償であることも選定しなかった理由のひとつです。

3. securefix-action

autofix.ciとはまったく違うアプローチで同じ問題を解決するのがsecurefix-actionです。Server役のリポジトリだけにPrivate keyが露出し、保護したいリポジトリ本体はワークフロー改変などに強い状態を保てます。一方でこれもそもそもすべてのニーズを満たすものではなく、導入するとしても限定的な活用に留まる見込みでした。

4. Octo STS的なものを内製する

Google CloudからGitHub PATと秘密鍵をなくす – Token ServerのGoogle Cloudへの拡張』のように、何らかのポリシーに基づいてGitHub App Token発行を制御する仕組みを内製することで、理想のGitHub App Tokenの運用を実現できます。ユースケースがまったく限定されないので、これひとつでだいたいの問題が解決しますが、一方で実装・運用には相応の手間を要し、10Xのセキュリティチームとしてはそれを許容できませんでした。

選ばれたのはOcto STSでした🎉

そういうわけで、求めているのはユースケースを限定せず、何らかのポリシーに基づいてGitHub App Token発行を制御する仕組みである一方、内製はあまり現実的ではなかったため、残った選択肢がOcto STSでした。ここまでで説明した通り、Octo STSはTrust Policyに基づくGitHub App Tokenの発行の制御をしてくれるアプリケーションで、無償のManaged GitHub AppまたはSelf-hostedなGitHub Appとして利用可能であるため、非常に有力な選択肢となりました。

導入にあたってのポイント

この記事を読んで「うちもOcto STS使いたい!」と思う人もいるかもしれないので、導入にあたってのポイントを紹介します。

Managed v.s. Self-hosted

結論から述べると、10XではOcto STSをセルフホストすることを選択しました。

ManagedのOcto STSが要求するパーミッションの一覧

上記の通り、ManagedのOcto STSが要求するパーミッションは非常に強力なもので、今回の私たちのユースケースにおいては不必要なものもたくさん含まれています。判断に悩むところではあるのですが、不用意に強力な権限を外部のGitHub Appに渡すことは許容できず、10Xでは自分たちで環境を用意し、Octo STSをセルフホストしています。

ちなみに、セルフホストする場合はGitHub Appに付与するパーミッションは自分たちで選択できるため、10XではManagedの場合よりもかなり少ない範囲に絞ってパーミッションを付与しています。

セルフホストするときのつまずきポイント

Octo STSのソースコードおよびGoogle Cloudで運用する際の必要なリソースはすべてGitHubで公開されていますが、ステップバイステップのガイドやセルフホストのための最小構成の提示はないので、必要ならアプリケーションのソースコードも読んで環境を準備する必要がありました。

実際につまずいたポイントは以下の通りです。

  1. octo-sts/appのIaCのコードはChainguardのモジュールにかなり依存していて、最小構成を読み解くのがちょっと大変
  2. 最小構成で動かすのに不必要な環境変数が必須になっていてうまく動かせない
  3. GitHub App Tokenの発行のログはEventarcでしか吐いてくれない
  4. Webhookが暴れてGitHub APIのRate limitに引っかかる

どう対応したか

人それぞれつまずくポイントは変わってくると思いますが、わたしがどう対応したかを簡単に紹介しておきます。

1. IaCを読み解く

今回は本家と同様にGoogle Cloud上で動かせればよかったので、IaCの定義は普通に読み解いて必要なものをすべて書き直しました。構成としてはそこまでややこしくなくて、外部からアクセス可能なCloud Run Serviceを動かせばOKです。個人的にネットワークまわりがあまり詳しくなかったのでけっこうつまずきました。また、デプロイの仕組みなども自前で用意しないといけないので、そのあたりも含めるとある程度の手間と時間がかかりました。

もしこれを読んでセルフホストに挑戦する人がいたら、事情が許せばroles/editorを持ったプロジェクトでコンソールで一旦動くものを作ってからIaC化するのが楽だと思います。

2. 必須にしなくていいのに必須になっている環境変数をなんとかする

本家にPull Requestを出しました。

3. Eventarcでしか吐いてくれないログを受け取る

実のところ、本家の構成を完全に真似すればこれは解決するのですが、どうにも過剰に感じて、Eventarcのリクエストを受け取ってそのままCloud Loggingに吐き出すCloud Functionを用意して動かしています。ログが残れば十分なのでこれで特に不満なく運用できています。

4. Webhookを無効化する

Octo STSには、GitHub AppのWebhook機能とChecks APIを利用して、Trust Policyのバリデーションをしてくれる機能が備わっています。

Trust Policy Validationが動作している様子

ハンドルするイベントはPullRequestEventをはじめとしていくつかあるのですが、実際に運用してみるとかなりの頻度でWebhookが呼ばれ、ハンドルしているイベントを受け取った場合は都度GitHub APIへのリクエストが発生します。(該当箇所

これによってGitHub Appに割り当てられたGitHub APIのRate limitをすぐに消費しきってしまい、GitHub App Tokenの発行もできなくなってしまう問題が発生しました。特に多いのがCheckRunEventだったはずで、手元で試した限りではGitHub AppのPermissions & eventsの設定から、SubscribeするイベントをPushとPull Requestに絞ることで受け取るイベントの数を低減し、Rate limitの過度な消費が抑えたままバリデーターの恩恵を受けられそうな雰囲気ですが、まだ本番のアプリでは適用できていません。

Permissions & eventsの設定画面の切り抜き

本家でこの問題にどう対処しているかを知りたいところです…。

所感と課題

いろいろと難所を越えながらどうにか実運用にまでこぎ着けたOcto STSですが、.githubリポジトリ以外のTrust Policyの管理はすべて各リポジトリの管理者に任せている状態で、セキュリティチームとしてはほぼ関知せずに利用してもらえています。発端であった大きなGitHub Appも無事解体でき、10Xの開発環境におけるセキュリティリスクをひとつ減らすことができました。

しかしながら、運用やOcto STSそのものの選択において注意するべき点もあります。

Trust Policyはよくできているが慣れは必要

GitHubのOIDCに慣れている人であればTrust Policyの記法や、記載している内容が何を意味するかはすぐわかるのですが、不慣れな場合は少しとっつきにくい印象を持つかもしれません。ID TokenのSubjectに何が入るかなどは場合によっては実際に動かす環境で確認した方が早い場合もあり、良いTrust Policyを書けるようになるまでには一定の習熟が必要と感じています。とはいえ、よくあるユースケースは社内ドキュメントや既存のTrust Policyを真似すれば済むことが多く、生成AIとの相性も良いので、実務上は大きな学習コストを払うことなく十分に使える印象です。

Default branchの保護

Trust PolicyはDefault branchに配置されているもののみが信頼されるというのは大きな強みであるものの、これはDefault branchが適切に保護されていることを前提としています。必要に応じてOrganization rulesetでDefault branchの保護を強制することも併せて検討する必要があるでしょう。

リポジトリ完結で強いパーミッション付きのTokenを作れてしまう

これは事実上、セルフホストをしてAppに付与するパーミッションを限定したかった理由のひとつでもあるのですが、リポジトリのTrust Policyでリクエストできるパーミッションと、組織のTrust Policyでリクエストできるパーミッションに差異がありません。これは何を意味するかというと、単一のリポジトリで完結するかたちで、例えばOrganization全体に影響があるような強いパーミッションを持つGitHub App Tokenを生成できるということです。

10Xの場合は、そもそも渡して困るパーミッションをGitHub Appに持たせていないので、この問題は無視できています。ManagedのOcto STSを使う場合や、セルフホストであっても強いパーミッションを渡す場合は、Organization rulesetで"Required review by specific teams"の機能を利用して、各リポジトリのTrust Policyの配置に特定のチームのレビューを必須にすることを検討するのがよいと思います。

全然バージョン切ってくれなくてつらかった

octo-sts/appocto-sts/actionともに2025年12月に新しいバージョンを切ってくれたのですが、それまでは全然新しいバージョンを切ってくれず、欲しい変更が入っているコードを得るためにcommit hashを指定するしかない状況が長らく続いていました。今後もこの問題が残るかどうかはわかりませんが、OSS特有の問題としてこのあたりを許容できるかどうかは十分に吟味した方がよいです。

まとめ

膨らんでいくGitHub App Tokenのユースケースに対するひとつの解として、10Xで採用したOcto STSについて紹介しました。注意するべき点もいくつかあるものの、概して"いい塩梅"でセキュリティチームの手を離れてGitHub App Tokenの管理が各チームで自治されている印象で、良い選択をしたなと思っています。

仲間募集

開発者体験とセキュリティを両立しつつ、最高のプロダクトを一緒につくっていきたい気持ちに満ち満ちているセキュリティチームがいる10X、オススメですよ。

10x.co.jp