😎

データ共有時のマスキングポリシーとデータベースロールの設計方法

2023/12/30に公開

1. はじめに

今年の終わり頃から、複数のSnowflakeアカウント間のデータ共有時におけるデータベースロールとマスキングポリシー(ダイナミックデータマスキングや行アクセスポリシーなど)を併用する案件が増えた。データベースロールとマスキングポリシーの併用時の制約事項を考慮した上で、データ共有時の効率的なデータベースやロール設計方法について、本記事で紹介する。

2. 前提知識

2-1. データベースロールとは

データベースロール(以後、DBロールと記述する)は、アカウントレベルで管理されるアカウントロールと基本的に同じである。ただし、これは各データベースに帰属するという点で異なり、階層的にはデータベースオブジェクトに該当する。各データベースに帰属するということは、同じデータベースにあるオブジェクト(テーブル/ビュー)に対する操作権限を設定できるものの、異なるデータベースにあるオブジェクトに対する操作権限は設定できないという点でアカウントロールとは異なる。

DBロールの利点はデータ共有時に現れる。DBロールが実装される前、コンシューマーアカウントのアカウント管理者は単一の権限IMPORTED PRIVILEGESしか選ぶことができず、ユーザーが共有オブジェクト内のすべてのオブジェクトにアクセスできた。つまり、共有されたデータベース内の細かなアクセス制御ができなかった。この問題を解決するために登場したのがDBロールである。プロバイダーアカウント側でDBロールに必要な操作権限を設定し、コンシューマーアカウントへの共有オブジェクトにGrantした後は、コンシューマアカウント側で共有されたDBロールを自アカウントのアカウントロールに適用することができるようになった。これにより、細かなアクセス制御がコンシューマ側でも可能となった。話が長くなるので、詳細はこちらの公式ドキュメントを参照することとする。

2-2. IS_DATABASE_ROLE_IN_SESSIONとは

DBロールを使用した際、マスキングポリシー内に使用されるIS_DATABASE_ROLE_IN_SESSION関数について説明する。こちらの公式ドキュメントによると、このコンテキスト関数は以下のように記述されている。なお、この機能はまだプレビュー段階である。(2023/12現在)

データベースロールが、現セッションのユーザーのアクティブなプライマリまたはセカンダリロール階層にあるかどうか、または指定された列に、現セッションのユーザーのアクティブなプライマリまたはセカンダリロール階層にあるデータベースロールが含まれているかどうかを確認します。

この関数は、ロール内に指定されたDBロールが存在するかを判定する関数である。これは、Snowflakeアカウント単体で使用することも、他のSnowflakeアカウントのデータ共有時にも利用することができる。こちらの公式ドキュメントにもある通り、データ共有時にも細かなアクセス制御したい場合に使用される関数となっている。

プロバイダーとして、 IS_DATABASE_ROLE_IN_SESSION 関数を呼び出すポリシー条件を記述し、データベースロールを共有します。コンシューマーとして、共有データベースロールをアカウントロールに付与します。

なお、データ共有時に使用できるコンテキスト関数は意外に少ない。IS_DATABASE_ROLE_IN_SESSION以外にはCURRENT_ACCOUNTINVOKER_SHAREしかない。その他の関数はマスキングポリシー内に設定できるものの、コンシューマーアカウントでそれら関数を利用した場合、どんな値が入力されようとFalseとして値が返ってくる。

3. マスキングポリシーとDBロールの制約事項

この記事でも最も重要な点であるDBロールとマスキングポリシーの制約事項について説明する。結論から伝えると、以下の制約事項が存在する。

もし、あるデータベースにあるマスキングポリシーが、別データベースにあるDBロールを参照しようとした場合、以下のエラーが発生する。

図で説明すると、DB#1に所属するマスキングポリシーは、DB#1以外にあるDBロールを指定しようとすると、上記のエラーが発生する。
cross_database

具体的なSQLでは、以下のようなポリシーを作成しようとするとエラーが発生する。

create or replace masking policy db1.public.test_str_masking_policy as (val string) return string ->
  case
    when IS_DATABASE_ROLE_IN_SESSION('db2.db_role_2') then val ##これがNG
    else sha2(val)
  end;

つまり、マスキングポリシーとDBロールは同一データベースで管理するという制約事項が存在する。シンプルなケースであるならば、この制約事項はそれほどの大きな問題ではないとは思うが、Snowflakeアカウントを大量に発行している/データ種別が多い/セキュリティレベルが多段で設定されている/データメッシュ型のデータ管理をしようとしている、エンタープライズ寄りの会社では、この制約事項が立ちはだかるのではないだろうか。

4. DBロール = アクセスロールという考え方

この課題に直面した背景には、DBロールをアクセスロールのような立ち位置と考えていたことから始まった。アクセスロールは、スキーマレベルのオブジェクトに対する操作権限が付与されるロールである。例えば、テーブルオブジェクトに対する読み込み権限のみが付与されたロールを作成するといった感じだ。昨今、DBロールをアクセスロールの代わりに使用するという流れが一部で発生している。DBロールを使用するメリットは、前述した通り、データ共有時にも利用できることの他に、アクセスロールがロール一覧に大量に並んでしまうというロール数の爆発問題を回避することができる。(DBロールはロール一覧には出てこない)したがって、データ共有時の拡張性なども踏まえて、機能ロール(Functional Role) + DBロールでのアクセス制御するという考えが、今後は主流になってくるのではないかと考えている。

マスキングポリシー内でアクセスロールをベースに作成していたことと、アクセスロールはアカウントレベルのオブジェクト(=アカウントロール)であることからデータベースを跨ぐという考えがないという点で、DBロールでもアクセスロールの考えをそのまま踏襲しようとしたところで、このエラーに直面した。

