SnowflakeにWorkload Identity FederationでGitHub Actionsからシークレットレス接続する
はじめに
Snowflakeの新しい認証方式として「Workload Identity Federation」がGAになりました。
これを使うと、AWS LambdaやEC2インスタンス、k8sのpodなどのワークロードでシークレットを保管することなく有効期限の短いトークンを使ってSnowflakeへの疎通ができます。
- シークレット管理の手間がなくなる
- 万が一トークンが漏洩した場合のリスクは永続的な鍵よりも低い
といった利点がありそうです。
Snowflakeでは、AWSやGCPなどのクラウドプロバイダからの認証に対応していますが、OIDCを使ったより広範な認証も可能なようです。
今回はGitHubのOIDC Providerを使って、GitHub ActionsからWorkload Identity Federationができないか試してみました。
前提条件
- 以下のSnowflakeドライバを使っていること。
- 接続時の認証方式として、Workload Identity Federationをサポートしている必要があります。
- 今回はSnowflake Connector for Pythonで試してみました。
| Driver | 最小バージョン |
|---|---|
| Go | v1.16.0 |
| JDBC | v3.26.0 |
| .NET | v4.8.0 |
| Node.js | v2.2.0 |
| ODBC | v3.11.0 |
| Python | v3.17.0 |
- GitHub ActionsのRunner上で実行されているワークフローであること。
- 今回はGitHubがホストしているランナーで検証してみましたが、セルフホストされたランナーでも動くと思います。
- 認証にあたって、GitHub OIDC Providerを信用できること。
Snowflakeでの準備
Snowflakeのドキュメントに近い形で、GitHub Actionsのワークフロー内でSnowflakeへ接続するためのユーザを作成します。ただし、EKSなどとはISSUERやSUBJECTが異なるため、GitHubの仕様に合わせていきます。
CREATE USER github_actions_service_user
TYPE = SERVICE
WORKLOAD_IDENTITY =(
TYPE = OIDC
ISSUER = 'https://token.actions.githubusercontent.com'
SUBJECT = 'repo:ORG-NAME/REPO-NAME:ref:refs/heads/BRANCH-NAME'
)
;
- ISSUERは、GitHubのOIDC Providerの識別子となるURLを指定します。
- GitHubのOIDC Providerを信頼するように構成することになります。
- SUBJECTは、GitHub Actionsのワークフローに応じて、適宜設定していきます。
- GitHubが発行するトークンのSubjectはこのページに例が書いてあるので、この書式に沿わせるなどして設定していきます。
- Subject毎にユーザが必要です。
- 例:envで切り分けたりするときには、env毎にユーザを作ってあげる必要があります。
GitHub Actions側の準備
公式ドキュメントに記載のある通り、GitHub Actionsで、id-token への権限設定を行います。
ワークフロー上で、GitHubのOIDC Providerから以下のような要領で設定していきます。
permissions:
id-token: write
jobs:
your_job:
steps:
- name: Some Steps...
必要なのは、permissions.id-token:write の部分です。
YAMLへ追記してIDトークンをワークフロー内で取得できるようにしましょう。
GitHub ActionsでのIDトークン取得
GitHub Actionsで、GitHubのOIDC Providerへ「GitHub Actionsのワークフローである私を認証して、私の身元を証明するIDトークンをください」とお願いする処理を書いていきます。
permissions:
id-token: write
jobs:
your_job:
steps:
- name: Get OIDC Token
id: get-token
uses: actions/github-script@v7
env:
AUDIENCE: snowflakecomputing.com
with:
script: |
try {
const idToken = await core.getIDToken(process.env.AUDIENCE);
core.exportVariable('SNOWFLAKE_OIDC_TOKEN', idToken);
console.log('IDトークンを取得しました');
} catch (error) {
console.error(error);
core.setFailed(`IDトークンの取得に失敗: ${error.message}`);
}
GitHub Actionsのツールキット中にgetIDTokenという関数があるので、これを使ってGitHubのOIDC ProviderにGitHub Actionsのワークフローを認証してもらいます。
認証チャレンジに成功したら、IDトークンをもらえます。
SNOWFLAKE_OIDC_TOKEN という環境変数に格納して、後続の接続ドライバを用いた処理に使えるようにしていきます。
Snowflakeへ接続するツール・アプリ側の設定
Workload Identity Federationを行うために、ツール・アプリ側でもSnowflakeの接続設定を変更する必要があります。
前段で環境変数のSNOWFLAKE_OIDC_TOKEN にトークンが入ってくるようにしているので、これに対応していきましょう。
Snowflake Connector for Pythonでの例を書いてみます。
import snowflake.connector
import os
conn = snowflake.connector.connect(
account='<snowflake_account>',
authenticator='WORKLOAD_IDENTITY',
workload_identity_provider='OIDC',
token = os.environ.get("SNOWFLAKE_OIDC_TOKEN")
)
公式ドキュメントでは、tokenにJWT文字列を渡すパターンと、token_file_pathでファイルとして渡すパターンが記載されています。
この例では、tokenにJWTの文字列を渡すような形としています。
userは不要です。SnowflakeがSUB を見て、接続してきたサービスをどのユーザでログインさせるか決めるためです。
こんな感じで設定した接続情報を使ってSnowflakeへ疎通できました。
最後に
Snowflake Connector for Pythonをつかったワークロードを、GitHub Actionsからシークレットレスで実行可能なことを検証できました。
鍵管理の運用を楽にできたり、トークンの期限が短いので万が一漏洩してしまった場合のリスクも低いなど、いろいろな恩恵が得られそうです。
GAからまだ日が浅いため、Snowflake Terraform Providerなどで対応していなかったりする(ユーザの作成も含め)のですが、使えそうな場面では積極的に使っていきたいなと思っています。
Discussion