くりにっき

フルスタックキュアエンジニアです

gcp-secretmanagerenvを作った

gcp-secretmanagerenvについて

GCPのSecret Managerのラッパみたいなやつで、 gcp-kmsenv のSecret Manager版です。

github.com

作った経緯

本番環境(GCP)ではSecret Managerを使うのでいいんだけど、ローカル開発時は環境変数を使いたかった *1 のですが、Secret Managerの値の取得と環境変数の取得を透過的に扱う部分をアプリケーションコード内に書きたくなくて先にライブラリ化しました。

使い方

READMEからの抜粋ですがこれがだいたい全てです。

package main

import (
    "context"
    "github.com/sue445/gcp-secretmanagerenv"
)

func main() {
    projectID := "gcp-project-id"
    c, err := secretmanagerenv.NewClient(context.Background(), projectID)
    if err != nil {
        panic(err)
    }

    // get from environment variable
    value, err := c.GetValueFromEnvOrSecretManager("SOME_KEY", true)
    // => return value from environment variable or Secret Manager

    // When key is not found in both environment variable and Secret Manager, returned empty string (not error)
    value, err := c.GetValueFromEnvOrSecretManager("INVALID_KEY", false)
    // => ""

    // When key is not found in both environment variable and Secret Manager, returned error
    value, err := c.GetValueFromEnvOrSecretManager("INVALID_KEY", true)
    // => error
}
  • GetValueFromEnvOrSecretManager の第1引数は検索するkey
    • 環境変数にkeyがあればその値を、SecretManagerにkeyがあればその値を返す
    • 探索順は環境変数 -> Secret Managerの順
  • projectIDが渡されなければSecretManagerの呼び出し自体を行わない
    • 本番環境のみ GCP_PROJECT のような環境変数を設定しておくような想定
  • GetValueFromEnvOrSecretManager の第2引数は必須とみなすかどうかのフラグ
    • true ならkeyが環境変数とSecret Managerの両方になかった場合にはエラーを返す
    • false ならkeyが環境変数とSecret Managerの両方になかった場合にはエラーを返さない(空文字を返す)

*1:OSSとして公開する可能性があるのでローカル開発時にサービスアカウント必須にしたくなかった

Cloud Functions CI/CD俺的ベストプラクティス

AWS Lambda CI/CD俺的ベストプラクティス - くりにっき のCloud Functions版です

sue445.hatenablog.com

最近Cloud Functions使うことがあってCI面からフレームワークなどを検討しました。AWS Lambdaほどガッツリ使ってないので比較対象が少ないのはご愛嬌

Terraform

https://www.terraform.io/

Deployment Manager

https://cloud.google.com/deployment-manager?hl=ja

Serverless Framework

https://www.serverless.com/

  • 今回比較した中では一番まとも
  • デプロイ関係の面倒くさい処理(GCSのバケットを作ってソースをzipに固めてアップロードとか)はよしなにやってくれる
  • プラグインが充実してるのも嬉しい
  • 不満点
    • デプロイのdry runや serverless.yml のvaridateができないところ
      • master自動デプロイにしてるとmasterにマージするまで動作確認できない
    • Serverless Framework + Cloud Functions + Goで書いてるんだけど情報が英語含めてほとんどないので直感で書いてる
  • 凝ったことをやりたくなると(例:Cloud Functionsで使うPubsub topicをServerless側で作る) serverless.yml にDeployment Managerのymlを書く必要があるのだが、それはAWSで分かってたことなので諦めてた

GitHub Actions上でPRを作る時はsecrets.GITHUB_TOKENは使わない方がよさそう

事象

GitHub Actions上で使える secrets.GITHUB_TOKEN だと別のジョブを起動できないというのが一番の理由。

https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token

具体的にどういうケースで困るのかというと、GitHub Actions上でPRを作った場合にそのPRに対するビルドが行われません。

普通だったらコミットIDの左側に✔が出るはずなんだけど出ない図

pushやPRなどのビルドをGitHub Actions以外(例:CircleCIなど)で行っている場合には問題ないです。

解決策

いくつか案はあるのですが現状だと GitHub App Token を使うのがいいと思ってます。

