🚀

goでgithub actionsのsecretをencryptしてterraformでapplyする

2021/07/18に公開

つい先日terraformでgithub actionsのsecretを管理できることを知り、goでencrypt -> terraform apply -> actionsで動作確認まで行ったのでまとめ。

terraformのoutputでrepositoryのpubkeyを取得

data "github_repository" "example" {
  full_name = "seita-uc/example"
}

data "github_actions_public_key" "example" {
  repository = data.github_repository.example.name
}

output "public_key" {
  value = data.github_actions_public_key.example.key
}

outputでbase64 encodedのrepository public keyが取得できる。

pubkeyで任意の文字列をencryptする

github actions apiのdocumentで述べられている通り、github actionsのsecretはLibSodiumという暗号ライブラリによって作成されているので、このライブラリの暗号方式に準拠した方法でsecretを作成してあげる必要があります。

documentでも複数言語でのsnippetが提示されていますが、goのsampleは今のところないので、terraformのgithub actionsのdocumentで紹介されているGo '/crypto/box' moduleを用いてsecretのencrypted valueを作成します。

以下の<base64-encoded-github-repository-public-key>に上のstepで取得したbase64 encodedのrepository public keyを渡せば、pubkeyで署名したplain-text-secretをbase64でencodeした値が得られます。

package main

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"

	"golang.org/x/crypto/nacl/box"
)

func main() {
	decodedPubKey, err := base64.StdEncoding.DecodeString("<base64-encoded-github-repository-public-key>")
	if err != nil {
		panic(err)
	}

	var serverPubKey = new([32]byte)
	_, err = io.ReadFull(bytes.NewReader(decodedPubKey), serverPubKey[:])
	if err != nil {
		panic(err)
	}

	var (
		msg = []byte("plain-text-secret")
		out []byte
	)
	rawEncryptedValue, err := box.SealAnonymous(out, msg, serverPubKey, rand.Reader)
	if err != nil {
		panic(err)
	}
	fmt.Println(base64.StdEncoding.EncodeToString(rawEncryptedValue))
}

terraform apply

先ほどのterraformファイルに以下の定義を追加します。

resource "github_actions_secret" "example_secret" {
  repository      = data.github_repository.example.name
  secret_name     = "example_secret_name"
  encrypted_value = "<上で取得した値>"
}

actionsでsecretを確認

以下のactionをdispatchして設定したsecretがmatchするか確認できます。

name: Echo
on: workflow_dispatch
jobs:
  echo:
    runs-on: ubuntu-latest
    steps:
      - name: Echo
        if: ${{ env.EXAMPLE_SECRET_NAME == 'plain-text-secret' }}
        run: echo "match"
        env:
          EXAMPLE_SECRET_NAME: ${{ secrets.EXAMPLE_SECRET_NAME }}

Reference

github provider

https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret

github actions api

https://docs.github.com/en/rest/reference/actions#example-encrypting-a-secret-using-nodejs

Discussion