❄️

超ざっくりSnowflakeのRBAC

2024/12/18に公開

本記事は、Snowflake Advent Calendar 2024 の 17 日目です。

はじめに

自分はSnowflake中部ユーザーグループ(以降、SCUG)のリーダーをやらせていただいてます。SCUGでは、3ヵ月に1回程度のペースで開催することを目標にしているので、企画のストックを持っておきたいところです。
その一つとして、「アクセスコントロールについてディスカッションする会」を考えています。そこで、準備として、SnowflakeのRBACに関する自分の知識を整理しておく目的で、この記事を書きました。

  • 初学者向けのざっくり記事です
  • アクセスコントロールというと、行レベルセキュリティ(行アクセスポリシー)、列レベルセキュリティ(マスキングポリシー)もありますが、本記事では扱わないです
  • しっかり学びたい方は公式ドキュメントをどうぞ
    https://docs.snowflake.com/ja/user-guide/security-access-control-overview

本記事の構成は、以下になります。
1.ロール作成の基本
2.スキーマ粒度でのテーブル権限付与
3.ファンクショナルロールとアクセスロール
4.補足

1.ロール作成の基本

使用データ

今回は以下のようなDBを使用します。

use role SYSADMIN;

// DBとSCHEMAの作成
create database DB1;
create schema SC1;
// TABLEの作成
create table t1 (c1 int, c2 int);
insert into t1
  select 1, random() from table(generator(rowcount => 3));

※ 余談ですが、テーブルのSQLはYoshiさんの動画で教えてもらったもののパクリです。好きな行数のダミーテーブルを作れるので便利です。

ROLE作成

テーブルT1をselectできるカスタムアカウントロールを作成します。(普通にcreate roleで作るやつです。以下、ロール)
筆者が最も基本的と考える、ロール作成のサンプルになります。

use role SECURITYADMIN;

// ROLEの作成、VWH使用権付与
create role ROLE11;
grant usage on warehouse COMPUTE_WH to role ROLE11;
// 権限付与
grant usage on database DB1 to role ROLE11;
grant usage on schema DB1.SC1 to role ROLE11;
grant select on table DB1.SC1.T1 to role ROLE11;
// ROLE階層作成
grant role ROLE11 to role SYSADMIN;

実行内容について説明します。

  • use role SECURITYADMIN;
     ロールを作成するためのロールに切り替えます。(ややこしいな)
     今回はuser、role関係は、SECURITYADMINでやってしまいます。言い換えると、USERADMINは使ってません。(SECURITYADMINとUSERADMINの使い分けについては、4‐2で触れます)

  • create role ROLE11;
     ロールを作成します。

  • grant usage on warehouse COMPUTE_WH to role ROLE11;
     仮想ウェアハウスの使用権を付与します。今回はアカウント作成時に既存のCOMPUTE_WHを使います。
     実際は、あらかじめ性能やコスト管理の観点で仮想ウェアハウスの設計を行い、そのロールに適切な仮想ウェアハウス(必要なら複数)の使用権を付与します。

  • grant usage on database DB1 to role ROLE11;

  • grant usage on schema DB1.SC1 to role ROLE11;
     アクセスしたいテーブルの属するDBとスキーマのusage権限(使用権)を付与します。

  • grant select on table DB1.SC1.T1 to role ROLE11;
     目的であるテーブルT1のselect権限を付与します。これでロールが完成です。
     ちなみに、テーブルの属するDBとスキーマのusage権限がないと、テーブルにselect権限があっても、selectできないです。
     下表で "select * from DB1.SC1.T1;"が成功するのは、ROLE11だけです。

権限 ROLE11 ROLE12 ROLE13
Usage DB1 ×
Usage SC1 ×
select T1
  • grant role ROLE11 to role SYSADMIN;
     最後に、ロール階層を組むために、上位のロールに紐づけます。
     Snowflakeでは、 「すべてのロールはACCOUNTADMINに通ず」 という掟があるので、お作法として、ロールを作成したときは、別のロール(上位ロール)にgrantしないといけません。


引用元:SnowDocument ロール階層と権限の継承

