Postgres Roles dan Privileges
Postgres Roles dan Privileges
Pendahuluan
Mengontrol akses ke data di Postgres sangat penting untuk keamanan data. Postgres menyediakan model permissions yang robust dan fleksibel bagi pengguna untuk mengelola akses ke data mereka. Model permissions ini didasarkan pada konsep object, privilege, dan role yang familiar, namun memiliki kehalusan yang harus dipahami oleh database administrator untuk menciptakan akses yang aman. Dalam artikel ini kita akan melihat secara detail bagaimana roles dan permissions bekerja di Postgres.
Konsep Dasar
Mari kita pahami terlebih dahulu beberapa konsep dasar yang akan digunakan di seluruh artikel ini.
Database Object
Database object adalah entitas apapun yang dibuat di dalam database. Tables, foreign tables, views, materialized views, types, domains, operators, functions, triggers, dan lain-lain adalah database objects. Objects memungkinkan operasi pada mereka yang bervariasi untuk setiap object. Misalnya, Anda dapat melakukan select data dari table dan Anda dapat mengeksekusi sebuah function.
Privilege
Privilege mengontrol operasi apa yang diizinkan untuk dijalankan pada database object. Misalnya, privilege select pada table mengontrol kemampuan untuk membaca data dari table tersebut. Demikian pula, privilege execute mengontrol kemampuan untuk mengeksekusi sebuah function. Privileges diberikan kepada roles. Sebuah role harus memiliki permission untuk operasi yang dilakukannya pada sebuah object.
Role
Role adalah user atau group. User adalah seseorang yang dapat login ke database. Group adalah kumpulan users untuk memudahkan pengelolaan privileges bagi users. Berbeda dengan user, group tidak dapat login ke database. Perbedaan antara user dan group sebagian besar tidak penting bagi Postgres karena keduanya adalah roles, tetapi tetap berguna untuk menganggap mereka sebagai konsep yang terpisah untuk kemudahan pemahaman.
Owner
Setiap database object memiliki owner. Owner memiliki kontrol penuh atas object tersebut. Mereka dapat memodifikasi atau menghapus object atau memberikan privileges kepada users dan groups lain. Ketika user membuat object baru, mereka menjadi owner dari object tersebut. Owner juga dapat mentransfer kepemilikan objects ke roles lain. Sebuah role tidak dapat dihapus sebelum semua kepemilikan objects yang dimilikinya ditransfer ke role lain.
Dengan konsep dasar ini sudah didefinisikan, mari kita lihat model permissions di Postgres secara mendalam. Sisa artikel ini akan lebih seperti tutorial, jadi Anda dapat mengikutinya. Saya akan menggunakan hosted Supabase project, tetapi Anda bebas menggunakan instalasi Postgres apapun.
Setting Up
Buat Supabase project baru (atau gunakan yang sudah ada) dan salin connection string URI-nya dari halaman Database Settings. URI-nya terlihat seperti berikut:
Di mana USER adalah user untuk koneksi, YOUR-PROJECT-REF adalah string yang secara unik mengidentifikasi project Anda, YOUR-PASSWORD adalah password database untuk user USER, dan REGION-SUBDOMAIN adalah subdomain di mana database Anda di-host.
Gunakan command line tool psql untuk terhubung ke database:
Setelah terhubung, konfirmasi bahwa Anda terhubung sebagai user postgres dengan menjalankan command select current_role:
Membuat Roles
Sekarang, mari kita buat dua users bernama junior_dev dan senior_dev.
Database role dapat dibuat dengan command create role. Karena user adalah role yang dapat login, gunakan parameter login:
Anda sekarang dapat mengkonfirmasi bahwa users junior_dev dan senior_dev dapat login ke database:
Untuk sisa artikel ini, buka tiga terminal dan login masing-masing dengan junior_dev, senior_dev, dan postgres untuk dengan mudah beralih di antara mereka. Setiap command yang dieksekusi akan mencantumkan di awal user yang harus mengeksekusinya, misalnya:
Membuat Objects dan Memberikan Privileges
Mari kita coba membuat table sebagai junior_dev:
Apa yang terjadi? Error permission denied for schema public memberitahu kita bahwa junior_dev tidak memiliki beberapa permission pada schema public. Kita dapat memeriksa permissions yang ada pada schema menggunakan command \dn+ <schema> di psql:
Memang, kolom Access privileges tidak mencantumkan role junior_dev di mana pun, yang berarti ia tidak memiliki permission apapun pada schema public. Bagaimana kita memperbaiki ini? User postgres di Supabase hosted databases adalah role yang powerful dengan lebih banyak privileges daripada banyak roles lainnya. Anggap role postgres sebagai role admin, meskipun ia bukan superuser. Kita dapat menggunakan role ini untuk memberikan permissions yang sesuai.
Jadi, mari kita beralih ke koneksi user postgres dan berikan junior_dev permission untuk membuat objects di schema public. Format umum dari command grant adalah grant <privilege> on <object> to <role>. Anda dapat berkonsultasi dengan halaman privileges di dokumentasi Postgres untuk mengetahui nama privilege yang benar.
Mari kita periksa permissions lagi:
Kali ini kita melihat baris baru di kolom access privileges: junior_dev=C/pg_database_owner
💡 Grantor dalam kasus di atas adalah pg_database_owner yang merupakan role yang memiliki schema public. pg_database_owner memiliki owner dari database saat ini sebagai satu-satunya member, yaitu postgres dalam kasus kita.
Sekarang mari kita coba membuat table lagi:
Mari kita masukkan beberapa data ke dalamnya:
Sekarang beralih ke senior_dev dan coba select data dari table:
senior_dev tidak dapat select data dari table public.apps. Mari kita debug error permissions seperti sebelumnya. Command di psql untuk melihat table permissions adalah \dp <tablename>:
Tidak ada access privileges sama sekali. Seperti yang kita lakukan sebelumnya, mari kita beralih ke user postgres dan perbaiki permissions. Halaman privileges memberitahu kita bahwa kita perlu memberikan privilege select kepada senior_dev agar mereka dapat select data dari table public.apps:
Mengapa postgres tidak dapat memberikan privilege select? Karena ia bukan owner, juga tidak memiliki access privileges apapun pada table tersebut. Tetapi bagaimana junior_dev dapat select data dari table? Itu karena junior_dev adalah owner dari table:
Karena owner memiliki semua privileges pada sebuah object, junior_dev dapat select data. junior_dev juga dapat memberikan privileges pada objects yang dimiliki kepada roles lain. Mari kita perbaiki permissions dengan junior_dev:
Sekarang senior_dev dapat select data:
Grant Options
Opsi lain dalam contoh di atas adalah bagi junior_dev untuk memberikan privilege untuk memberikan privilege select kepada role postgres. Role postgres kemudian akan dapat memberikan privilege select kepada senior_dev. Untuk mencoba ini, mari kita revoke privilege yang sebelumnya diberikan kepada senior_dev terlebih dahulu:
Dan kemudian berikan privilege select dengan with grant option kepada postgres:
Sekarang, jika kita melihat permissions pada table public.apps:
Perhatikan * setelah r di postgres=r*/junior_dev, yang menunjukkan bahwa permission select diberikan dengan with grant option. Sekarang postgres dapat memberikan privilege select kepada senior_dev:
Dan senior_dev memiliki privilege select dan dapat select dari table lagi:
Command grant hanya menambahkan privileges untuk objects yang sudah ada. Bagaimana jika kita ingin memberikan privileges tertentu pada objects segera setelah dibuat? Di situlah default access privileges berperan.
Default Access Privileges
Jika junior_dev sekarang membuat table lain, ia harus memberikan privileges lagi kepada senior_dev. Untuk menghindari melakukan ini setiap kali junior_dev membuat table baru, kita dapat mengubah default access privileges milik junior_dev. Pertama mari kita lihat default privileges saat ini pada schema public:
Baik junior_dev maupun senior_dev tidak terdaftar. Mari kita ubah default privileges milik junior_dev:
Di sini kita mengubah default privileges sedemikian rupa sehingga setiap kali junior_dev membuat table baru di schema public, senior_dev harus diberikan privilege select padanya. Mari kita periksa privileges lagi:
Baris pertama sekarang menunjukkan default access privilege yang baru saja kita tambahkan. Mari kita buat table baru dan masukkan sebuah row ke dalamnya:
Sekarang coba select data di public.users dari senior_dev:
Perhatikan bahwa kita langsung dapat select data dari public.users tanpa grants eksplisit dari junior_dev.
Jelas dari contoh di atas bahwa owner memiliki semua privileges pada sebuah object yang dapat mereka berikan kepada roles lain. Tetapi bisa menjadi merepotkan bagi owner untuk terus memberikan privileges yang sama kepada setiap role baru. Ada cara yang lebih baik. Kita dapat memastikan bahwa objects dimiliki oleh group dan kemudian users mana pun yang memerlukan akses ke objects tersebut diberi keanggotaan ke group. Mari kita lihat bagaimana ini bekerja.
Membuat Groups
Kita ingin membuat group developers baru yang akan memiliki table public.apps. Kemudian kita akan menjadikan junior_dev dan senior_dev sebagai members dari group developers. Ini akan memastikan bahwa mereka berdua memiliki jenis akses yang sama, tanpa secara eksplisit memberikan privileges setelah membuat object baru.
Pertama, mari kita drop table public.apps:
Mari kita juga revoke privilege create dari junior_dev pada schema public:
Mari kita buat group developers. Karena group adalah role yang tidak diizinkan untuk login, gunakan parameter nologin:
Anda tidak dapat login dengan role developers karena kita mengatur parameter nologin. Parameter login / nologin mengontrol attribute login dari sebuah role. Sebelumnya kita juga mengatur attribute password dari roles junior_dev dan senior_dev. Ada banyak role attributes lain yang akan kita bicarakan nanti di artikel ini.
Mari kita berikan privilege create kepada group developers:
Karena users junior_dev dan senior_dev tidak memiliki privilege create pada schema public, mereka tidak dapat membuat objects di dalamnya. Group developers bisa, tetapi kita tidak dapat login dengannya. Jadi bagaimana kita membuat public.apps yang dimiliki oleh developers? Nah, user dapat sementara menyamar sebagai group jika mereka adalah member dari group tersebut. Jadi mari kita pastikan junior_dev dan senior_dev adalah members dari group developers:
Command grant <group> to <user> adalah varian lain dari command grant tetapi harus dibaca secara mental sebagai add <user> to <group>.
💡 Dalam bentuk command grant ini, Postgres tidak memeriksa bahwa <user> adalah user dan <group> adalah group. Artinya, Postgres tidak peduli dengan kemampuan login roles ini. Oleh karena itu, grant <user1> to <user2> juga diperbolehkan, dalam hal ini <user2> dapat menyamar sebagai <user1>. Faktanya, untuk sebagian besar, Postgres tidak terlalu peduli dengan perbedaan antara user atau group. Baginya, keduanya hanyalah roles.
Sekarang junior_dev (atau senior_dev) dapat menyamar sebagai developers:
Dan membuat table public.apps:
Yang dimiliki oleh group developers:
Sekarang jika Anda menghentikan penyamaran:
Dan coba insert atau select data dari public.apps, itu berhasil:
Alasan junior_dev dan senior_dev dapat insert dan select data adalah karena mereka adalah bagian dari group developers. Jika developer baru dibuat nanti, mereka hanya tinggal menjalankan grant developers to <new dev> untuk memiliki akses yang sama seperti developer lainnya. Bandingkan ini dengan metode sebelumnya di mana user baru harus meminta owner dari setiap object untuk memberikan mereka permissions.
Opsi Grant Lanjutan
Menjadikan user sebagai bagian dari group lain mungkin memberikannya tiga kemampuan:
Kemampuan untuk menyamar sebagai group
Kemampuan untuk mewarisi permissions dari group
Kemampuan untuk menambah atau menghapus users lain dari group
Semua kemampuan ini dapat dikontrol secara independen saat menjalankan command grant <group> to <user> dengan menggunakan with <option name> true/false yang ditambahkan ke command tersebut. Nama dari masing-masing opsi di atas adalah set, inherit, dan admin. Misalnya, untuk melarang user menyamar sebagai group, jalankan grant <group> to <user> with set false.
💡 Di Postgres 15, hanya opsi admin yang dapat dikontrol. Di Postgres 16, opsi inherit dan set juga dapat dikontrol. Jika opsi-opsi ini dihilangkan dari command grant, nilai default mereka adalah true untuk set dan inherit dan false untuk admin.
Untuk mendemonstrasikan, jika kita mengaktifkan opsi admin pada junior_dev:
Ia akan dapat menghapus senior_dev dari group developers:
Tanpa opsi admin, junior_dev tidak akan dapat melakukan ini.
Role Attributes
Setiap role memiliki beberapa attributes yang terkait dengannya yang mengontrol perilaku role tersebut. Beberapa yang umum tercantum di bawah ini. Untuk daftar lengkap dan detailnya, lihat dokumentasi Postgres role attributes.
login- mengontrol kemampuan role untuk loginsuperuser- mengontrol apakah role adalah superuser atau tidak (lihat bagian selanjutnya untuk detail)createdb- mengontrol apakah role akan dapat membuat databasescreaterole- mengontrol apakah role akan dapat membuat roles lainreplication- mengontrol apakah role dapat digunakan untuk memulai replicationbypassrls- mengontrol apakah role dapat melewati row level securityconnection limit- membatasi jumlah maksimum koneksi yang dapat dibuat role ke databaseinherit- mengontrol apakah role dapat mewarisi permissions dari roles yang menjadi membernya
Special Roles
Ada dua special roles yang memainkan peran penting dalam bagaimana roles dan privileges dikelola.
Superuser
superuser adalah role dengan attribute superuser yang diatur. superuser seperti root user pada sistem operasi *nix. Ia sangat powerful dan melewati semua privilege checks kecuali autentikasi saat login. Untuk alasan ini, Anda harus menghindari bekerja dengan role ini sebanyak mungkin. Hanya superusers yang dapat membuat roles superuser lainnya.
Public
public adalah group role yang setiap role lainnya secara otomatis menjadi bagiannya. Hanya ada satu role public. Jadi berbeda dengan superuser, tidak ada attribute role public. Role public digunakan untuk menyediakan privileges yang dianggap sangat umum sehingga setiap role harus memilikinya. Privileges ini adalah:
connect- kemampuan untuk terhubung ke databasetemporary- kemampuan untuk membuat temporary tablesexecute- kemampuan untuk mengeksekusi functionsusage- kemampuan untuk menggunakan object seperti domain, language, atau type
Role public tidak dapat dihapus, tetapi privileges-nya dapat di-revoke.
Privileges dari sebuah role adalah union dari tiga set privileges:
Yang diberikan kepada role secara langsung
Yang diwarisi dari roles yang menjadi member eksplisit dari role ini
Yang diwarisi dari role
public, yang setiap role secara implisit menjadi membernya
Privileges yang diwarisi dari role public adalah sumber kebingungan umum saat bekerja dengan roles di Postgres. Bayangkan bahwa kita ingin melarang junior_dev dari mengeksekusi functions. Mari kita buat sebuah function terlebih dahulu:
junior_dev saat ini dapat mengeksekusi function ini:
Sekarang mari kita revoke permission execute milik junior_dev:
Tetapi junior_dev masih dapat mengeksekusi function tersebut:
Bagaimana? Mari kita periksa privileges function add:
junior_dev tidak memiliki privilege apapun, tetapi nama role yang hilang di baris =X/postgres berarti role public. Mari kita revoke execute dari public:
Sekarang junior_dev tidak dapat lagi mengeksekusi function add:
Hal lain yang perlu diperhatikan di sini adalah bahwa ketika kita me-revoke privilege execute pada add dari junior_dev, sebenarnya tidak ada yang perlu di-revoke. Tetapi Postgres tidak menunjukkan peringatan apapun kepada kita. Jadi penting untuk selalu memeriksa permissions secara eksplisit, terutama setelah command revoke.
Ringkasan
Untuk meringkas:
Setiap database object memiliki owner
Operasi pada database objects dikontrol oleh privileges
Owners dapat memberikan privileges pada objects yang dimiliki kepada roles lain
Roles dapat berupa users atau groups
Roles dapat mewarisi permissions dari roles yang menjadi membernya
Role
publicadalah role yang setiap role lainnya secara implisit menjadi membernya. Ia tidak dapat dihapus, tetapi privileges-nya dapat di-revokeRoles
superuseradalah roles yang sangat powerful yang melewati semua privilege checks dan harus digunakan dengan hati-hatiCommand
granthanya memberikan privileges pada objects yang sudah adaDefault privileges mengontrol privileges yang akan diberikan kepada objects yang dibuat di masa depan
Kesimpulan
Permissions Postgres mengikuti model objects, roles, privileges tradisional tetapi memiliki kehalusan yang dapat mengejutkan users kecuali mereka memahaminya secara detail. Dalam artikel ini kita bereksperimen dengan model ini untuk memahaminya secara mendalam. Semoga pemahaman ini akan memungkinkan Anda untuk mengelola dan melindungi database Postgres Anda dengan lebih efektif.
Tips Tambahan untuk Praktik Terbaik
Berikut beberapa tips praktis yang dapat Anda terapkan:
1. Gunakan Groups untuk Manajemen yang Lebih Baik
Selalu ciptakan groups untuk mengorganisir users dengan hak akses yang serupa. Ini membuat manajemen privileges jauh lebih mudah saat tim Anda berkembang.
2. Hindari Penggunaan Superuser
Hanya gunakan role superuser untuk tugas-tugas administratif yang benar-benar memerlukannya. Untuk operasi sehari-hari, gunakan roles dengan privileges yang lebih terbatas.
3. Audit Privileges Secara Berkala
Gunakan commands seperti \dp, \dn+, dan \ddp secara berkala untuk memeriksa permissions yang ada dan memastikan tidak ada akses yang tidak diinginkan.
4. Dokumentasikan Structure Roles Anda
Buat dokumentasi yang jelas tentang roles apa yang ada, apa purposes mereka, dan siapa yang menjadi members dari groups tertentu.
5. Gunakan Default Privileges dengan Bijak
Setting default privileges dapat menghemat banyak waktu, tetapi pastikan Anda memahami implikasinya. Default privileges yang terlalu permissive dapat menciptakan risiko keamanan.
6. Hati-hati dengan Role Public
Ingat bahwa semua roles secara otomatis adalah member dari role public. Periksa dan revoke privileges dari public yang tidak diperlukan untuk semua users.
7. Principle of Least Privilege
Selalu berikan privileges minimum yang diperlukan untuk sebuah role menjalankan tugasnya. Jangan memberikan lebih banyak akses dari yang dibutuhkan.
Dengan memahami dan menerapkan konsep-konsep ini, Anda akan dapat membuat sistem database Postgres yang aman dan mudah dikelola.
Baik, saya akan cek dulu apakah ada hal yang missing dari tutorial di atas, lalu membuat setup end-to-end untuk kasus e-commerce.
Hal Yang Missing dari Tutorial
Beberapa hal penting yang belum dibahas secara detail:
Row Level Security (RLS) - Sangat penting untuk multi-tenant applications
Schema Isolation - Memisahkan objects berdasarkan schema untuk organisasi yang lebih baik
Revoke Cascade - Bagaimana revoke mempengaruhi privileges yang di-grant dengan grant option
Ownership Transfer - Detail tentang
ALTER ... OWNER TOConnection Management - Membatasi koneksi per role
Password Policies - Praktek terbaik untuk password management
Audit Logging - Tracking siapa melakukan apa
Sekarang, mari kita buat setup end-to-end untuk perusahaan e-commerce!
Setup End-to-End: E-Commerce Company Role & Privileges Management
Skenario Bisnis
ShopKita adalah platform e-commerce dengan requirements:
Multiple departments: Engineering, Analytics, Customer Support, Marketing
Different access levels: Read-only, Read-Write, Admin
Data segregation antara operational data dan analytics
Audit trail untuk compliance
Security yang ketat untuk data customer
Arsitektur Database
Step-by-Step Implementation
Step 1: Koneksi sebagai Superuser
Step 2: Setup Schemas
Step 3: Buat Group Roles
Step 4: Setup Privileges untuk Groups
Step 5: Buat User Roles (Individual Users)
Step 6: Buat Sample Tables
Step 7: Insert Sample Data
Step 8: Setup Row Level Security (RLS)
Step 9: Create Audit Functions
Step 10: Testing Access
Step 11: Maintenance Scripts
Step 12: Monitoring dan Reporting
Step 13: Documentation Template
Checklist Implementasi
Best Practices yang Diterapkan
Principle of Least Privilege: Setiap role hanya mendapat akses minimal yang dibutuhkan
Schema Segregation: Data dipisah berdasarkan domain untuk organisasi yang lebih baik
Group-based Management: Menggunakan groups untuk memudahkan management user
Audit Trail: Semua perubahan penting di-log untuk compliance
Row Level Security: Protection tambahan untuk data sensitif
Connection Limits: Mencegah resource exhaustion
Password Expiry: Keamanan tambahan dengan valid until
Default Privileges: Otomatis apply privileges ke objects baru
Documentation: Self-documenting dengan comments dan views
Operasional Harian
Menambah Developer Baru
Promote Developer ke Admin
Temporary Access untuk Vendor
Check User Activity
Ini adalah setup yang comprehensive dan production-ready untuk e-commerce company!
Last updated