Terraform で GitHub Actions の secret を管理する

Posted on

GitHub Actions の secret を手動で追加していくと、あとからコンテキストを追いかけるのが大変です。追加した値は平文で見ることができないので、変更するのもためらいます(平文の値が残っていないと何かあっても戻せない)。なので、コードで管理したいと思っていました。

そんなとき、Terraform の GitHub provider で secret の管理ができることを知ったので試してみました。

今回は repository secret で試していますが、organization secret もほぼ同じ手順です。

暗号化された値の生成

github_actions_secret には plaintext_valueencrypted_value を指定できますが、平文でコミットしたくないので encrypted_value を使うケースがほとんどだと思います。

"Create or update a repository secret" で述べられていますが、GitHub Actions の secret は LibSodium で暗号化されています。 encrypted_value に渡す値も同じように LibSodium で暗号化する必要があります。

まず repository の public key を取得します。

$ curl \
  -u "manabusakai:$GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  https://api.github.com/repos/manabusakai/(repository)/actions/secrets/public-key
{
  "key_id": "568250167242549743",
  "key": "WYmnVsmInmBH/nxkxsTk7Z0tQ7vxonruUd9C6VVra3w="
}

次にローカルで平文を暗号化します。 "Create or update a repository secret" にいくつかの実装サンプルが載っています。ここでは Ruby を使います。

libsodium ライブラリと Gem をインストールします。

$ brew install libsodium

$ echo 'gem "rbnacl"' > Gemfile
$ bundle install

Ruby スクリプトで暗号化された値を生成します。

require "rbnacl"
require "base64"

key = Base64.decode64(ARGV[0])
public_key = RbNaCl::PublicKey.new(key)

box = RbNaCl::Boxes::Sealed.from_public_key(public_key)
encrypted_secret = box.encrypt(ARGV[1])

puts Base64.strict_encode64(encrypted_secret)

今回は "foobar" という値を暗号化します。第 1 引数が public key、第 2 引数が暗号化したい文字列です。

$ bundle exec ruby generate_encrypted_value.rb "WYmnVsmInmBH/nxkxsTk7Z0tQ7vxonruUd9C6VVra3w=" "foobar"

Terraform で secret を追加する

暗号化された値が準備できたら Terraform で secret を追加します。ミニマムのコードだと次のようになります。

terraform {
  required_providers {
    github = {
      source  = "integrations/github"
      version = "~> 4.0"
    }
  }
}

provider "github" {}

data "github_repository" "example" {
  full_name = "manabusakai/(repository)"
}

resource "github_actions_secret" "test_secret" {
  repository      = data.github_repository.example.name
  secret_name     = "TEST"
  encrypted_value = "(snip)"
}

apply して "Settings > Secrets" の画面に出てくれば OK です!

GitHub Actions secrets

値の確認

念のため次のような workflow を追加して、期待通りの値が設定されているか確認しました。

name: Verify

on: push

jobs:
  echo:
    runs-on: ubuntu-latest
    steps:
      - name: Vefify
        if: ${{ env.TEST == 'foobar' }}
        run: echo "OK"
        env:
          TEST: ${{ secrets.TEST }}

まとめ

暗号化された値を作るのがちょっと面倒ですが、それ以外は普通の Terraform と同じです。 IaC と同じライフサイクルで secret を管理できるのは素晴らしいですね!