Snowflake CLI はじめ各種クライアントライブラリは user_name / password による認証に加えてキーペアでの認証にも対応している。
ユーザーに紐づける鍵は 2048 bits 以上の RSA 鍵である必要があり ed25519 には現状対応していない。
$ openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
$ openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
$ mkdir -p ~/.snowflake
$ cp rsa_key.p8 ~/.snowflake/
1 ユーザーに 2 つまで公開鍵を登録できる。
ALTER USER ***** SET RSA_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----
MIIBI...'
Terrafom を実行する。
terraform {
required_providers {
snowflake = {
source = "Snowflake-Labs/snowflake"
}
}
}
provider "snowflake" {
organization_name = "*****"
account_name = "*****"
role = "*****"
authenticator = "SNOWFLAKE_JWT"
}
resource "snowflake_warehouse" "warehouse" {
name = "WAREHOUSE"
warehouse_size = "XSMALL"
auto_resume = true
initially_suspended = true
auto_suspend = 1
}
パラメータは環境変数で渡すこともできる。
$ export SNOWFLAKE_USER="****"
$ export SNOWFLAKE_PRIVATE_KEY=$(cat ~/.snowflake/rsa_key.p8)
$ terraform apply
...
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
オブジェクトは provider で設定した role で作られるが、SECURITYADMIN 権限が必要なユーザーやロールの作成や変更を除いて、ACCOUNTADMIN で行うことは推奨されていない。alias を付けることで複数の provider を記述でき role を使い分けることができる。
Snowflake の Role を Terraform で作成しユーザーにテーブルへのアクセス権限を与える - sambaiz-net
provider "snowflake" {
role = "*****"
...
}
provider "snowflake" {
alias = "accountadmin"
role = "ACCOUNTADMIN"
...
}
resource "snowflake_role" "role" {
provider = snowflake.accountadmin
...
}
Snowflake CLI をインストールする。
$ brew tap snowflakedb/snowflake-cli
$ brew update
$ brew install snowflake-cli
$ snow --version
Snowflake CLI version: 3.3.0
設定ファイル ~/.snowflake/config.toml に接続情報を追加する。
$ snow connection add \
--connection-name test \
--account ***** \
--user ***** \
--role ***** \
--warehouse ***** \
--database ***** \
--schema ***** \
--authenticator SNOWFLAKE_JWT \
--private-key-file ~/.snowflake/rsa_key.p8 \
--no-interactive
$ cat /Users/*****/.snowflake/config.toml
[cli.logs]
save_logs = true
path = "/Users/*****/.snowflake/logs"
level = "info"
[connections.test]
account = "*****"
user = "*****"
database = "*****"
warehouse = "*****"
role = "*****"
authenticator = "SNOWFLAKE_JWT"
private_key_file = "/Users/*****/.snowflake/rsa_key.p8"
これでクエリが実行できる。
$ snow sql --connection test -q "SELECT * FROM test_table"
SELECT * FROM test_table
+-----------+
| ID | DATA |
|----+------|
| 1 | aaaa |
| 2 | bbbb |
| 3 | cccc |
+-----------+
Go の Snowflake ドライバー gosnowflake からもクエリする。
package main
import (
"context"
"crypto/rsa"
"crypto/x509"
"database/sql"
"encoding/pem"
"fmt"
"os"
"github.com/snowflakedb/gosnowflake"
)
func main() {
account := os.Getenv("SNOWFLAKE_ACCOUNT") // https://docs.snowflake.com/en/user-guide/admin-account-identifier
user := os.Getenv("SNOWFLAKE_USER")
warehouse := os.Getenv("SNOWFLAKE_WAREHOUSE")
keyPath := os.Getenv("PRIVATE_KEY_PATH")
ctx := context.Background()
db, err := newSnowflake(ctx, account, user, warehouse, keyPath)
if err != nil {
fmt.Printf("failed to init snowflake: %v\n", err)
os.Exit(1)
}
defer db.Close()
var version string
if err := db.QueryRowContext(ctx, "SELECT current_version()").Scan(&version); err != nil {
fmt.Printf("query error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Snowflake version: %s\n", version)
}
func newSnowflake(ctx context.Context, account, user, warehouse, keyPath string) (*sql.DB, error) {
pemBytes, err := os.ReadFile(keyPath)
if err != nil {
return nil, fmt.Errorf("read key file: %w", err)
}
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
pkRaw, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse PKCS#8 private key: %w", err)
}
rsaPk, ok := pkRaw.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("not an RSA private key: %T", pkRaw)
}
dsn, err := gosnowflake.DSN(&gosnowflake.Config{
Account: account,
User: user,
Warehouse: warehouse,
Authenticator: gosnowflake.AuthTypeJwt,
PrivateKey: rsaPk,
})
if err != nil {
return nil, fmt.Errorf("build DSN: %w", err)
}
db, err := sql.Open("snowflake", dsn)
if err != nil {
return nil, fmt.Errorf("open DB: %w", err)
}
if err := db.PingContext(ctx); err != nil {
db.Close()
return nil, fmt.Errorf("ping DB: %w", err)
}
return db, nil
}
参考
Snowflake CLIをインストールしてキーペア認証でSnowflakeへ接続してみた | DevelopersIO