ここでは、上位ロールとして、DB系のオブジェクトの元締めであるシステム定義ロールSYSADMINに紐づけています。もちろん、図の右側にあるようにカスタムアカウントロールで階層を組むこともできますが、その場合でも最上位のロールはSYSADMINに紐づけることになるかと思います。

作成が終わったので、ROLE11に付与された権限を確認します。

show grants to role ROLE11;

付与した4つの権限が登録されています。
ちなみに、SECULITYADMINでGRANT実行したのに、granted_byがSYSADMINやACCOUNTADMINになってるのは謎です。誰か知ってたら教えてください。

補足として、上図の最下層にあるシステム定義ロールPUBLICは、すべてのUSERとROLEに対し暗黙でGRANTされます。(明示的にgrant文書かなくても。というか、やろうとすると不要の旨の警告が出る)
全ユーザーに共通して付与する権限があるのであれば、PUBLICに付与しておけば、いちいち個別のロールに書かなくてよいです。
なお、PUBLICはshow grantsに表示されないので留意ください。

2.スキーマ単位でのテーブル権限付与

前章のサンプルでは、テーブル単位で権限付与していますが、実際にはスキーマ内には2桁~3桁個のテーブルが存在するはずです。
テーブル1個1個にgrant文を書くの大変ですし、テーブルが追加されるつどgrantも追加しないといけないのは、ちょっと面倒です。
そこで、スキーマ単位でテーブル権限を付与する方法があります。

use role SECURITYADMIN;

// ROLEの作成、VWH使用権付与
create role ROLE21;
grant usage on warehouse COMPUTE_WH to role ROLE21;
// DBとSCHEMAのUSAGE付与
grant usage on database DB1 to role ROLE21;
grant usage on schema DB1.SC1 to role ROLE21;
// スキーマ単位のテーブル権限付与
grant select on all tables in schema DB1.SC1 to role ROLE22;
grant select on future tables in schema DB1.SC1 to role ROLE21;
// ROLE階層の作成
grant role ROLE21 to role SYSADMIN;

上記実行直後のROLE21の権限は、

show grants to role ROLE21;

です。
on all tablesのgrant文により、テーブルT1を個別指定することなく、スキーマに既存のテーブル全部に対する権限が付与されています。
ここではT1ひとつしかないですが、複数テーブルあればそのすべてにselect権が付与されます。

ここで、テーブルT2を追加します。

use role SYSADMIN;
// テーブルT2の作成
create table T2 (C1 int, C2 int);
insert into T2
  select 1, random() from table(generator(rowcount => 3));

もう一度、ロール権限を確認すると、

show grants to role ROLE21;

新たに作成されたT2に対して、on future tables のgrant文で設定したselect権限が自動的に付与されています。以降も同様に、スキーマSC1にテーブルが追加されると、自動的にtableのselect権限が追加されます。

スキーマ単にの権限管理でよかれば、このやり方は運用が楽です。
例えば、利用者がアドホックにデータをアップロードするような場合に、他の利用者がそれを使えるようにいちいち権限追加しないですむためです。
このあたりは、ユースケースや、組織的な権限管理のデザインに依存するので、一概にスキーマ単位の管理がいいわけではないですが、選択肢として知っていることは有用かと思います。

参考: DataSuperHeroぺいさんのポスト

3.ファンクショナルロールとアクセスロール

なにものか

ロールの設計では、ファンクショナルロールとアクセスロールの2階層に分けるのがベストプラクティスと言われています。ざっくりイメージは以下です。

  • ファンクショナルロール
     ヒト・組織側の観点で、データに対する役割に基づき設けるロール。
     上記の例では、個人営業担当か法人営業担当かという業務上の役割に基づいて、ファンクショナルロールが作成されています。
  • アクセスロール
     データオブジェクト側の観点で、データへのアクセスの仕方に種類に基づき設けるロール。
     個人顧客、法人顧客、商品の2つのデータに対して、それぞれ作成されています。
     上記の例では、個人顧客データと法人顧客データに対しては、参照用と更新用の2つが作られています。