2023/01/08 追記:このエントリ執筆時点では3rd party製の https://github.com/tibdex/github-app-token しかなかったけど、現在は公式で https://github.com/actions/create-github-app-token があるので後者を使うのがよさそう。(引数名がちょっと違う以外は使い勝手は一緒)

App作成の手間はあるもののインストールするリポジトリを指定できるので権限の分離ができるのが一番の理由です。 (パーソナルアクセストークンだと業務で所属してるorg含めて自分が見れる全リポジトリが対象になるので漏洩時のリスクが怖い)

実際に動かした図

sue445-botGitHub App)でPRを作ったのでGitHub Actionsでビルドが実行されるようになってます

上記AppだとパーミッションはActionsとPull requestsにRead & writeをつけたけどActionsの方は不要だったかもしれないです。

2020/09/12追記:Pull requestsのRead & writeだけで良かった

go-mod-tidy-prでも同様の問題を抱えていたのでGitHub App Token推奨にしました。

https://github.com/sue445/go-mod-tidy-pr#note-warning

謝辞

という情報を プリッカソン のSlackで知って便利

2020/9/1 22:50 追記

GutHub Appを作った場合は今度はAppの秘密鍵の管理を考える必要がありますが、Organizationであれば Organization secrets が使えるのでこの手の秘密鍵の管理に便利そうです。(ってことを ruby-jp Slack で話していて思い出した)

github.blog

#ginzarails にRuby on CIで発表した

RubyKaigi2020 用に準備してた資料の供養として発表させていただきました。

僕が知ってるCIの知見や2020年時点でのCIのトレンドはだいたい詰め込めたと思っています。

connpass

ginza-rails.connpass.com

スライド

gcp-kmsenvを作った

github.com

モチベーション

condo3app.yaml 内に Cloud KMS で暗号化した秘匿値を埋め込みやすくするために作ったモジュールをライブラリ化しました。

とはいえGoogle App Engine専用というわけではなく、GCP + GoであればCloud Functionsとかでも使えるはずです。

使い方

READMEからかいつまんで説明すると下記

  1. 秘匿値を gcloud kms encrypt で暗号化してBASE64に変換
  2. KMS_ACCESS_TOKEN のように KMS_ を先頭に付けた環境変数として登録
    • KMSで暗号化済なのでこの変数はgitにコミットしても問題ない
  3. GetFromEnvOrKms で呼び出す
    • この時接頭辞の KMS_ は不要

FAQ

Q. Secret Manager使えばいいのでは?

本番環境ではKMSで暗号化された値を使いたいけどローカルでは暗号化していないプレーンな値を環境変数で渡したいことがよくあるので、暗号化していないプレーンな環境変数とKMSで暗号化した環境変数を透過的に扱えるようにしています。( os.Getenv と似たようなインターフェースにしたのも同じ理由)

質問をする技術

以前社内に書いたポエムなんだけど年に1回くらい引用したくなるので公開した

tl;dr;

テンプレ

【質問内容】

【やりたいこと or 今困ってること or 質問の意図】

質問をする時はゴールを提示する【MUST】

  • なにかやりたい
  • けど実現できない、うまくいかない
  • それで質問する

って感じに、質問をする動機としてまず やりたいことありき のはずなので、それを提示すべきです

理由1

質問される側(以下回答者)は質問内容がふわっとしていると色々なケースを想定して回答を組み立てます *1

例「Aの場合は~だけど、Bの場合は~」

こういう場合回答者は質問者のやりたいことを想像しながらいろんな場合分けをする必要があるので、頭を使うので疲れるし、回答を組み立てるのに時間もかかります。

酷い場合だと5つくらい場合分けして回答したことがあります。

こういう時、僕は自分の中で 超能力で回答 という言葉を使ってます。(自分以外で使ってる人は見たことないので同意は得られないことは覚悟してます)

超能力を発揮するのは疲れます。

僕も色々質問を受ける立場ですがゴールが明示されてた方が圧倒的に回答しやすいです。

理由2

初心者によくありがちなのですが、やりたいことに対して質問内容が的外れということが往々にしてあります。

  • 質問者「これってどうやるんですか?」
  • (回答者は頑張って回答を組み立てる)
  • 回答者「こうやるんですよ」
  • 質問者「さっきのだとうまくいきませんでした」

