RLS
SQL実行時の権限によって、SELECTの結果にアクセス権限のある行だけが返されるという素敵な仕組み。
これによって、フレームワークやライブラリがWHERE句を注入するアプリケーション層でのデータ分離方式よりも、シンプルに問題を解決できる。
RLSの多くの例は、DBコネクションのユーザーでアクセス制御を行っているが、PostgreSQLのROLEを使うことで、DBコネクション確立後に SET ROLE="2" のようにして任意のID(例えばテナントID)でアクセス制御を行える。 https://scrapbox.io/files/60835823247345001c3dbccc.png
設定方法などのサンプルコード付き
Djangoでのコンセプト実装
RLSの設定方法
すぐにお試しの環境を作れるように記録
英語だけどシンプルで分かりやすい
code:console
$ docker-compose exec db psql db -U db
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.
db=# \dt
public | auth_group | table | db
public | auth_group_permissions | table | db
public | auth_permission | table | db
public | customers | table | db
public | django_admin_log | table | db
public | django_content_type | table | db
public | django_migrations | table | db
public | django_session | table | db
public | tenants | table | db
public | tenant_users | table | db
public | tenant_users_groups | table | db
public | tenant_users_user_permissions | table | db
db=# select * from customers;
1 | aa | aa | aa | aa | 2
2 | bb | bb | bb | bb | 1
3 | cc | cc | cc | cc | 2
4 | dd | dd | dd | dd | 1
5 | ee | ee | ee | ee | 2
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
8 | hh | hh | hh | hh | 2
9 | ii | ii | ii | ii | 1
10 | jj | jj | jj | jj | 2
db=# CREATE ROLE "1";
CREATE ROLE
db=# CREATE ROLE "2";
CREATE ROLE
db=# CREATE ROLE tenantuser;
CREATE ROLE
db=# GRANT select, insert ON customers TO tenantuser;
GRANT
db=# GRANT tenantuser TO "1";
GRANT ROLE
db=# GRANT tenantuser TO "2";
GRANT ROLE
db=# ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE
db=# CREATE POLICY tenantuser_customers ON customers USING(tenant_id::text = current_user);
CREATE POLICY
db=# SELECT session_user, current_user;
db | db
db=# SELECT * FROM customers;
1 | aa | aa | aa | aa | 2
2 | bb | bb | bb | bb | 1
3 | cc | cc | cc | cc | 2
4 | dd | dd | dd | dd | 1
5 | ee | ee | ee | ee | 2
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
8 | hh | hh | hh | hh | 2
9 | ii | ii | ii | ii | 1
10 | jj | jj | jj | jj | 2
db=# SET ROLE "1";
SET
db=> SELECT * FROM customers;
2 | bb | bb | bb | bb | 1
4 | dd | dd | dd | dd | 1
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
9 | ii | ii | ii | ii | 1
参考文献
RLSを使うことで多少パフォーマンスに影響がでる可能性があるようです。
PostgresSQLの行レベルセキュリティと、SpringAOPによる処理を組み合わせて、ログインしているテナントのデータにしかアクセスできなくする仕組みを実現