これができればGitHub Actions上級者だ!
この記事は株式会社ココナラ Advent Calendar 2024 9日目の記事です。
SREチームマネージャーのよしたくです。
ココナラに入ってからGitHub Actionsをレビューする機会が多くなってきました。そこで今回は「わかってるな」と思わせるGitHub Actionsの書き方をいくつか紹介していきます。
比較的簡単に「わかっている風」にできるものから記載します。
サードパーティのアクションをコミットハッシュで指定する
Not Good
- uses: actions/checkout@v4
Better
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
サードパーティのREADMEには前者で記載されていることがほとんどですが、タグではなく利用したいコミットハッシュで指定するというものです。
この記載だとDependabotやRennovateがうまく適用されないのでは、と感じるかもしれませんが、ちゃんとコミットハッシュでの更新をしてくれます。よくできていますね。
どうしてコミットで指定することがよいのか
一言で表すと、タグはMutable、コミットハッシュはImmutableだからです。
ブランチやタグは例えば一度消してから同名で作り直すなどすることで、実態が以前と異なる状態を作り出すことが可能です。仮にサードパーティアクションに攻撃を仕掛けられたとしてもコミットハッシュは不変であるため、その影響を即時に受けることがありません。
もちろん利用するコミットハッシュのバージョンが信用できるかどうかは事前に調べる必要があります。
サードパーティ製のものをどこまで信用するのか
個人・会社によって意見が分かれるところです。
Web系の企業でしたら、アタリマエのように利用するでしょうし、業務系の企業では一切禁止、そもそも自社企業管轄のSelfHostedRunnerが最低条件ということも多々あるでしょう。
サードパーティ製が使用禁止の場合は車輪の再発明をせざるを得なく、自社Organizationに専用のActionsを作成し、それを参照するようにするほかありません。なおOrganizationの設定でどの範囲を利用可能とするかをポリシーとして定めることが可能です。(参考)
信頼できない入力値はそのまま使わない
Not Good
- uses: hoge@fuga
with:
message: |
pr title is ${{ github.event.pull_request.title }}
Better
- env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE"
内部式の評価
${{hoge}}
のように内部式で囲まれている場合、hogeの部分が評価された結果の値に置き換えられるため、この部分にコマンドを打ち込める可能性のある場合はコマンドインジェクションに対して脆弱性を持っていることになります。この場合ですと、例えばPRタイトルに悪意あるコマンドを設定することで、Actionsノードでそのコマンドが評価実行されてしまいます。
主な対処としては以下が一般的かと思います。
- 信頼できない入力変数値は中間環境変数に入れ込む
- ダブルクオートで囲んで、文字列として扱う
Environmentを利用する
Not Good
steps:
auth_aws_prodcution:
- if: ${{ github.ref_name == 'main' }}
name: auth production
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: ${{ secrets.IAM_ROLE_PRODUCTION }}
auth_aws_develop:
- if: ${{ github.ref_name == 'develop' }}
name: auth develop
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: ${{ secrets.IAM_ROLE_DEVELOP }}
Better
job_name:
environment: ${{ github.ref_name }}
- name: auth production
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: ${{ secrets.IAM_ROLE }}
Environmentの恩恵
うまく使いこなせるとさまざまな恩恵を享受することができます。
例えば、上記の例のように、ブランチによってデプロイ先のAWSアカウントやサービス名称が異なるケースは多くあると思います。そのような場合、
- ほとんど同じ表記だが、変数部分だけ違うworkflowファイルを2個以上作っている
- ファイルは1つなのだけどif文でなんとかする
といった手法をとっているケースが意外と多く見られました。もちろんこれでも動くのですが、いわゆる「共通でいいんだよな〜」という箇所を修正するときに「developのほうは直したけどproductionのほうは直し忘れた」といったケースがあるなど、少しメンテナンス性が悪いですね。
これを解決できるのが、Environmentで
- developという名前のEnvironment
-
IAM_ROLE
: hoge
-
- mainという名前のEnvironment
-
IAM_ROLE
: fuga
-
というような感じで設定してBetterに記載したようなフローを動かすと、IAM_ROLE
の値がdevelopブランチではhoge
、mainブランチではfuga
となります。つまり変数名は同じだけれどその実態を使い分けることが可能です。「変数が違うだけなんだよなあ」というケースにうまく対応できます。
ほかにも、
- このEnvironmentはこのブランチで動く時にしか使うことができないようにする
- このEnvironmentでdeployするときは承認を必要とする
といった便利な機能も組み込むことが可能です。
この機能をうまく利用してCI/CDフローが組めているとGitHub Actionsわかっている風を吹かせることができます。
timeoutを設定する
Better
- run: deploy.sh
timeout-minutes: 5
痛い目を見たことがあるか
ふつうGitHub Actionsでやるようなことは長くても数十分程度でしょう。ところがGitHub Actionsのデフォルトタイムアウト値は360分です。
シェルのループ処理に不備があって無限ループしてしまった、クラウドリソース設定の不備でずっと待ち状態になってしまった、などのケースでtimeoutを設定していないと360分はRunnerが走り続けることになります。
- 失敗に気づくことが360分後になってしまう可能性がある
- 場合によっては無駄な金額コストが発生する
などのデメリットを防ぐためにも設定しておくと無難です。
job単位、step単位のそれぞれに設定可能で、オススメは「jobに想定時間の1.5倍のtimeout値を設定」することです。
PATを新規作成しない、GithubAppを利用する
PAT(PersonalAccessToken)は便利で活用しやすいものではありますが、そのぶん漏えいリスクも発生します。
そこで用途によって以下を使い分けるのが得策です。
-
github.token
(secrets.GITHUB_TOKEN
) - GithubAppを利用した一時トークンの発行
本章についてはこちらの記事に詳しく記載があります。
快適なGitHub Actionsライフを
形から入るということも大切ですので、まずはサードパーティActionsをコミットハッシュ指定にするところから入ると良いと思います。一気に書き換えることが可能なCLIツールも出ていますね!
こういった小さなところから、主に
- メンテナンスのしやすさ
- 一定のセキュリティ対策
- 開発改善への寄与
の観点を考慮したGitHub Actionsを実装できるようになり、ゆくゆくは便利でpublicなActions(CompositeActions/ReusableWorkflowを含む)を書いてくれる方が増えるといいなあと思っています。
明日は@suzuki-maruchanさんによる Slack WorkflowをDeno Slack SDKで作ってみた です。
ココナラでは積極的にエンジニアを採用しています。
採用情報はこちら。
カジュアル面談希望の方はこちら。
Discussion