はっきり言ってこういうのは時間の無駄なので先にやりたいことを明示してもらえれば

  • 質問者「こういうことをやりたいんですがここでちょっと詰まってます。これってどうやるんですか?」
  • 回答者「んー。それ、意味ない(質問内容が実現されてもゴールにたどりつけない)っぽいw」

って感じで時間のロスを防げます

コンテキストを詳しく共有する【SHOULD】

*2

質問する側は質問内容に関して長時間思考してるのでコンテキストを理解しています。

しかし回答者はそうとは限らないので可能な限り詳しく書くべきです。

実行したコマンドと実行結果を全て書くくらいが丁度いいと思ってます。

質問内容や人によってはヒアリングしながらコンテキストを少しずつ理解するというのもありそうですが、僕は圧倒的に最初にコンテキストを詳しく書くしそういう風に質問された方が嬉しいです。

チャットとかだと横で見た人が回答しやすいってのもあります。

あと質問内容をまとめる段階で頭の中で問題が整理されるので、質問するために文章を書いてたら答えが分かるという副産物もあります。

期待してた結果(expect)と実際の結果(actual)を書く【IMO】

*3

これはどっかのOSSのissueテンプレートで見たことあるやつ。

質問内容によっては「こういうことをやっててこういう結果になってほしいんだが全然違う結果になるんだが理由がよく分からん」ってことがあると思います。

そういう場合には期待してた結果と実際の結果を書くと質問文も組み立てやすいし、回答者も回答しやすいです

2020/7/22 12:30追記

ブコメレス

id:obatakanae

“こういう場合回答者は質問者のやりたいことを想像しながらいろんな場合分けをする必要がある” それならそこで聞き返せばいいんでないの?そういうことができない状況を想定しているのだろうか?

すぐ後に

質問内容や人によってはヒアリングしながらコンテキストを少しずつ理解するというのもありそうですが、僕は圧倒的に最初にコンテキストを詳しく書くしそういう風に質問された方が嬉しいです。

って書いているように好みの問題だと思います。

チャットだと非同期でのやりとりになってすぐに返信が返ってくる(返せる)とは限らないという理由で、僕は最初にまとまって書かれている方が嬉しいです。

id:jmako

このブログ主は自分視点で考えてるが、相手視点で考えると全く違った発想になる。この本がためになるはず。"「良い質問」をする技術" 是非読んでみて。

Kindle版ポチりました!

2020/7/22 19:00追記

ブコメレス

id:sippo_des

ググれカス、一人で生きていけるようになれ、とか、そんなこと聞くな、とか言われ続けた結果なのかな。 人から与えられた経験とか。交渉とか。

「質問する時はちゃんと準備することでお互いに円滑にコミュニケーションが進むよ」っていうのがこのエントリの主題です。そんなことは一言も言っていないしそんな経験もしたこともないです。

根拠もなく勝手な想像をするのはやめてください。

*1:あくまで自分はこうしています。主語がでかすぎたかもしれないけど前職で聞いたら何人か同じことやってたっぽい

*2:MUST(絶対こうしろ)よりはニュアンス弱めて「なるべくこうすべき」くらいのニュアンス

*3:In My Opinionの略でPullRequestでよく使うスラング。「自分ならこうするけどどうよ?(強制はしない)」くらいのニュアンス

rubocop-itamae v0.1.3とrubocop_auto_corrector v0.4.1をリリースした

rubocop-itamae v0.1.3

https://github.com/sue445/rubocop-itamae/blob/master/CHANGELOG.md#v013

rubocop 0.87.0で v1 Upgrade Notes が出ていたので自分のcopでもv1形式のシンタックスに追従。

rubocopのカスタムcopを作ってる人はv1 Upgrade Notesを読んで対応しといた方がよさそう。

rubocop_auto_corrector v0.4.1

https://github.com/sue445/rubocop_auto_corrector/blob/master/CHANGELOG.md#v041

同じくrubocop 0.87.0関係の対応。

v1 Upgrade Notesだとカスタムcopでautocorrectを実装する時に autocorrect メソッドを実装するのではなく AutoCorrector moduleを extend するようになるのですが *1、そういうcop*2がいる場合にWeekly buildがコケてたので対応。