こうすることで、例えば商品マスタデータに関する権限のメンテをするときに、商品マスタ参照ロールだけを変更すればすみます。また、個人営業担当ロールと法人営業担当ロールが、商品マスタに対して同じ権限を持つことも保証されます。
(もし、個人営業担当ロール、法人営業担当ロールに、直接商品データの権限を付与していると、両方をメンテしないといけませんし、メンテミス等で権限の不一致が起きるリスクも生じます)
アクセスロールによって、権限が抽象化されるので、管理しやすくなるものと理解してます。

参考: DataSuperHeroクラメソさがらさんの記事
「SnowflakeでFunctional Role+Access Roleのロール設計を実現するTerraformのModule構成を考えてみた」

SQLサンプル

すでにあるT1、T2に対して、ファンクショナルロール・アクセスロールを作成してみます。イメージは以下です。

サンプルSQLは以下になります。

/// 3-1 ファンクショナルロールとアクセスロール(DBロール版)
use role SECURITYADMIN;

// ファンクショナルロールの作成
create role F_ROLE31;
grant usage on warehouse COMPUTE_WH to role F_ROLE31;

// アクセスロール(DBロール)の作成
use role SYSADMIN;

/// T1のアクセスロール(DBロール)
create database role DB1.A_ROLE31_DB;
grant usage on database DB1 to database role DB1.A_ROLE31_DB;
grant usage on schema DB1.SC1 to database role DB1.A_ROLE31_DB;
grant select on table DB1.SC1.T1 to database role DB1.A_ROLE31_DB;
/// T2のアクセスロール(DBロール)
create database role DB1.A_ROLE32_DB;
grant usage on database DB1 to database role DB1.A_ROLE32_DB;
grant usage on schema DB1.SC1 to database role DB1.A_ROLE32_DB;
grant select on table DB1.SC1.T2 to database role DB1.A_ROLE32_DB;

// ロール階層を形成
use role SECURITYADMIN;

grant database role DB1.A_ROLE31_DB to role F_ROLE31;
grant database role DB1.A_ROLE32_DB to role F_ROLE31;
grant role F_ROLE31 to role SYSADMIN;

// ユーザ作成、ファンクショナルロール付与
create user USER31 password='pw31' default_role=F_ROLE31 default_warehouse=COMPUTE_WH;
grant role F_ROLE31 to user USER31;

要点は、

  • ファンクショナルロールは、普通のrole(アカウントロール)で作成している
  • アクセスロールは、database roleで作成する
    です。
    アクセスロールの実装は、普通のroleとdatabase roleの2択があり、ここではdatabase roleでやっています。理由は次項で触れます。
    ちなみに、ファンクショナルロールは普通のrole一択になります。なぜなら、database roleはuserに付与できないためです。

また、アクセスロールの作成のところで、SECURITYADMINから一旦SYSADMINに切り替えているところも留意点です。
データベースロールはDBのオブジェクトだからか、DBに対するUSAGEすら無いSECURITYADMINには作成できないようなのです。(SECURITYADMINにCREATE DATABASE ROLE権限が無い)

アクセスロールにdatabese roleを推す理由

アクセスロールを普通のroleではなく、database roleにした方がいい理由が、少なくとも2つあります。

  • ロール選択UIでアクセスロールが表示されない
     アクセスロールをdatabase roleにすると、SnowSight等のUIのロール選択で、表示されません。
     一般のuserは、自分のビジネス上の役割でロールを選べばいいので、ファンクショナルロールだけ意識すればよいです。アクセスロールは意識しなくていいのに、UIの選択肢に、下手すると大量に並ぶのは、ユーザ体験がよくないです。

  • DBが異なれば同じ名前が使える
     普通のrole の名前は、アカウント内でユニークです。そのため、異なるDBに同名のデータがあって、アクセスロールをそれぞれに作るときに困ります。role名にDB名等を持ち込むなど、区別のつくネーミングルールを考えないといけないです。

一方、デメリット、というか考慮事項として、以下があります。

  • database roleでは、権限付与がSECURITYADMINではなく、DB所有者の役割となる
     Snowflakeは基本は「roleへの権限付与はSECURITYADMINの役目」という思想と思われますが、database role ではそれがくずれてしまいます。

