KADOKAWA Connected / ドワンゴの @saka1 と Integrated Data Service 部の中野 (takamoto) です。
私達の部署では主にSnowflakeを中心にしたデータ基盤(データプラットフォーム)の開発保守を行っています。この記事では、Snowflakeの機能を駆使してデータに対して適切に権限を割り振るために整備した仕組みについて紹介していきたいと思います。
Snowflakeのオブジェクト階層や、RBACベースの権限管理モデルは非常に柔軟かつ強力です。かなり自由に何でもできてしまうがゆえに、どういった整理の仕方で権限付与を行っていくかが大切に感じられます。我々のやり方が正解だとは必ずしも思いませんが、一つの事例としてコミュニティに還元されるといいかなと思い、この記事を書きました。
部署の概要やデータ基盤の概要については別の記事があるので、そちらも参照ください。
データ基盤にはAWSとの連携部分など様々な面白い箇所があるのですが、この記事では全体について細部を説明することはせず、ポイントを絞って解説していきます。また、データセキュリティ・データガバナンスというと非常に広範な領域を指すと思いますが、この記事ではあくまでその一側面しか触れることはしません。
前提
権限管理の構成を紹介する前に、構成を考える上での前提となる3つのポイントに触れておきます。
データ基盤における「権限管理」とは
この記事でいう「権限管理」とは、ざっくり言うと「必要な利用者に必要な権限が付与されていること」です。
例えば、あるアナリストが自分の分析したい対象のデータを読み出せないと、分析業務ができないため困ります。一方で、自分の業務とは無関係のデータを読み出せるのは適切ではなさそうです。あるいは、うっかりデータを(UPDATE文やDELETE文の発行によって)破壊できてしまうとしたら、それはアナリストの操作ミスというよりあらかじめ付与された権限が不適切と言うべきでしょう。
つまりは、以下の3つ組をできるだけ過不足なく、会社が求めるポリシーに合う形で管理することが目標になります。
- 誰(どの利用者)が
- 何(どのオブジェクト)に対して
- どのような操作(例えばテーブルに対するSELECT文の発行)ができるか
Snowflakeの権限管理機能
Snowflake上に作成されるユーザ・テーブル・ビューなどの権限付与の対象になるものは、全てオブジェクトと呼ばれます。オブジェクトのうち、テーブルのように階層化されて整理されるものは特にスキーマオブジェクトと言います。このスキーマオブジェクトは、SQL上では <データベース名>.<スキーマ名>.<オブジェクト名>
の形式で表記されます1。この辺りはよくあるRDBMSと変わりません。
一方で、オブジェクト(特にテーブル)に関するアクセス制御の仕組みは多様です。
- 任意アクセス制御(DAC)とロールベースのアクセス制御(RBAC)
- 行レベル・列レベルのセキュリティ
ロールベースのアクセス制御(RBAC)
この記事ではRBACに注目したいので、もう少しその機能について説明します。
SnowflakeにおけるRBACでは、ユーザに対して権限を直接割り振るのではなく、権限の集合であるロールをまず定義して、ロールをユーザに割り当てることで間接的に権限を付与します。また、以下のような操作も可能です。
- ロールに別のロールを割り当てて、ロールを階層化できる。
- 階層化されたロール間では、権限が継承される。
- ユーザは複数のロールを使い分けることができる。
- ロールAでSQLを発行した後に、ロールBでSQLを発行する、といったロールの使い分けができる。
ロール階層というのは少し分かりづらいかもしれません。サンプルを見たほうが理解しやすいと思うので、以下の例を元に説明します。
-- ロールを2つ作る。名前はそれぞれrole1とrole2 create role role1; create role role2; -- role1はdb1に関する権限を、role2はdb2に関する権限を持っている(この例ではusageという権限) grant usage on database db1 to role role1; grant usage on database db2 to role role2; -- role1とrole2でロール階層を作る grant role2 to role role1;
上記のSQLの実行後、 role1は上位ロール、role2は下位ロールとなります。図で表すと以下のようなロール階層になります。
role1はrole2の全ての権限を持っています。なので、role1が付与されたユーザは、 db1 だけでなく db2に関する権限も付与されているものとして振る舞います。
ロール階層の深さや、ユーザに付与できるロール数には、どうやら制限がないようです。つまりかなり柔軟に、望みの形で権限を振ることができます。もちろん制限がないからといって好き勝手にやってしまうと無秩序が発生するので、(この記事で紹介するような)何らかの設計が必要だということになります。
参考資料
- アクセス制御の概要 | Snowflake Documentation
- アクセス制御の考慮事項 | Snowflake Documentation
- Snowflake Security Overview and Best Practices
我々のデータ基盤で想定される権限付与のパターン
データ基盤において、「利用者にどういった権限を割り当てる必要があるか?」はシステムに求められているユースケースを元に整理することになると思いますが、我々の場合はおおむね以下のような利用パターンがありそうでした2。
複数の “基盤提供テーブル” に対する SELECT 権限をひとまとめに付与したい
データ基盤上には、利用者が生成したテーブル以外に、データ基盤側で生成・提供しているテーブル群が存在します(この記事中では “基盤提供テーブル” と呼ぶことにします)。
この基盤提供テーブルは様々な目的で整備されていますが、同じ目的で整備されたテーブルが複数存在するケースがあります。権限管理をシンプルにするために、それらに対して「必ず一括でSELECT権限の付与が行われる(一部のテーブルのみに権限が付与されることがない)」状態を作れることが求められました。
特定の複数利用者間でデータを共有する用途で、それらの利用者に対して、指定の “共有スキーマ” 以下のスキーマオブジェクトの読み書き権限を付与したい
これは例えば、データ基盤の複数利用者でチームを組んで分析を行っている際に「チーム内で生成したテーブルなどを双方向に共有できる」状態を作りたい、ただしその共有範囲はデータ基盤側で管理されている状態にしたい、というケースになります。
この際の共有単位としてはデータベース、スキーマ、テーブルなどいろいろな粒度が考えられますが、我々のデータ基盤ではスキーマの粒度で管理するのが適当そうでした(このスキーマのことを、この記事では “共有スキーマ” と呼ぶことにします)。
特定の事業領域に属する利用者に対して、その事業の “事業内公開テーブル” すべてへの SELECT 権限を付与したい
データプラットフォーム統合プロジェクトの紹介 - KADOKAWA Connected Engineering Blog でも紹介したように、我々のデータ基盤は複数の事業領域を横断して構築されており、事業領域ごとに異なる利用者と会社間契約する形で、利用者の方々に利用されています。
また、基盤提供テーブルの一部には、特定の事業に所属する利用者であれば誰でも参照して良いものが存在しています(このようなテーブルを、この記事では “事業内公開テーブル” と呼ぶことにします)。そのため、「ある事業Xに所属する利用者は、必ずXの事業内公開テーブルをすべて読める」という状態を作れる必要がありました。
この記事で紹介する権限管理の構成は、これらのパターンをカバーできるように設計したものになります。
我々が採用した構成
上記の前提を踏まえ、我々のデータ基盤では以下の3つの要素からなる構成を採用しました。
操作主体をSnowflake上のロールにマッピング
「誰が操作(例えば特定テーブルへのSELECT文の発行)をするのか」の「誰」の管理は意外と面倒です。
- 1人の人間であっても諸事情により複数のアカウント(Snowflake上ではユーザ)を持っている、あるいは持たせたいことがある。
- クエリを発行したいのは特定の人間というより、データ基盤を利用するシステムである事がある。
ただし、この記事ではこのトピックには深入りしません。我々のデータ基盤では、大まかには認証の問題をIdP(Auth0を採用しています)に委譲し、利用申請があった社員のアカウントをプロビジョニングする仕組みを整備しました。その結果、ほとんどのデータ基盤利用者がGoogle Workspace経由でシングルサインオンできるようになっています。
以降の記述では、ユーザ認証の問題は解決できていて、操作の主体はSnowflake上のユーザと適切にマッピングできているとします。また、以下のようなクエリによって、ユーザに1:1対応するロールを作り、ユーザにそのロールの権限を付与してあるとします3。
create role <ユーザに対応するロール>; -- ユーザに対応するロールの持つ権限がユーザに継承される grant role <ユーザに対応するロール> to user <操作主体に対応するユーザ>;
こうすることで、後はロール間の関係を適切に構成すればよいという話にまで問題は単純化されます。したがって以降はロールにのみ注目していきます。また、ユーザに対応するこのロールのことを、この記事では “ユーザロール” と呼ぶことにします。
抽象化された権限をSnowflake上のロールで表現
権限付与のパターンが3種類ほどあることを上で述べました。何やら複雑に見えたかもしれませんが、 Snowflake の強力な RBAC を利用して、パターンごとに権限を Snowflake 上のロールとして抽象化して扱えば、これらを実現するのは(少なくとも表面的には)それほど難しい話ではありません。
まず、1つ目のパターン「複数の “基盤提供テーブル” に対する SELECT 権限をひとまとめに扱いたい」については、以下のように複数のテーブルに対するSELECT権限を持つロールを作ります。
create role <基盤提供テーブル群に対応するロール名>; -- 基盤提供テーブル群に対応するロールに具体的な権限を振る grant select on table <基盤提供テーブル1> to role <基盤提供テーブル群に対応するロール名>; ... grant select on table <基盤提供テーブルn> to role <基盤提供テーブル群に対応するロール名>;
次に、2つ目のパターン「特定の複数利用者間でデータを共有する用途で、それらの利用者に対して、指定の “共有スキーマ” 以下のスキーマオブジェクトの読み書き権限を付与したい」については、以下のように共有スキーマごとに対応するロールを作ります。
create schema <共有スキーマ名> with managed access; create role <共有スキーマに対応するロール名>; -- 共有スキーマに対応するロールに具体的な権限を振る grant usage on schema <共有スキーマ名> to role <共有スキーマに対応するロール名>; grant create table on schema <共有スキーマ名> to role <共有スキーマに対応するロール名>; grant all on future tables in schema <共有スキーマ名> to role <共有スキーマに対応するロール名>;
最後に、3つ目のパターン「特定の事業領域に属する利用者に対して、その事業の “事業内公開テーブル” すべてへの SELECT 権限を付与したい」については、以下のように事業ごとに、事業内公開テーブルすべてに対応するロールを作ります。
create role <事業Xの事業内公開テーブルすべてに対応するロール>; -- 事業Xの事業内公開テーブルについて、基盤提供テーブル群ごとに GRANT 文を発行する -- (1つ目のパターンで用意されたロール経由で付与する) grant role <基盤提供テーブル群1に対応するロール名> to role <事業Xの事業内公開テーブルすべてに対応するロール名>; ... grant role <基盤提供テーブル群nに対応するロール名> to role <事業Xの事業内公開テーブルすべてに対応するロール名>;
あとは、作成した抽象化された権限を表すロール群と、ユーザロールとの上下関係を以下のように適宜設定すればよいだけです。
-- SELECT 権限を付与したい基盤提供テーブル群ごとに GRANT 文を発行する grant role <基盤提供テーブル群1に対応するロール名> to role <ユーザロール名>; ... grant role <基盤提供テーブル群nに対応するロール名> to role <ユーザロール名>; -- 読み書き権限を付与したい共有スキーマごとに GRANT 文を発行する grant role <共有スキーマ1に対応するロール名> to role <ユーザロール名>; ... grant role <共有スキーマmに対応するロール名> to role <ユーザロール名>; -- 所属している事業ごとに GRANT 文を発行する grant role <事業1の事業内公開テーブルすべてに対応するロール名> to role <ユーザロール名>; ... grant role <事業xの事業内公開テーブルすべてに対応するロール名> to role <ユーザロール名>;
上記のSQLによって、ロール階層は以下のようになります。
この節で提示した構成は説明の都合上簡略化してあり、実際の権限付与ではもっと複雑なことをやっています4。ですが、システムに求められた要件に応じてロールを組み合わせるエッセンス部分は説明できているかなと思います。
権限付与操作のWeb API化
ここまでで述べた権限付与は、機械的ですがある程度の作業量になります。ユーザを新規に払い出すたび、あるいはテーブルが増えるたびに関連するSQLをデータエンジニアが手作業で発行するのは現実的ではありません。また、権限付与は手作業で行うとミスをするリスクがあります。最悪の場合にはセキュリティ事故になってしまいます。
そういった点を踏まえて、権限周りの典型的な操作はWeb API化しています5。つまりはアプリケーションサーバ上のロジックとして「利用者と事業の紐づけ」などを表現した上でSQLの発行までを行うことで、Snowflakeの状態を安全かつ期待した状態に保つようにしています。
まとめ
以上が、ごく簡単にはしているのですが、私達の部署で整備してきた権限管理の仕組みです。
改めて強調しておきたいのはSnowflakeのアクセス制御の強力さです。権限管理回りでの要求は実は少しずつ変化しつつ今の形になったのですが、そういった経時的な複雑化にもこれまで見事に耐えてくれました。
我々のデータ基盤は今後も新しい要件を取り込んで進歩し続けると思いますが、今回説明したような仕組みを順次発展させることで対応できるんじゃないかなと期待しています。
- 参考資料: オブジェクト名の解決 | Snowflake Documentation↩
- なお、求められる権限管理ポリシーは必ずしも事業間で一致しないため、事業ごとに権限付与のパターンは少し異なる場合があります。例えば、事業によっては 3つ目のパターンは存在していません。↩
- 実際、我々のSnowflakeではそうなっています。↩
- 例えば、共有スキーマに対応するロールには、実際にはもっと多くの権限が付与されています。また、事業内公開テーブルへのINSERTなどのデータを更新する権限については別のロールで管理するなど、さらに追加の工夫が必要でした。↩
- この Web API は、 データプラットフォーム統合プロジェクトの紹介 - KADOKAWA Connected Engineering Blog の記事中のアーキテクチャ図に描かれている APIアプリケーションの一部分に対応します。↩