OmniStream Docs
  • Panduan Pengguna
  • Developer
  • API Reference
Developer Hub
Pendahuluan
Autentikasi
Model Data
Webhook
WebSocket
Self-Hosting
    Prasyarat Self-HostingDocker ComposeEnvironment VariablesDatabase MigrationsMeta App SetupSMTP ConfigReverse Proxy + CORS ProduksiCORS di Produksi
Error & Rate Limit
Self-Hosting

Reverse Proxy + CORS Produksi

Reverse Proxy + CORS Produksi

Halaman ini membahas dua hal yang wajib dikonfigurasi sebelum anda mengekspos OmniStream ke internet publik:

  1. Reverse proxy Nginx + TLS untuk membungkus api-gateway, ws-server, dan frontend.
  2. Mengganti CorsLayer::permissive() yang saat ini di-hardcode di backend menjadi allowlist yang ketat.

1. Reverse proxy Nginx

Topologi yang disarankan:

Code
Internet │ ▼ ┌─────────────┐ │ Nginx │ TLS termination + CORS hint + gzip └─────┬───────┘ │ 127.0.0.1 (internal) ├──► api-gateway :3000 (REST) ├──► ws-server :3002 (WebSocket) └──► frontend :4000 (SvelteKit)

Contoh server block (minimalis):

Code
# /etc/nginx/sites-available/crm.example.com server { listen 443 ssl http2; server_name crm.example.com; ssl_certificate /etc/letsencrypt/live/crm.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/crm.example.com/privkey.pem; # Frontend (default) location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # REST API location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # WebSocket location /ws { proxy_pass http://127.0.0.1:3002; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 86400; } gzip on; gzip_types application/json text/plain text/css application/javascript; }

Rate limiter tower_governor di api-gateway memakai PeerIpKeyExtractor. Pastikan Nginx meneruskan X-Forwarded-For dan backend anda dikonfigurasi untuk mempercayainya, supaya rate limit dihitung per-IP klien asli — bukan per-IP proxy. Jika tidak, seluruh traffic akan terlihat datang dari 127.0.0.1 dan saling "mencuri" token.

2. CORS produksi — mengapa permisif itu salah

Kondisi saat ini (2026-04-11): baris crates/api-gateway/src/main.rs:253 menggunakan CorsLayer::permissive():

Code
// crates/api-gateway/src/main.rs:250-253 let app = routes::build_router(state) .layer(GovernorLayer::new(governor_conf)) .layer(TraceLayer::new_for_http()) .layer(CorsLayer::permissive());

CorsLayer::permissive() dari tower-http memasang header:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH
  • Access-Control-Allow-Headers: *

Di produksi ini bermasalah:

  1. Cookie httpOnly login tidak akan dikirim untuk request cross-origin ketika Allow-Origin: * — browser menolak mengirim credentials ke origin wildcard. Artinya fitur cookie auth (dokumentasikan di developer/autentikasi/cookies) akan gagal secara diam-diam.
  2. Allowlist wildcard membuka JWT Bearer dari situs manapun yang menyimpan/mencuri token. Meskipun JWT di header sulit dicuri tanpa XSS, praktik ini merusak defense-in-depth.
  3. Dokumentasi publik yang memberi contoh CorsLayer::permissive() mengajarkan pola yang salah untuk produksi.

Catatan ruang lingkup

Halaman ini hanya mendokumentasikan masalah dan rekomendasi perbaikannya. Perubahan kode backend bukan bagian dari plan docs-site v1 — tracking-nya ada di docs-site/SCOPE.md Section 4 (Backend follow-up register, item BE1). Perbaikan kode adalah tugas pemilik backend api-gateway.

3. Rekomendasi: allowlist eksplisit

Ganti CorsLayer::permissive() dengan builder yang membaca allowlist dari FRONTEND_URL (sudah ada di config):

Code
use axum::http::{HeaderValue, Method, header}; use tower_http::cors::{AllowOrigin, CorsLayer}; let allowed_origins: Vec<HeaderValue> = std::iter::once(config.frontend_url.as_str()) // tambahkan origin lain yang sah — misal docs site yang memakai Try-It .chain(std::iter::once("https://docs.example.com")) .filter_map(|s| HeaderValue::from_str(s).ok()) .collect(); let cors = CorsLayer::new() .allow_origin(AllowOrigin::list(allowed_origins)) .allow_methods([ Method::GET, Method::POST, Method::PUT, Method::PATCH, Method::DELETE, Method::OPTIONS, ]) .allow_headers([ header::AUTHORIZATION, header::CONTENT_TYPE, header::ACCEPT, ]) .allow_credentials(true); // WAJIB supaya cookie httpOnly login jalan let app = routes::build_router(state) .layer(GovernorLayer::new(governor_conf)) .layer(TraceLayer::new_for_http()) .layer(cors);

Hal-hal penting:

  • allow_credentials(true) tidak boleh dikombinasikan dengan origin wildcard — browser menolaknya. Karena itu anda wajib memakai AllowOrigin::list(...) dengan origin konkret.
  • Authorization harus masuk allow_headers agar JWT Bearer lewat.
  • Methods yang di-allow harus mencakup semua verb yang benar-benar anda pakai — jangan include TRACE atau method eksotis.
  • Tambahkan origin lain yang sah hanya setelah didiskusikan — setiap origin baru memperluas attack surface.

4. Origin mana yang perlu di-allow?

Biasanya hanya 1–2 origin:

  1. Frontend produksi anda: https://crm.example.com (sama dengan FRONTEND_URL).
  2. Dokumentasi Try-It playground (jika anda meng-host docs site yang memanggil api-gateway dari browser, misal untuk memverifikasi OpenAPI Try-It secara live). Jangan tambahkan ini kecuali memang perlu.

Origin tidak perlu di-allow jika traffic-nya sudah lewat reverse proxy yang sama (same-origin) — kasus paling umum di deployment "frontend + backend di balik 1 Nginx" di atas.

5. Ringkasan follow-up backend

Tugas backend yang docs-site hanya dokumentasikan:

  • BE1 — Ganti CorsLayer::permissive() di crates/api-gateway/src/main.rs:253 dengan allowlist berbasis FRONTEND_URL. Prioritas HIGH sebelum anda membuka API ke internet publik.

Lihat docs-site/SCOPE.md Section 4 untuk daftar lengkap backend follow-up lainnya.

Last modified on June 8, 2026
SMTP ConfigCORS di Produksi
On this page
  • 1. Reverse proxy Nginx
  • 2. CORS produksi — mengapa permisif itu salah
  • 3. Rekomendasi: allowlist eksplisit
  • 4. Origin mana yang perlu di-allow?
  • 5. Ringkasan follow-up backend
Rust
Rust