ただ、それがどこまで悪いかは一概に言えないと思われます。データ管理を中央集権型から連邦型に変えるような場合に、各ドメインのデータアクセスの種類は、ドメインのオーナーが管理するのが具合がよい、というシーンもあるんじゃないかと思います。

個人的には、「roleへの権限付与はSECURITYADMINの役目」が綺麗と思うので、全DBのdatabase role関連の権限を、SECURITYADMINに持たせるのもありな気がしますが、運用が面倒になるのでイマイチな気もします。(DB所有者に付与権限が残るので、統制観点でも中途半端です)

4.補足

セカンダリーロール

村の古くからの言い伝えで「セカンダリーロールには手を出してはならぬ」と聞いているので、割愛します。

ただ、セカンダリーロールは「BIツールなど参照専用3rd PartyツールからSnowflakeにアクセスする際に使える」という噂も聞いたことがあります。

SECURITYADMINとUSERADMINの役割分担

今回のサンプルでは、SECURITYADMINを使って、USERADMINは使っていません。userとroleまわりの仕事は、SECURITYADMINで全部できるからです。
 Snowflake導入当初から、あまり大人数で使っていなかったのもあり、この2つの役割分担、というかUSERADMINの使いどころがいまひとつ腹落ちしていません。
 一応の理解は以下になります。

  • 日常的な運用として、利用者増減によるuser作成/削除やrole割り当てが発生する。それだけを担当する者(オペレーター)には、SECURITYADMINは権限が強すぎる。
     例: SECURITYADMINは作成したuserにACCOUNTADMINの付与までできちゃう(USERADMINはできない)
  • そこで、ユーザ管理用途に必要な機能に絞ったUSERADMINが用意されている
    • userの作成・変更・削除(create user他)
    • カスタムROLEの作成・変更・削除(create role他)
    • roleの、userやroleへの割り当て(grant role to)
      ※ SECURITYADMINが作成したroleは除く

腹落ちしてないのは以下になります。

  • USERADMINはdatabase roleの、userやroleへの割り当てができない
  • このため、ファンクショナル&アクセスロールでやるときの、役割分担がよくわからない。実際運用したことがないので個人的な想像になりますが、以下になる気がします。
    • userとファンクショナルロール(role)の作成、その紐づけはUSERADMIN
    • アクセスロール(database role)の作成、それへのオブジェクト権限付与はSYSADMIN
    • アクセスロールのファンクショナルロールの紐づけはSECURITYADMIN
    • 特殊な権限付与、およびそれに必要なroleの作成はSECURITYADMIN
      このあたり、ぜひ実際やっている人の実例・意見を聞いてみたいです。

ROLEへの権限付与は許可だけで、禁止はない

ROLEへの権限付与は、私の知る限り「○○をできるようにする」すなわち許可だけです。「○○をできなくする」すなわち禁止は設定できないです。
 許可と禁止を両方扱うと、「重ねた時にどっちが勝つか」というのを扱わないといけないので、難しくなるし間違いが怖いので、個人的には「許可のみ」の方針には賛成です。
 
 この観点で見ると、今回扱えなかった「列レベルセキュリティ」(マスキングポリシー)や「行レベルセキュリティ」(行アクセスポリシー)の機能が、「禁止」を担っているという見方ができる気がします。(テーブルは見ていいけど、その一部を見せないようにするものであるため)

おわりに

Snowflake中部ユーザー会で、アクセスコントロールについてやってみたいと思ったのは、

  • ロール階層
  • ファンクショナル&アクセスロールの採用
  • SECURITYADMINとUSERADMINの役割分担
    など、一概にこれが正しいというのが明確でない中、各企業でなんらかの選択をして運用しているだろうから、お互いどうやっているかを知ることで、学びや改善のきかっけが得られるのではないかと考えるためです。

SCUGの次回第3回はdbtがテーマなので、第4回か第5回にこれを開催できればと思います。ご興味のある方は、ご参加いただけると嬉しいです!

Snowflake中部ユーザー会

最後まで読んでいただきありがとうございました。みなさま、よいお年をお迎えください!

Discussion