retlat's blog

Secret Manager のベストプラクティスはちゃんと読んだ方が良いという話

これは Google Cloud Platform Advent Calendar 2021 4 日目の投稿です。

今日は Secret Manager についてです。
今年は無料枠に追加されるなどアップデートがありました。
ただその中にはベストプラクティスでは使うのは避けた方が良いとされているものがあります。

例えば Cloud Run および Cloud Functions では、 Secret を

  • 環境変数に設定する
  • ファイルとしてマウントする

という機能が提供されるようになりました

ところがこれらは

ファイル システムでシークレットにアクセスできると、攻撃者がシークレット マテリアルを読み取ることができるため、ディレクトリ トラバーサル攻撃などのアプリケーションの脆弱性が深刻化する可能性があります。

環境変数を介してシークレットが使用されると、デバッグ エンドポイントの有効化やプロセス環境の詳細をログに記録する依存関係などの構成ミスにより、シークレットが漏洩する可能性があります。

という理由で可能な限り避けるように書かれています。

ただ現実として環境変数で設定できると開発者ごとの違いを吸収しやすいのは事実です。
そこで Cloud Run には実行するコンテナに自動で設定される環境変数があるので、

package main

import (
	"context"
	"os"

	secretManager "cloud.google.com/go/secretmanager/apiv1"
	secretManagerPB "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
)

func FetchPassword() string {
	// Cloud Run のみに存在する環境変数をチェックして
	_, ok := os.LookupEnv("K_SERVICE")
	if !ok {
		// 未設定の場合には環境変数から取得
		return os.Getenv("PASSWORD")
	}

	// 設定されている場合には Secret Manager にアクセス
	ctx := context.Background()
	c, _ := secretManager.Client(ctx)
	r := &secretManagerPB.AccessSecretVersionRequest{ Name: "1" }
	s, _ := c.AccessSecretVersion(ctx, r)
	return s.Payload.Data
}

といった形で使っていくのが良さそうだと思っています。

ということで良い機能だなと思っても 1 回立ち止まってベストプラクティスを読んでみる方が良いというお話でした (自戒を込めて)。

Post: 2021-12-04
Tags: Secret Manager

Node Native Addon を TypeScript で使うために型定義する

Node から OS の C API を触りたくて Native Addon を生成していて、使う側はいつも通り TypeScript でやりたい
ディレクトリ構成はこんな感じ

.
|- src
|  |- index.ts
|  `- sample.node
|- package.json
|- tsconfig.json
`- yarn.lock

Addon は sample 関数だけ export しているとして、 index.ts はこんな感じで import する

import { sample } from './sample.node'

sample()

そうするとモジュール名の箇所で
error TS2307: Cannot find module './sample.node' or its corresponding type declarations.
となる

Vue.js の SFC を TypeScript で使う時と同じエラーの出方だなーと思ったので、

declare module "*.node" {
  export function sample(): string;
}

という型定義ファイルを index.ts の隣に置く
これでエラーが消えて、ちゃんと補完が効いたり解析が正しく動くようになる
ただこれだと複数の addon がある時には同じ型定義になってしまう

これは Wildcard module declarations という JS 以外のファイルを扱うケース向けの機能を使っている
今回の場合は suffix で判定しているため、以下のようにすると複数の addon に違う型が適用される

// import {} from './sample.node' の時に使われる
declare module "*sample.node" {
  export function sample(): string;
}

// import {} from './foobar.node' の時に使われる
declare module "*foobar.node" {
  export function foobar(): string;
}

解決方法として妥当かはよくわからないけど、まあなんとかなる
Monorepo なら多少楽になるかもしれないけど、どちらにせよ自分で書くのは面倒なので C++ の実装から型定義を自動生成したいなー

Post: 2021-11-06 Update: 2021-12-04
Tags: TypeScript

cmake-js を CLion で快適に扱う

