Tutorial OpenFGA & Go: Solusi Otorisasi ReBAC Zero Trust di VPS
Poin Kunci Artikel Ini:
- Jam empat sore di hari Jumat biasanya jadi waktu paling rawan buat developer backend.
- Kopi sudah dingin, mental sudah siap menyambut akhir pekan, lalu tiba-tiba notifikasi Slack menjerit.
- Anda langsung membuka repositori kode Go, menyisir baris-baris middleware otorisasi, dan menemukan tumpukan yang sudah mirip mi goreng instan.
Jam empat sore di hari Jumat biasanya jadi waktu paling rawan buat developer backend. Kopi sudah dingin, mental sudah siap menyambut akhir pekan, lalu tiba-tiba notifikasi Slack menjerit. Ada tiket bug darurat: akun mitra vendor yang kontraknya sudah habis tadi siang ternyata masih bisa mengunduh file laporan keuangan sensitif di aplikasi dashboard.
Kepanikan dimulai. Anda langsung membuka repositori kode Go, menyisir baris-baris middleware otorisasi, dan menemukan tumpukan if-else yang sudah mirip mi goreng instan. Logikanya hanya mengecek apakah isi token JWT memiliki role vendor_editor. Masalahnya, role itu berlaku menyeluruh di sistem tanpa peduli siapa pemilik file atau folder yang diakses.
Kondisi seperti ini sangat wajar terjadi ketika aplikasi bertumbuh cepat. Pendekatan otorisasi lama yang mengandalkan peran pengguna mulai kewalahan menghadapi kebutuhan bisnis yang makin rumit. Misalnya saat tim produk minta aturan baru: User hanya boleh membaca dokumen finansial kalau dia anggota tim keuangan atau atasan langsung dari pembuat dokumen.
Di panduan ini, kita bakal membedah solusi otorisasi modern yang dipakai raksasa teknologi seperti Google, Airbnb, dan Carta: Relationship-Based Access Control (ReBAC) berbasis OpenFGA. Kita akan langsung mempraktikkan cara install OpenFGA di VPS dan menghubungkannya dengan microservice Go berarsitektur Zero Trust.
Kenapa RBAC Biasa Bikin Pusing dan Kenapa Harus ReBAC?
Saat aplikasi masih kecil, sistem Role-Based Access Control (RBAC) terasa sangat praktis. Anda cukup membuat tabel users, roles, dan permissions di database relational. Di sisi backend Go, middleware tinggal membaca isi token JWT lalu mengecek apakah role user bernilai admin atau editor.
Namun begitu sistem berkembang menjadi arsitektur microservices dan hirarki data makin rumit, RBAC mulai menunjukkan titik lelahnya:
- Role Explosion (Ledakan Role): Jumlah role mendadak membengkak tak terkendali. Anda mendadak punya role seperti
editor_folder_a,viewer_finance_v2, atauadmin_subteam_3. Database pun penuh dengan data role redundan. - Keterikatan Ketat (Tight Coupling): Kode otorisasi tersebar acak di puluhan microservice backend. Tiap kali ada perubahan kebijakan akses dari manajemen, Anda harus mengubah logika dan melakukan deploy ulang pada banyak service secara bersamaan.
- Kehilangan Konteks Hubungan: RBAC tahu siapa Anda, tetapi RBAC tidak tahu apa hubungan Anda dengan objek data yang ingin Anda akses. RBAC kesulitan menangani hirarki bertingkat seperti folder di dalam folder.
Untuk mengatasi kebuntuan ini, Google menerbitkan paper ilmiah bertajuk Zanzibar pada tahun 2019. Zanzibar adalah sistem otorisasi terpusat milik Google yang mengamankan Google Drive, YouTube, dan Google Cloud IAM. Zanzibar menggeser fokus otorisasi dari sekadar stempel role ke rantai relasi (relationship) antara objek dan subjek.
OpenFGA (Fine-Grained Authorization) merupakan proyek open-source di bawah naungan CNCF (Cloud Native Computing Foundation) yang mengimplementasikan konsep Google Zanzibar. Dalam filosofi Zero Trust, aplikasi backend tidak boleh percaya begitu saja hanya karena request membawa token JWT yang valid atau berasal dari jaringan internal. Setiap ada pemanggilan API ke resource penting, backend Go akan langsung bertanya ke server OpenFGA: Apakah user A punya relasi viewer pada objek document:123?
Setup Server OpenFGA di VPS Menggunakan Docker Compose
Mari kita langsung praktik. Kita bakal menyiapkan server OpenFGA bersama database PostgreSQL sebagai media penyimpanan data relasi (tuples) di VPS Ubuntu.
1. Menyiapkan Docker Compose
Login ke VPS Anda melalui SSH, buat direktori baru bernama openfga-setup, lalu buat file docker-compose.yml dengan isi berikut:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: openfga-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secretpassword
POSTGRES_DB: openfga
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
openfga-migrate:
image: openfga/openfga:latest
container_name: openfga-migrate
command: migrate
environment:
OPENFGA_DATASTORE_ENGINE: postgres
OPENFGA_DATASTORE_URI: postgres://postgres:secretpassword@postgres:5432/openfga?sslmode=disable
depends_on:
postgres:
condition: service_healthy
openfga:
image: openfga/openfga:latest
container_name: openfga-server
command: run
environment:
OPENFGA_DATASTORE_ENGINE: postgres
OPENFGA_DATASTORE_URI: postgres://postgres:secretpassword@postgres:5432/openfga?sslmode=disable
OPENFGA_HTTP_ADDR: 0.0.0.0:8080
OPENFGA_GRPC_ADDR: 0.0.0.0:8081
ports:
- "8080:8080"
- "8081:8081"
- "3000:3000"
depends_on:
openfga-migrate:
condition: service_completed_successfully
volumes:
pgdata:
Jalankan seluruh service di latar belakang dengan perintah berikut:
docker compose up -dSetelah container berjalan, OpenFGA HTTP API akan aktif di port 8080, gRPC API di port 8081, dan dashboard UI Playground di port 3000.
2. Merancang Schema Otorisasi (FGA DSL)
OpenFGA memakai bahasa deklaratif yang sederhana (DSL) untuk mendefinisikan hubungan antar objek. Bayangkan kasus berikut: kita ingin mengatur akses folder dan dokumen di aplikasi perusahaan.
- Objek
userbertindak sebagai pengakses. - Objek
folderpunya relasiownerdanviewer. Siapa saja yang menjadi owner otomatis berhak menjadi viewer. - Objek
docpunya relasiparentyang menunjuk kefolder, serta relasiownerdanviewer. Hak akses viewer pada dokumen bisa didapat secara langsung atau diwarisi otomatis dari folder induknya.
Buat file baru bernama model.fga di server Anda:
model
schema 1.1
type user
type folder
relations
define owner: [user]
define viewer: [user] or owner
type doc
relations
define parent: [folder]
define owner: [user]
define viewer: [user] or owner or viewer from parent
3. Membuat Store dan Mengunggah Schema
Kita dapat memanfaatkan alat bantu Curl untuk membuat Store baru di OpenFGA dan mendaftarkan schema yang sudah dirancang tadi.
Jalankan perintah ini untuk membuat Store baru:
curl -X POST http://localhost:8080/stores \
-H "Content-Type: application/json" \
-d '{"name": "otorisasi-perusahaan"}'Response dari API akan mengembalikan JSON yang berisi id store (contoh: 01H8...). Simpan ID tersebut karena akan dipakai untuk langkah selanjutnya.
Selanjutnya, daftarkan schema otorisasi kita menggunakan endpoint penulisan authorization model:
curl -X POST http://localhost:8080/stores/YOUR_STORE_ID/authorization-models \
-H "Content-Type: application/json" \
-d '{
"schema_version": "1.1",
"type_definitions": [
{"type": "user"},
{
"type": "folder",
"relations": {
"owner": {"this": {}},
"viewer": {"union": {"child": [{"this": {}}, {"computedUserset": {"relation": "owner"}}]}}
}
},
{
"type": "doc",
"relations": {
"parent": {"this": {}},
"owner": {"this": {}},
"viewer": {
"union": {
"child": [
{"this": {}},
{"computedUserset": {"relation": "owner"}},
{"tupleToUserset": {"tupleset": {"relation": "parent"}, "computedUserset": {"relation": "viewer"}}}
]
}
}
}
}
]
}'Menulis Data Relasi (Tuples) ke OpenFGA
Di OpenFGA, hak akses tidak disimpan sebagai flag atribut biasa, melainkan berbentuk tuple relasi dengan format: user:siapa#relasi@objek:apa.
Mari masukkan data skenario nyata:
- User
budiadalah owner darifolder:keuangan. - Dokumen
doc:gaji-2024memiliki parentfolder:keuangan. - User
sitihanya diberikan akses viewer langsung kedoc:gaji-2024.
Jalankan perintah Curl untuk menulis tuple data ini ke server OpenFGA:
curl -X POST http://localhost:8080/stores/YOUR_STORE_ID/write \
-H "Content-Type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{"user": "user:budi", "relation": "owner", "object": "folder:keuangan"},
{"user": "folder:keuangan", "relation": "parent", "object": "doc:gaji-2024"},
{"user": "user:siti", "relation": "viewer", "object": "doc:gaji-2024"}
]
}
}'Perhatikan hal ajaib di sini: Kita tidak perlu mendaftarkan user:budi secara eksplisit sebagai viewer pada doc:gaji-2024. Karena budi adalah owner folder:keuangan, dan folder tersebut adalah parent dari doc:gaji-2024, OpenFGA secara otomatis mengevaluasi bahwa budi sah menjadi viewer dokumen tersebut berdasarkan aturan hirarki schema yang kita buat.
Integrasi SDK OpenFGA ke Microservice Go
Sekarang saatnya menghubungkan logika otorisasi terpusat ini ke dalam aplikasi backend berbasis Go. Kita akan memanfaatkan SDK resmi Go dari OpenFGA.
1. Inisialisasi Project Go
Buat folder baru untuk project Go Anda, lalu unduh library SDK OpenFGA:
mkdir go-openfga-demo && cd go-openfga-demo
go mod init go-openfga-demo
go get github.com/openfga/go-sdk2. Membuat Code Client dan Middleware Otorisasi
Buat file main.go dan tuliskan kode berikut. Kode ini menyimulasikan service HTTP backend Go yang mencegat request masuk dan memverifikasi izin akses ke OpenFGA server sebelum merespons client.
package main
import (
"context"
"fmt"
"log"
"net/http"
openfga "github.com/openfga/go-sdk"
"github.com/openfga/go-sdk/client"
)
type AuthzService struct {
FgaClient *client.OpenFgaClient
}
func NewAuthzService(apiUrl string, storeId string) (*AuthzService, error) {
configuration := &client.ClientConfiguration{
ApiUrl: apiUrl,
StoreId: storeId,
}
fgaClient, err := client.NewSdkClient(configuration)
if err != nil {
return nil, err
}
return &AuthzService{FgaClient: fgaClient}, nil
}
// CheckAccess mengecek apakah seorang user boleh melakukan aksi terhadap objek tertentu
func (a *AuthzService) CheckAccess(ctx context.Context, user string, relation string, object string) (bool, error) {
body := client.ClientCheckRequest{
User: user,
Relation: relation,
Object: object,
}
response, err := a.FgaClient.Check(ctx).Body(body).Execute()
if err != nil {
return false, err
}
return response.GetAllowed(), nil
}
func main() {
// Ganti dengan Store ID hasil setup Anda di VPS
storeID := "YOUR_STORE_ID"
fgaApiUrl := "http://localhost:8080"
authz, err := NewAuthzService(fgaApiUrl, storeID)
if err != nil {
log.Fatalf("Gagal inisialisasi OpenFGA Client: %v", err)
}
// Handler simulasi baca dokumen
http.HandleFunc("/document/read", func(w http.ResponseWriter, r *http.Request) {
// Di aplikasi nyata, userID didapat dari parsing token JWT
userID := r.URL.Query().Get("user_id")
docID := r.URL.Query().Get("doc_id")
if userID == "" || docID == "" {
http.Error(w, "Parameter user_id dan doc_id wajib diisi", http.StatusBadRequest)
return
}
userFormat := fmt.Sprintf("user:%s", userID)
docFormat := fmt.Sprintf("doc:%s", docID)
// Terapkan prinsip Zero Trust: selalu verifikasi ke OpenFGA
allowed, err := authz.CheckAccess(r.Context(), userFormat, "viewer", docFormat)
if err != nil {
http.Error(w, "Gagal memproses otorisasi", http.StatusInternalServerError)
log.Printf("Error check FGA: %v\n", err)
return
}
if !allowed {
http.Error(w, "Akses Ditolak: Anda tidak memiliki izin membaca dokumen ini", http.StatusForbidden)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Akses Diterima! Selamat datang %s. Berikut isi dokumen %s...", userID, docID)
})
log.Println("Server Go berjalan di port 8082...")
log.Fatal(http.ListenAndServe(":8082", nil))
}Pengujian Skenario Zero Trust
Jalankan aplikasi backend Go Anda:
go run main.goMari kita uji beberapa skenario akses menggunakan perintah cURL di terminal berbeda untuk membuktikan bahwa logika otorisasi ReBAC bekerja sesuai ekspektasi:
Skenario 1: Budi membaca dokumen gaji (Pewarisan Akses)
curl -i "http://localhost:8082/document/read?user_id=budi&doc_id=gaji-2024"Hasil: HTTP/1.1 200 OK. Budi berhasil mengakses dokumen ini karena Budi adalah owner dari folder induknya, meskipun kita tidak pernah mendaftarkan izin Budi secara langsung di dokumen tersebut.
Skenario 2: Siti membaca dokumen gaji (Akses Langsung)
curl -i "http://localhost:8082/document/read?user_id=siti&doc_id=gaji-2024"Hasil: HTTP/1.1 200 OK. Siti berhasil membaca dokumen karena ada relasi viewer langsung yang terdaftar untuknya.
Skenario 3: Joko mencoba membobol akses (User Tanpa Relasi)
curl -i "http://localhost:8082/document/read?user_id=joko&doc_id=gaji-2024"Hasil: HTTP/1.1 403 Forbidden. Server menolak request Joko karena tidak ada relasi langsung maupun tidak langsung yang menghubungkan Joko dengan dokumen tersebut.
Langkah Keamanan & Kesimpulan
Menggeser sistem otorisasi dari RBAC biasa ke ReBAC berbasis OpenFGA membuat arsitektur aplikasi Anda jauh lebih rapi, scalable, dan aman. Logika bisnis otorisasi kini terisolasi sepenuhnya di server OpenFGA. Jika suatu saat tim produk ingin menambahkan aturan baruβmisalnya menambahkan role editor atau hak akses terbatas waktuβAnda cukup memperbarui schema FGA tanpa perlu membongkar atau merombak ulang kode microservice Go Anda.
Sebelum menerapkan arsitektur ini ke lingkungan produksi (production VPS), pastikan Anda melakukan langkah-langkah pengamanan ekstra berikut:
- Amankan Port OpenFGA: Jangan buka port
8080dan8081ke publik secara bebas. Gunakan firewall VPS (UFW) agar port tersebut hanya bisa diakses oleh jaringan internal backend Go Anda. - Aktifkan Otentikasi Pihak Ketiga: Konfigurasikan OpenFGA agar membutuhkan token Preshared Key (PSK) atau OIDC/OAuth2 pada setiap request API yang masuk.
- Gunakan Pre-authorization Caching: Untuk request berfrekuensi sangat tinggi, manfaatkan fitur in-memory cache di Go agar tidak menembak server OpenFGA secara terus-menerus untuk query yang sama.
Sekarang saatnya Anda mencoba sendiri. Ambil salah satu VPS percontohan Anda, jalankan file Docker Compose OpenFGA di atas, dan mulailah membersihkan tumpukan if-else otorisasi di repositori Go Anda sebelum tiket bug Jumat sore kembali menghantui!



