Day26 前半のゴール
「“何でもできるユーザー”をやめて、“役割ごとに守る”感覚を持つ」
Day26 は「権限管理」です。
ここまで性能や自動処理を見てきたけれど、本番運用で一番怖いのは「やってはいけない人が、やってはいけない操作をできてしまうこと」です。
今日は、その入口となる「ロール・権限設計」の前半戦です。
前半のゴールはこうです。
PostgreSQL の「ロール」が、ユーザーとグループの両方の役割を持つことを理解する。
権限(プリビレッジ)が「何をしていいか」を細かく制御するものだと分かる。
アプリ用ユーザー・開発者・管理者をざっくり分けた権限イメージを持てる。
まずは、「ロールって何者?」からいきます。
ロールとは何か
「ユーザーでもあり、グループでもある“箱”」
PostgreSQL では、「ユーザー」と「グループ」を厳密に分けず、
どちらも「ロール(role)」という概念で扱います。
イメージとしては、「権限をぶら下げるための箱」です。
あるロールは「ログインできるユーザー」として使える。
別のロールは「ログインはしないけど、権限の束(グループ)」として使える。
ロール同士で「このロールはあのロールの権限を引き継ぐ」という関係を作れる。
例えば、こんな感じの設計ができます。
アプリ用のログインロール:app_user
開発者向けのロール:dev_role
読み取り専用のロール:read_only_role
そして、「app_user に read_only_role を付与する」といった形で、
権限を組み合わせていきます。
ここで重要なのは、「ロール=ログインIDそのもの」ではなく、
「ログインできるロール」「権限だけ持つロール」を柔軟に作れる、という感覚です。
権限(プリビレッジ)とは何か
「“どのオブジェクトに対して、何をしていいか”のルール」
ロールが「誰か・どんな役割か」だとすると、
権限(privilege)は「そのロールが何をしていいか」です。
PostgreSQL では、オブジェクトごとに権限の種類が決まっています。
代表的なものだけ、イメージで押さえましょう。
テーブルに対する権限
SELECT(読み取り)
INSERT(追加)
UPDATE(更新)
DELETE(削除)
TRUNCATE(全削除)
REFERENCES(外部キー参照)
TRIGGER(トリガー作成)
データベースに対する権限
CONNECT(接続)
CREATE(スキーマなどの作成)
スキーマに対する権限
USAGE(そのスキーマ内のオブジェクトを参照できる)
CREATE(そのスキーマ内にテーブルなどを作れる)
これらを組み合わせて、「このロールは、このテーブルに対して SELECT だけ」「このロールは、このスキーマに CREATE できる」といったルールを作ります。
大事なのは、「権限は“オブジェクト×操作”の組み合わせで考える」という視点です。
例題:アプリ用ユーザーの権限をイメージする
「本番DBに“スーパーユーザーで接続”をやめる」
よくある悪いパターンから、逆に学びましょう。
ありがちなNG構成:
アプリケーションが、本番DBにスーパーユーザー(何でもできるユーザー)で接続している。
これだと、アプリのバグやSQLインジェクションが起きたときに、
テーブル削除・権限変更・他DBへのアクセスなど、何でもできてしまいます。
セキュリティ的には最悪です。
理想に近づけるために、まずは「アプリ用ロール」をこうイメージします。
アプリ用ロール app_user は、
特定のデータベースに CONNECT できる。
アプリが使うスキーマに USAGE できる。
必要なテーブルに対して、SELECT / INSERT / UPDATE / DELETE だけ持つ。
テーブル定義の変更(ALTER TABLE)や DROP はできない。
他のユーザーやロールの管理は一切できない。
これだけでも、「アプリからの事故で壊せる範囲」がかなり小さくなります。
実際のGRANT文のイメージはこんな感じです。
-- DBへの接続を許可
GRANT CONNECT ON DATABASE myapp_db TO app_user;
-- スキーマの利用を許可
GRANT USAGE ON SCHEMA public TO app_user;
-- テーブルへの操作を許可
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
SQLここでの重要ポイントは、「アプリ用ユーザーには“本当に必要な最小限”だけを与える」という考え方です。
ロールの“継承”という考え方
「権限の束を“役割”としてまとめる」
ロールは、「他のロールをメンバーとして持つ」ことができます。
これは、グループにユーザーを所属させるイメージに近いです。
例えば、こういう設計を考えます。
読み取り専用ロール:read_only_role
→ テーブルに対して SELECT だけ持つ。
アプリ用ロール:app_user
→ read_only_role を継承しつつ、追加で INSERT / UPDATE / DELETE を持つ。
開発者ロール:dev_role
→ read_only_role を継承しつつ、一部の管理系権限も持つ。
このとき、read_only_role に対して権限をGRANTしておけば、
それを継承しているロールは自動的にその権限を使えます。
イメージとしては、
「権限の束(read_only_role)」を作る。
「実際にログインするロール(app_user, dev_role)」は、その束を引き継ぐ。
という構造です。
これにより、
「読み取り専用の範囲を変えたい」ときは read_only_role だけを直せばよい。
アプリ用・開発者用など、複数のロールに一括で反映される。
というメリットが出てきます。
ここでの学びは、「ロールを“ログインID”としてだけでなく、“権限の束”としても設計する」という視点です。
例題:開発者と本番運用者のロールを分ける
「“何でもできる人”を減らす設計」
もう一つ、現実に近い例を考えます。
小さなチームだと、「開発者がそのまま本番DBにも入る」ということがよくあります。
それ自体は仕方ない場面もありますが、「何でもできる権限」を渡す必要はありません。
例えば、こう分けられます。
dev_role(開発者ロール)
開発用DBでは、ほぼ何でもできる(CREATE TABLE, ALTER TABLE, DROP TABLE など)。
本番DBでは、基本的に SELECT のみ、必要に応じて一部UPDATEを許可。
ops_role(運用ロール)
本番DBで、メンテナンスやリカバリに必要な権限を持つ。
ただし、アプリデータの直接編集は極力避ける。
superuser(スーパーユーザー)
本当に必要なときだけ使う“最後の鍵”。
日常運用では使わない。
このように、「役割ごとにロールを分ける」ことで、
「誰がどこまでできるか」を明確にできます。
ここで大事なのは、「スーパーユーザーを日常的に使わない」「アプリ用・開発用・運用用を分ける」という2つの軸です。
Day26 前半のまとめ
PostgreSQL のロールは「ユーザー」と「グループ」を兼ねる“権限の箱”であり、ロールに対して「どのオブジェクト(DB・スキーマ・テーブル…)に、どの操作(SELECT / INSERT / UPDATE / DELETE / CONNECT / USAGE…)を許すか」という形で権限(プリビレッジ)をGRANTしていくことで、「アプリ用ユーザーはこのDBに接続できて、このスキーマのテーブルを読み書きできるが、定義変更や他DB操作はできない」といった細かいルールを作れる。
さらに、ロール同士を継承させることで「read_only_role のような権限の束」を作り、それを app_user や dev_role が引き継ぐ構造にすると、「読み取り専用の範囲を変えたいときに1箇所直せば全体に反映される」ようになり、「スーパーユーザーで本番に接続する」「何でもできるユーザーが常に使われている」といった危険な状態から一歩抜け出せる――ここまでのイメージが持てれば、Day26 前半としてはとても良いスタートです。
