fj.doany.io。git のホスティングと Forgejo Actions。GitHub Actions の課金を避けて、CI をこのクラスタで回すために置いた。
| ファイル | 中身 |
|---|---|
| forgejo.yaml | Forgejo 本体(最新を追う)。chart は使わない(理由はファイルの先頭) |
| postgres.yaml | DB。k8up が pg_dump を取る |
| runner.yaml | Runner(v13)+ docker(dind)。runs-on: ubuntu-latest をそのまま拾う |
| httproute.yaml | 公開経路。SSH は出さない(clone / push は HTTPS + トークン) |
| forgejo-secrets.yaml | Infisical から Secret 3 つ |
| argocd-creds.yaml | ArgoCD が Forgejo のリポジトリを読むための資格情報 |
バックアップは ../k8up/schedules.yaml(毎日 14:45 UTC)。
-
Infisical に 2 つ入れる(同期より先に)
/forgejo/forgejo-db:postgres-password── 英数字だけのランダム(openssl rand -hex 32など)。 postgres は最初の起動でしかパスワードを設定しないので、後から変えるなら DB 側も変える/forgejo/forgejo-oauth:key=b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e(Main のクライアント ID。秘密ではなく bootstrap/auth にも平文で書いてある)、secret=${prod.auth.auth-secrets.oidc-client-secret}(値は写さず参照)
-
Entra のアプリ登録 Main のリダイレクト URI(Web)に
https://fj.doany.io/user/oauth2/entra/callback── 2026-09-15 にaz ad app updateで追加済み -
main にマージ → ArgoCD が同期
-
管理者を作る(1 度だけ)。Entra のメールと同じにしておくと、Entra でログインしたときに自動で紐づいて管理者になる。 パスワードは捨てる(表示させない)。組織
doaとチームmembers(読み取り・すべてのリポジトリ)も作るkubectl -n forgejo exec deploy/forgejo-web -c forgejo -- \ gitea admin user create --admin --username info --email info@doany.io --random-password >/dev/null
members を作る前にログインした人は、次のログインで入る
-
Runner の秘密を入れる: Infisical
/forgejo/forgejo-runnerにrunner-secret= 16 進 40 文字のランダム。 値は画面に出さずに作って貼る(WSL ならopenssl rand -hex 20 | tr -d '\n' | clip.exe。貼ったらクリップボードは消す)。 管理画面での操作は要らない。Forgejo と Runner の Pod が入れ替わり、Forgejo の init が同じ秘密で Runner を登録し (forgejo.yaml の setup.sh の 5)、Runner は同じ秘密から uuid / token を出して繋ぐ(runner.yaml)。/admin/actions/runnersにforgejo-runnerが「Idle」で出れば済み。 秘密を変えると別の Runner として登録される(UUID は秘密の先頭 16 文字から作る)。古い方は一覧で消す
方針: プライベートは GitHub に置かない(2026-09-15)。GitHub Actions の課金はプライベートにだけ掛かるので、 公開リポジトリ(gitops・blog など)は GitHub のままでよい。対象は shadai / tamasagashi / worklog-cloud(noren は閉じた)。
Forgejo が唯一の置き場になる。 バックアップは k8up(R2)の 1 日 1 回だけなので、手元の clone も捨てないこと。
先に 1 回だけ:
- Forgejo に組織
doaを作る - ArgoCD 用のアクセストークンを作り(
read:repositoryとread:organization)、Infisical/argocd/forgejo-repo-credsにurl=https://fj.doany.io/doa/username/password= トークン で入れる(argocd-creds.yaml)。 スコープは repository・organization・issue の読み取り。 issue が無いと、非公開リポジトリができた時点で ApplicationSet がtoken does not have at least one of required scope(s): [read:issue]で止まる(2026-09-15) bootstrap/argocd/repos.yamlに Forgejo の generator を足す PR をマージする(Forgejo とトークンが揃ってから。 API に届かないと ApplicationSet 全体の生成が止まる)。 2026-10-05 にrepos-forgejoという別の ApplicationSet に分けた。 同居していると、まっさらな クラスタ(Forgejo が空で、トークンもまだ無い)で GitHub 側の Application まで 1 本も作られず、 Forgejo 自身もデプロイされないので抜けられなかった。いまは止まるのは Forgejo 側だけ
リポジトリごとに:
- Forgejo の「新しい移行」で組織
doaの下に取り込む(Issue / PR / リリースも)。非公開のまま入り、チーム members が読める - ワークフローを Forgejo 向けに直す(
.github/workflows/のままで読まれる)- イメージは
ghcr.io/danything/<name>→fj.doany.io/doa/<name>。push はワークフローのsecrets.GITHUB_TOKEN(Forgejo のトークン)で通る - クラスタが pull できるように、アプリの namespace に
imagePullSecretsを足す(Forgejo のread:packageトークンを Infisical からkubernetes.io/dockerconfigjsonで) - claude-review(GitHub App)のワークフローは消す。Forgejo では動かない
- イメージは
- Forgejo で CI とイメージの push が通るのを確かめる
- GitHub のリポジトリを消す。両方に
deploy/argocd.yamlがあると Application 名がぶつかる (ApplicationSet がreposとrepos-forgejoに分かれているので、2 つが同じ Application を奪い合う)。 ApplicationSet はpreserveResourcesOnDeletion: trueなので、Application が作り直されても Pod や PVC は消えない - ghcr.io の古いパッケージを消す
| 公開リポジトリ | 非公開リポジトリ | fork・PR | push・マージ | 管理画面 | |
|---|---|---|---|---|---|
| ログインしていない人 | 見える | 見えない | ─ | ─ | ─ |
| テナントの人(Entra、チーム members) | 見える | 読める | できる | できない | ─ |
| 管理者(Forgejo の管理画面で付ける) | 全部 | 全部 | できる | できる | できる |
- テナントのゲスト(もとは ERPNext のために招いた人)も tid が同じなので members に入る。 外したくなったら Forgejo 専用のアプリ登録を作り、 「割り当てが必要」にして人ごとに割り当てる(Main の設定を変えると、ロールを割り当てていない人が Main の後ろのアプリ全部から締め出される。 ../../docs/entra.md)
- Entra のアプリロール admin は Forgejo の管理者に連動しない(グループのクレームを tid に使っているため)。管理者は管理画面で付け外しする
- fork からの PR のワークフローは、管理者が承認するまで走らない(読み取り権限だけの人は承認が要る。Forgejo の既定)。 Runner は privileged なので、中身を見ずに承認しないこと
- Runner の docker は privileged。namespace の PSA が
privilegedなのはこのため - dind のイメージ置き場は
emptyDir。Pod が入れ替わると次のジョブで pull し直す - Cilium は vxlan なので dind の MTU を 1400 にしてある。1500 に戻すと大きい pull が途中で止まる
公開にしてはいけない。 パッケージ(コンテナイメージ)の読み取り権限はリポジトリではなく組織の公開範囲で決まり、
公開の組織だと非公開リポジトリのイメージでも匿名で pull できる(services/packages/perm.go の
HasOrgOrUserVisible。2026-09-15 に shadai のイメージが匿名で取れるのを見つけて limited にした)。
ワークフローの自動トークン(GITHUB_TOKEN)ではパッケージに書けない(Forgejo 15。services/packages/perm.go に
TODO: ActionUser permission check とあり、Actions のユーザーは組織のメンバー扱いにならない)。
組織 doa の Actions のシークレット REGISTRY_TOKEN(bot ユーザー forgejo-bot の write:package だけのトークン)で
docker login fj.doany.io -u forgejo-bot する。下の承認済みインテグレーションで秘密を無くせることは確かめたが、
forgejo-bot のものは管理の手段が無いので見送っている(下の表)。
CI から Forgejo の API・git・レジストリに長く生きる秘密なしで入る仕組み。外から来た JWT のクレームを、ユーザーごとに
登録したルールで確かめ、合えばそのユーザーとして(登録したスコープとリポジトリの範囲で)通す。
ドキュメント。設定(app.ini)は要らない
([authorized_integration] の既定で外の発行者に取りに行ける)。既定では取りに行けるのは外のホストだけで、
クラスタの中・プライベートの IP・ループバックには行かない(ALLOW_LOCALNETWORKS が false。services/auth/authorized_integration.go
の initAuthorizedIntegrationHTTPClient)。リダイレクトも追わず、jwks_uri は発行者と同じホストに限る。
もっと絞るなら FORGEJO__authorized_integration__ALLOWED_DOMAINS: token.actions.githubusercontent.com
(Forgejo Actions の JWT は中で確かめるので、この一覧に要らない)。
- 誰として動くか: インテグレーションの持ち主。push・PR・マージの予約も持ち主がしたことになるので、
付属の actions トークンと違ってワークフローも起きる(
services/actions/notifier_helper.goはIsActions()のときだけ止める) - 渡し方: API は
Authorization: Bearer <JWT>(token <JWT>でも通る)。git とレジストリは Basic のパスワードに JWT (ユーザー名は見ない。routers/web/web.goのbuildGitAuthGroup、routers/api/packages/api.goのContainerRoutes) - JWT の寿命: Forgejo Actions のものは 1 時間(
[actions].ID_TOKEN_EXPIRATION_TIME)。レジストリのトークンもそれに縛られる - リポジトリを絞ると、スコープは repository / issue しか選べず、管理者の権限は効かない(
services/authz/access_token.go)。 レジストリ(package)と組織の操作は「すべて」で作る - Forgejo Actions のジョブは
enable-openid-connect: trueで JWT を取る。fork からの PR では取れない。 JWT のworkflowはname:ではなくファイル名(run.WorkflowID。2026-10-07 に実ジョブの JWT で確かめた。GitHub とは違う)。 再利用ワークフロー(uses: ./...)のジョブは、呼ばれる側のファイルの設定が効き、JWT のworkflowは呼ぶ側のファイル名になる - 境界は main に書ける人: main のワークフローを書き換えられる人は、その JWT で持ち主として書ける(シークレットのときと同じ)。 doa の main に書けるのは bots と Owners だけ(members は読み取り)
| 状態 | 持ち主 | 名前 | 発行者 | クレームのルール | 範囲・スコープ | aud の置き場 | 代わりに消したもの |
|---|---|---|---|---|---|---|---|
| 使っている(2026-10-07) | yui | repo-config | GitHub Actions | GitHub の 5ym/repo-config、refs/heads/main、forgejo-settings.yml、push / schedule / workflow_dispatch |
すべて・write:organization write:repository |
GitHub の 5ym/repo-config の変数 FORGEJO_AUDIENCE |
repo-config の secrets FORGEJO_TOKEN と yui のトークン repo-config-settings |
| 見送り | forgejo-bot | doa-registry | Forgejo Actions | doa の todoroku・tamasagashi・worklog-cloud、refs/heads/main、publish.yml / fetch-latest.yml、push / workflow_dispatch / schedule |
すべて・write:package |
組織 doa の変数 REGISTRY_AUDIENCE |
(今は組織の REGISTRY_TOKEN) |
| 見送り | forgejo-bot | todoroku-deploy | Forgejo Actions | todoroku、refs/heads/main、publish.yml、push / workflow_dispatch |
todoroku だけ・write:repository |
todoroku の変数 DEPLOY_AUDIENCE |
(今は todoroku の DEPLOY_TOKEN) |
forgejo-bot の 2 つは見送った(2026-10-07)。下のとおり forgejo-bot のものは 16.0.5 では画面にも CLI・API にも 管理の手段が無く、止める・作り直すには DB を直接触ることになる。秘密を減らす効果より、後から手で扱えない設定を本番に 増やす損が大きい。v17 以降で管理の手段が揃ったら、下のコマンドで作り、ワークフローの PR(doa/tamasagashi#106・ doa/todoroku#368・doa/worklog-cloud#148。閉じてある)を出し直す。
aud はインテグレーションを作ると決まる(u:<ユーザー ID>:<UUID>)。秘密ではないので変数に置く。
doa にイメージを出すリポジトリを足したら、doa-registry のリポジトリ ID も足す(変更の CLI が無いので作り直し。aud が変わる)。
forgejo-bot は画面にログインできない(ENABLE_INTERNAL_SIGNIN: "false")ので、forgejo-bot のものは本番の CLI で作る。
一覧・変更・削除の CLI と API は無い(16.0.5)。急いで止めるなら、forgejo-bot を組織 doa の bots チームから外す
(パッケージもリポジトリも書けなくなる。トークンも巻き込む)。prohibit_login は API と git は止めるが、
レジストリは止めない(routers/api/packages は見ていない)。消すのは DB の authorized_integration
(と authorized_integ_resource_repo)の行。
# (見送り中。作るときのため)
# doa-registry(リポジトリ ID は 1 = todoroku、3 = tamasagashi、4 = worklog-cloud。組織 doa の ID は 2)
kubectl -n forgejo exec deploy/forgejo-web -c forgejo -- forgejo admin user create-authorized-integration \
--username forgejo-bot --name doa-registry \
--description "doa の main の publish がイメージを push する" \
--issuer urn:forgejo:authorized-integrations:actions \
--claim-eq repository_owner_id=2 --claim-eq ref=refs/heads/main \
--claim-in repository_id=1,3,4 --claim-in workflow=publish.yml,fetch-latest.yml \
--claim-in event_name=push,workflow_dispatch,schedule \
--scope write:package --repo all
# todoroku-deploy
kubectl -n forgejo exec deploy/forgejo-web -c forgejo -- forgejo admin user create-authorized-integration \
--username forgejo-bot --name todoroku-deploy \
--description "todoroku の publish が deploy-bump を push して PR を作り、マージを予約する" \
--issuer urn:forgejo:authorized-integrations:actions \
--claim-eq repository_id=1 --claim-eq ref=refs/heads/main --claim-eq workflow=publish.yml \
--claim-in event_name=push,workflow_dispatch \
--scope write:repository --repo doa/todoroku出力の audience を変数に入れる(POST /api/v1/orgs/doa/actions/variables/REGISTRY_AUDIENCE、
POST /api/v1/repos/doa/todoroku/actions/variables/DEPLOY_AUDIENCE。本文は {"value": "<audience>"})。
yui のもの(repo-config)は画面(設定 → 承認済みインテグレーション → 汎用 JWT)で作る。発行者は
https://token.actions.githubusercontent.com、範囲は「すべて」、スコープは organization と repository を「読み取りと書き込み」。
ルール(GitHub の 5ym/repo-config の ID は 1315588536、5ym は 13718335。クレームの値は文字列):
{"rules": [
{"claim": "repository_id", "compare": "eq", "value": "1315588536"},
{"claim": "repository_owner_id", "compare": "eq", "value": "13718335"},
{"claim": "ref", "compare": "eq", "value": "refs/heads/main"},
{"claim": "workflow_ref", "compare": "eq", "value": "5ym/repo-config/.github/workflows/forgejo-settings.yml@refs/heads/main"},
{"claim": "event_name", "compare": "in", "values": ["push", "schedule", "workflow_dispatch"]}
]}ホストからは Gateway の 443 に繋がらないので、node-access.yaml の中継 + ホストの /etc/hosts
(10.43.200.10 fj.doany.io)+ /etc/rancher/k3s/registries.yaml の資格情報(info の read:package)。
アプリ側に imagePullSecrets は要らない。Talos では ../../talos/README.md。
組織単位でマージ方法を縛る設定は Forgejo に無い(15 時点)。既定のマージ方法は forgejo.yaml の
DEFAULT_MERGE_STYLE、許すマージ方法はリポジトリごと。リポジトリを作ったり取り込んだりしたら、これを当てる:
curl -X PATCH -H "Authorization: token $T" -H 'Content-Type: application/json' \
-d '{"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_fast_forward_only_merge":false,"allow_squash_merge":true,"default_merge_style":"squash","default_delete_branch_after_merge":true}' \
https://fj.doany.io/api/v1/repos/doa/<name>2026-09-15 に shadai / renovate / tamasagashi / worklog-cloud へ当てた。