Node Native Addon を作ってみようと思ったので、まずは node-addon-examples の first project を node-gyp から cmake-js に置き換えてやってみました
普段から使い慣れているので IDE は CLion を使って
ただ NODE_API_MODULE()C++ requires a type specifier for all declarations だったり、いくつかエラーが出てしまってどうにもおかしい

C++ をろくに書いたことがなくてどこが悪いのかさっぱり思いつかないので、まずは以下のコマンドを実行してコンパイルしてみた

$ ./node_modules/.bin/cmake-js compile

これは成功してちゃんと動くものができてたので実装は問題ないはず
じゃあ何が違うんだろうということで、綺麗なところからビルドしてディレクトリを比較してみる

まずは元の綺麗な状態がこう

.
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

cmake-js compile 実行後にはこんな感じ

.
|- build
|  |- Release
|  |  `- out.node
|  `- (省略)
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

次に CLion で開いた時にはこうなる

.
|- cmake-build-debug
|  |- Release
|  |  `- out.node
|  `- (省略)
|- lib
|  `- binding.js
|- node_modules
|- package.json
|- src
|  `- hello_world.cc
|- test
|  `- test_binding.js
|- CMakeLists.txt
`- yarn.lock

ということで生成されるフォルダが build か cmake-build-debug かというところが違った

これを調整したらエラーが消えるのかもということで、 CLion の設定で
Build, Execution, Deployment > CMake
の順にたどって ProfileBuild directory を build に設定してみる

設定を適用してロードを待ったらエラーが消えたのでめでたしめでたし
CLion は build directory に生成されたデータを元に解析対象を探してるっぽい

Post: 2021-10-07 Update: 2021-12-04
Tags: cmake-jsCLion

Visual Studio 2019 に付属の Git を使う

Windows での開発全くしてこなかったんですが、仕事のマシンで初めて Visual Studio 2019 をインストールしました
どんなのが入ってるのかとディレクトリを彷徨ってみたら、 git.exe がいたので単体で動くのか遊んでみました
Git for Windows はインストールしてない環境です

とりあえず簡単に PATH を通して

set PATH="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\CommonExtensions\Microsoft\TeamFoundation\TeamExplorer\Git\cmd";%PATH%

git version してみたら正常に出力されました

git version 2.33.0.windows.1

細かいことは見てないけど、 Electron のソース取得 は無事に動いたので普通に使えそう
Build Tools for Visual Studio とやらにも入ってたら良いなと思いますが、調べるのは面倒くさい

Post: 2021-10-06 Update: 2021-12-04
Tags: Windows

Golang で UTF-8 BOM 付きの CSV を読む

お仕事で UTF-8 の CSV を Go で読む機会があったんですが、中を見たら BOM があるファイルでした
これを何も考えずに encoding/csv で読んでみたらエラーになるんですね
実際には要件のおかげで雑に対処したけど、 BOM の有無にかかわらず読むってどうしたら良いのかなと遊んでみました

ドキュメント には RFC 4180 に書いてある CSV をサポートしていると書いてあって、 このフォーマットは BOM について書いていないので知る必要がないと issue に書いてありました
そりゃそうだってことで Seek の位置を調整してから Reader に渡すようにしてみたらちゃんと読めました

愚直にしか書けないからか頭の中がそのまま記述されるみたいで Go は書いてて気持ち良い

package main

import (
	"encoding/csv"
	"log"
	"os"
)

func main() {
	f, err := os.Open("sample.csv")
	if err != nil {
		log.Fatalln("Open failed", err)
	}

	b := make([]byte, 3)
	_, err = f.Read(b)
	if err != nil {
		log.Fatalln("Read failed", err)
	}

	if !(b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xEF) {
		f.Seek(0, os.SEEK_SET)
	}

	r := csv.NewReader(f)
	rs, err := r.ReadAll()
	if err != nil {
		log.Fatalln("CSV Reader read failed", err)
	}

	log.Printf("%v\n", rs)
}
Post: 2021-08-18 Update: 2021-12-04
Tags: Go