
dbt grantsを使用してアクセス制御を行ってみた
かわばたです。
dbt grantsを使用して、dbt Cloudでアクセス制御を行ってみます。
【公式ドキュメント】
対象読者
- dbtのアクセス制御について確認したい方
検証環境と事前準備
検証環境
- dbt CloudのEnterprise版
- SnowflakeトライアルアカウントのEnterprise版
事前準備
dbtのサンプルデータであるjaffle_shop
を活用しています。
また、Snowflakeにデータベース・スキーマ・ロールを作成しています。
-- martデータベース作成必要(dbtで加工したマート保存用)
create database KAWABATA_MART_DB;
-- -- スキーマ作成(本番用スキーマ)
create schema KAWABATA_MART_DB.PROD_SCHEMA;
-- ロールの作成
-- 分析者のロール
CREATE ROLE IF NOT EXISTS ANALYST_ROLE;
-- BI使用者のロール
CREATE ROLE IF NOT EXISTS BI_USER;
-- 誰も使用しないロール
CREATE ROLE IF NOT EXISTS unused;
今回行わないこと
今回は基本的なdbt grantsの機能を試していきます。
ですので、post-hook内でGRANT文を記述するなど高度な要件については触れません。
【参考ドキュメント】
dbt grantsの概要
dbtのgrantsは、dbtが生成するテーブルやビューといったデータセットへのアクセス権限を、dbtプロジェクト内のコードとして管理するための機能です。
特徴としては、宣言的な権限管理で「このテーブルは、このロールにSELECT権限を持つべき」という最終的な状態をコードで定義します。
- Snowflakeにおける権限のマッピング
権限 (dbt config) | 一般的な目的 | Snowflake |
---|---|---|
select | データの読み取り | SELECT |
insert | データの追加 | INSERT |
update | データの更新 | UPDATE |
delete | データの削除 | DELETE |
usage | スキーマ内のオブジェクトへのアクセス | USAGE |
実際に試してみた
martsフォルダ内のみ反映
下記のようにdbt_project.ymlを設定しました。これはmodelsフォルダのmartsフォルダ内のオブジェクト(ここではテーブル)に対して、ANALYST_ROLEはselect
・insert
権限を付与し、BI_USERはselect
権限を付与しています。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
# - "target"
# - "dbt_packages"
seeds:
# Builds seeds into '<your_schema_name>_raw'
jaffle_shop:
+schema: raw
models:
jaffle_shop:
# Materialize staging models as views, and marts as tables
staging:
+materialized: view
marts:
+materialized: table
## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
+grants:
select: ['ANALYST_ROLE', 'BI_USER']
insert: 'ANALYST_ROLE'
flags:
require_generic_test_arguments_property: true
実際にこちらの設定で実行してみます。
Snowflake側も確認してみましょう。
上記のように、GRANT
文が実行されていることが分かります。
試しに一つのテーブルについて権限を確認してみました。
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;
想定通りの付与ができています。
念のため、スキーマに影響がないか確認してみます。
SHOW GRANTS ON SCHEMA kawabata_mart_db.DBT_TKAWABATA;
こちらには反映されていませんでした。想定通りテーブルのみに付与されていることが分かります。
宣言的な権限管理の確認
さきほどはselect
・insert
権限を付与しましたが、select
・delete
権限にして対応してみます。
宣言的に権限管理するため、insert
権限がなくなるはずです。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
# - "target"
# - "dbt_packages"
seeds:
# Builds seeds into '<your_schema_name>_raw'
jaffle_shop:
+schema: raw
models:
jaffle_shop:
# Materialize staging models as views, and marts as tables
staging:
+materialized: view
marts:
+materialized: table
## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
+grants:
select: ['ANALYST_ROLE', 'BI_USER']
delete: 'ANALYST_ROLE'
flags:
require_generic_test_arguments_property: true
実行結果を確認すると
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;
delete
権限に置き換わっていることが分かります。
継承の上書きor追加
先ほどまではフォルダ内全体へ権限を付与していましたが、モデルごとに付与することも可能です。
その際に、dbt_project.ymlで定義していたフォルダ内全体へ権限付与と合わせてモデルでの権限付与を行った場合、権限を上書きするか、追加するかを確認していきます。
ここでは、dbt_project.ymlは「宣言的な権限管理の確認」で確認した時の内容となります。
- select
ANALYST_ROLE
,BI_USER
- delete
ANALYST_ROLE
上書き
あわせてモデルに権限付与のconfigを加えます。例として、locations.sqlを使用しています。
-- BI_USERにdelete権限付与
{{ config(
grants={
'delete':'BI_USER'
}
) }}
with
locations as (
select * from {{ ref('stg_locations') }}
)
select * from locations
この場合、権限が上書きされるためlocationsテーブルはdelete
権限がBI_USER
となるはずです。
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;
想定通りの内容でした。
念のため他のモデルに影響ないか確認します。
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.products;
こちらはdelete
権限がANALYST_ROLE
のままでした。
追加
上書きではなく、所定のテーブルだけ権限を追加したいケースもあるかと思います。その場合+
プレフィックスを活用します。先ほどの設定に+
を追加しています。
-- BI_USERにdelete権限付与
{{ config(
grants={
'+delete':'BI_USER'
}
) }}
with
locations as (
select * from {{ ref('stg_locations') }}
)
select * from locations
この場合は権限が追加されるためlocationsテーブルはdelete
権限がANALYST_ROLE
とBI_USER
となるはずです。
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;
想定通りのロール構成となっていました。
targetによる動的な権限付与
開発環境と本番環境によって権限を分けたいケースがあるかと思います。
そのようなケースの時に、targetによる動的な権限付与が有効です。
dbt_project.ymlを下記のように定義しました。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
# - "target"
# - "dbt_packages"
seeds:
# Builds seeds into '<your_schema_name>_raw'
jaffle_shop:
+schema: raw
models:
jaffle_shop:
# Materialize staging models as views, and marts as tables
staging:
+materialized: view
marts:
+materialized: table
## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
+grants:
# target.nameが 'prod' の場合
select: "{{ 'BI_USER' if target.name == 'prod' else 'ANALYST_ROLE' }}"
# target.nameが 'prod' ではない場合 (開発環境の場合)
insert: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
update: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
flags:
require_generic_test_arguments_property: true
今回簡易な条件分岐の方法を取ったため、unused
権限を明示的に記載しています。なにも割り当てないとGRANT
文の生成時にエラーとなる可能性があるため付与しています。
開発環境
開発環境では、ANALYST_ROLE
にselect
,insert
,update
権限が付与されて、BI_USER
には権限が付与されない形です。
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;
想定通りANALYST_ROLE
にのみ権限が付与されていました。
本番環境
下記のようにjobの設定でtarget nameをprod
として実行しました。
SHOW GRANTS ON TABLE kawabata_mart_db.PROD_SCHEMA.LOCATIONS;
想定通りBI_USER
にselect
権限が付与されて、ANALYST_ROLE
には権限が付与されていないことが分かります。
最後に
今回の例ではロールに対して権限を付与してきましたが、ユーザーに対して付与することも可能です。
ただ、基本的にはロールへの付与を推奨しています。
ユーザーに直接権限を付与していた場合、例えば離任などで現場を離れる際に都度dbtのコードを修正し、権限設定を更新する必要があります。
ロールの場合は、Snowflake上で該当ユーザーからロールを剥奪するだけで済むため、dbtのコードを触れずに対応することができます。
dbtのアクセス制御は宣言的に権限を付与できるので、是非活用してみてください!