5. マスキングポリシーとDBロールの設計方針(単一Snowflakeアカウント編)

アクセスロールは、データベース内にあるオブジェクト単位で操作権限を作成するという考えから、データベースとロールの帰属関係ができてしまう。裏を返すようではあるが、マスキングポリシーには、オブジェクトを関連しないロールを使用すれば、上記のエラーが発生しないのではないかと考えた。

早速の回答として、こちらの画像のようにすれば良いという結論に至った。
DBrole_w_MaskingPolicy

ダイナミックデータマスキングのポリシーはこちらである。

create or replace masking policy governance_db.public.pii_masking_policy as (val string) return string ->
  case
    when IS_DATABASE_ROLE_IN_SESSION('governance_db.pii_db_role') then val ## 同一データベースであるので、エラーは発生しない
    else sha2(val)
  end;

こちらの画像は、単一Snowflakeアカウント内で顧客情報が管理されるCustomer DBとマスキングポリシーなど管理されるGovernance DBの二つから構成されている。Customer DBには、顧客情報が管理されているテーブルとそのテーブルに対する読み込み(=SELECT)権限が付与されたDBロールが格納されている。一方で、Governance DBには、上記のSQLコードで記述されたマスキングポリシーとPII RoleというDBロールが格納されている。マスキングポリシーは、Customer DBにあるテーブルに適用されている。なお、マスキングポリシー自体は、別データベースにあるテーブルに適用することが可能である。(この辺で頭がゴチャゴチャしてくる)

この構成で大事なのは、PII DBロールというのは特定のオブジェクトを操作するために存在しているわけではないという点であり、このDBロールは

  • マスキングポリシー内で使用される
  • 機能ロールに付与する/付与しない

という2点だけで使用されている。

今回の例でいうと、このPII RoleというDBロールは、Sensitive Roleという機能ロールにだけGrantされており、Insensitive RoleにはGrantされていない。これにより、Sensitive Roleを持つAdminは生値、Insensitive Roleを持つUserはハッシュ化された値が返ってくるようになる。この挙動は、マスキングポリシー内でPII RoleというDBロールを持っている/持っていないという点でPII関連情報の閲覧の許可/拒否を判定していることにより、成立している。

仮にマスキングポリシーを適用するオブジェクトが増えた場合でも、簡単に拡張することができる。こちらの画像のように、Governance DBにあるマスキングポリシーをOther DBにあるテーブルに適用するだけであり、拡張性が非常に優れていることもわかる。

DBrole_w_MaskingPolicy_Applying_MultiTables

6. マスキングポリシーとDBロールの設計方針(データ共有編)

本記事の本題である複数Snowflakeアカウント間でのデータ共有時、どのように設計すれば良いのかを説明する。結論は、以下の画像の通りである。

DBrole_w_MaskingPolicy_over_Share

データプロバイダー側にあたるアカウント#1では、データが格納されているデータベースCustomer DBOther DB、マスキングポリシーやDBロールが格納されているデータベースGovernance DBを用意しておく。5章との単体アカウントとの違いは、アカウント間でやり取りするための共有オブジェクトを作成し、Governance DBにあるマスキングポリシー以外を、その共有オブジェクトにGrantしておく点である。

データコンシューマ側にあたるアカウント#2では、共有された共有オブジェクトを元に、それぞれのデータベースを作成する。今回の例では、元のデータベース名の冒頭にShared_と付与している。マスキングポリシー以外は、テーブルやDBロールがすべてProvisioningされていることがわかる。データコンシューマ側で共有されたDBロールに対し、機能ロール側に適宜適用することができる。今回の例では5章と同じように、Sensitive RolePII RoleというDBロールを適用している。

もし、Governance DBをコンシューマ側に共有しなければ、そのコンシューマ側では常にマスキングされた結果のみを参照することとなる。データプロバイダ側で特定アカウントにだけ機密情報を参照させたい場合は、DBロールを共有すれば良いだけであり、データソース側で制御できるというのは、一つのメリットである。また、マスキングポリシー自体の内容は、データソース側でしか変更できないようになっている。なお、今回の例では、コンシューマ側にマスキングポリシー自体を共有することはしていないが、もちろん、マスキングポリシーも共有することはできる。仮に共有されたマスキングポリシーをデータコンシューマ側で使用したい場合(=コンシューマ側のオリジナルデータに適用したい場合)には、共有してあげれば良い。

7. 備考

この構成は、ダイナミックデータマスキングでは実装できるが、行アクセスポリシーではまだ実装できない。データ共有時のみ、シェアされた行アクセスポリシーが他のデータベースにあるオブジェクトを参照できないという制約が存在している。エラー内容とすると、以下が出てくる。ただし、このエラーは近い将来に改善する方向ではある。

したがって、現状では、複数アカウント間でデータ共有する、かつ、行アクセスポリシーを使用する場合、行アクセスポリシー、DBロール、テーブルの3点セットを同一データベースに格納するしか方法がない。

8. さいごに

本記事では、複数アカウント間でデータ共有する際のマスキングポリシーとデータベースロールを中心とした設計方針について説明した。データベースロール自体が最近GAされた機能であり、まだまだ認知度が低いかと思う。ただし、Snowflakeアカウントをいくつか作成し、その間でデータシェアしながら、データに対するマスキングを適用するという話は、決して珍しくないと考えており、DBロールが利用される機会が今後益々増えてくると考えている。Snowflakeを利用する1人でも多くの人に、こちらの記事が役に立つことを期待し、本記事を終わらせて頂く。

Discussion