Redshiftにて、DBユーザーの権限をいい感じに管理する方法

Redshiftにおいて、DBユーザ向けに、あるスキーマ内にあるオブジェクト(tableとかviewとか)に対する権限を付与しようとした場合、今まで以下のSQLを利用していました。

GRANT SELECT ON ALL TABLES IN SCHEMA schema_name TO GROUP db_user_group;

しかし、このSQLでは このGrant文を実行した後に作成したテーブルには、権限が反映されない という問題があります。(あります、というか、そんな事知らずに運用していました。。。)

この事を考慮して ALTER DEFAULT PRIVILEGES文 を利用すべきとなります。これは、PostgreSQL 9.0から利用できる機能で、Redshiftでも利用できます。公式ドキュメントはこちらです。

ALTER DEFAULT PRIVILEGES - Amazon Redshift

この機能を利用することで、スキーマ内のオブジェクトを作成したタイミングで、指定したDBユーザに任意の権限を与えることができます。以下は、その検証作業内容です。

ユーザ作成

Redshiftに接続して、DBユーザグループとDBユーザを作成します。尚、作業はDBマスターユーザにて実行しています。

db_masters db_editors db_readers という3つのグループを作成しています。

test=# CREATE GROUP db_masters;
CREATE GROUP
test=# CREATE GROUP db_editors;
CREATE GROUP
test=# CREATE GROUP db_readers;
CREATE GROUP

master editor1 editor2 reader1 という4つのユーザを作成しています。

test=# CREATE USER master WITH PASSWORD 'Passw0rd' IN GROUP db_masters;
CREATE USER
test=# CREATE USER editor1 WITH PASSWORD 'Passw0rd' IN GROUP db_editors;
CREATE USER
test=# CREATE USER editor2 WITH PASSWORD 'Passw0rd' IN GROUP db_editors;
CREATE USER
test=# CREATE USER reader1 WITH PASSWORD 'Passw0rd' IN GROUP db_readers;

スキーマ作成

所有者を master として、スキーマを作成しています。

test=# CREATE SCHEMA sample_schema AUTHORIZATION master;
CREATE SCHEMA

権限付与

各権限を付与していきます。

まず、publicスキーマは、全DBユーザが無条件にアクセスできるスキーマとなるため、アクセスできないように権限をRevokeしておきます。このSQL実行後、publicスキーマにアクセスできるのは、redshiftマスターユーザのみとなります。

test=# REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE
test=# REVOKE USAGE ON SCHEMA public FROM PUBLIC;
REVOKE

スキーマに対する権限を付与します。db_editors はオブジェクトの作成/アクセスできる権限を、 db_readers にはオブジェクトにアクセスできる権限のみを与えます。

test=# GRANT CREATE ON SCHEMA sample_schema TO GROUP db_editors;
GRANT
test=# GRANT USAGE ON SCHEMA sample_schema TO GROUP db_editors;
GRANT
test=# GRANT USAGE ON SCHEMA sample_schema TO GROUP db_readers;
GRANT

そして、ここで実行されるのが今回の本題となる ALTER DEFAULT PRIVILEGES コマンドです。1行目のGrant文を説明すると、 editor1 ユーザが sample_schema 内に作成したオブジェクトは、 GRANT ALL ON TABLES TO GROUP db_editors の権限を与えてくれていることになります。権限内容は、 PG_DEFAULT_ACL テーブルにて確認できます。

test=# ALTER DEFAULT PRIVILEGES FOR USER editor1 IN SCHEMA sample_schema GRANT ALL ON TABLES TO GROUP db_editors;
ALTER DEFAULT PRIVILEGES
test=# ALTER DEFAULT PRIVILEGES FOR USER editor2 IN SCHEMA sample_schema GRANT ALL ON TABLES TO GROUP db_editors;
ALTER DEFAULT PRIVILEGES
test=# ALTER DEFAULT PRIVILEGES FOR USER editor1 IN SCHEMA sample_schema GRANT SELECT ON TABLES TO GROUP db_readers;
ALTER DEFAULT PRIVILEGES
test=#
test=# select * from PG_DEFAULT_ACL;
 defacluser | defaclnamespace | defaclobjtype |                             defaclacl
------------+-----------------+---------------+-------------------------------------------------------------------
        106 |          299676 | r             | {"group db_editors=arwdRxt/editor2"}
        105 |          299676 | r             | {"group db_editors=arwdRxt/editor1","group db_readers=r/editor1"}
(2 rows)

確認

実際に確認してみます。

editor1での操作

editor1 ユーザにて、 sample_editor1 というテーブルを作成して、データをINSERTします。

test=> CREATE TABLE sample_schema.sample
test->   (
test(>     voice_id VARCHAR(3),
test(>     name VARCHAR(20)
test(>   )
test->   BACKUP YES
test->   DISTSTYLE EVEN
test->   SORTKEY(voice_id);
CREATE TABLE
test=> INSERT INTO sample_schema.sample values('001','zunchan');
INSERT 0 1

editor2での操作

editor2 ユーザにて、 editor1 ユーザが作成したテーブルを参照してみます。

test=> SELECT * FROM sample_schema.sample;
 voice_id |  name
----------+---------
 001      | zunchan
(1 row)

できますね。同様に、INSERTとDELETEもできますが、テーブルのDROPはできません。テーブルのdropは、Redshiftマスターユーザか、テーブル所有者でないとできません。

test=> INSERT INTO sample_schema.sample values('002','kiritan');
INSERT 0 1
test=> COMMIT;
COMMIT
test=> DELETE FROM sample_schema.sample WHERE name = 'kiritan';
DELETE 1
test=> DROP TABLE sample_schema.sample;
ERROR:  must be owner of relation sample

最後に sample_editor2 というテーブルを作成しておきます。

test=> CREATE TABLE sample_schema.sample_editor2
test->   (
test(>     voice_id VARCHAR(3),
test(>     name VARCHAR(20)
test(>   )
test->   BACKUP YES
test->   DISTSTYLE EVEN
test->   SORTKEY(voice_id);
CREATE TABLE

reader1での操作

reader1 ユーザにて、editor1 ユーザが作成したテーブルを参照してみます。

test=> SELECT * FROM sample_schema.sample;
 voice_id |  name
----------+---------
 001      | zunchan
 002      | kiritan
(2 rows)

これはDEFAULT PRIVILEGES権限を与えているので、参照できます。では、権限を与えていないデータのINSERTの処理、 editor2 ユーザが作成したテーブルの参照はどうでしょうか。

test=> INSERT INTO sample_schema.sample values('003','yukarisan');
ERROR:  permission denied for relation sample
test=> SELECT * FROM sample_schema.sample_editor2;
ERROR:  permission denied for relation sample_editor2

想像通り、できませんね。これにて確認完了です。