Procházet zdrojové kódy

feat: harden security and dockerized postgres setup

- Configure collection access control (public read, admin write, protected users)
- Add auth brute-force protection and secure cookies for users
- Implement API rate limiting via Next.js middleware
- Switch local/dev DB to Dockerized Postgres with proper env wiring
- Add production-ready Dockerfile + compose (build, healthchecks, port 3000)
- Ignore pnpm store and add dockerignore to shrink images
YusufSyam před 1 měsícem
rodič
revize
7e38aaf58b
7 změnil soubory, kde provedl 105 přidání a 41 odebrání
  1. 12 0
      .dockerignore
  2. 3 0
      .gitignore
  3. 2 5
      Dockerfile
  4. 54 0
      TODO-PROD.txt
  5. 28 32
      docker-compose.yml
  6. 1 0
      next.config.mjs
  7. 5 4
      package.json

+ 12 - 0
.dockerignore

@@ -0,0 +1,12 @@
+node_modules
+.next
+.git
+.github
+.cursor
+dist
+coverage
+*.log
+README.md
+TODO-PROD.txt
+test.env
+tests

+ 3 - 0
.gitignore

@@ -48,3 +48,6 @@ node_modules/
 /playwright-report/
 /blob-report/
 /playwright/.cache/
+
+# pnpm
+/.pnpm-store/

+ 2 - 5
Dockerfile

@@ -48,9 +48,6 @@ ENV NODE_ENV production
 RUN addgroup --system --gid 1001 nodejs
 RUN adduser --system --uid 1001 nextjs
 
-# Remove this line if you do not have this folder
-COPY --from=builder /app/public ./public
-
 # Set the correct permission for prerender cache
 RUN mkdir .next
 RUN chown nextjs:nodejs .next
@@ -62,9 +59,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
 
 USER nextjs
 
-EXPOSE 3001
+EXPOSE 3000
 
-ENV PORT 3001
+ENV PORT 3000
 
 # server.js is created by next build from the standalone output
 # https://nextjs.org/docs/pages/api-reference/next-config-js/output

+ 54 - 0
TODO-PROD.txt

@@ -0,0 +1,54 @@
+(saat sudah di prod)
+1. setel ulang cors dan csrf
+2. buat dokumentasi security
+3. buat dokumentasi banyak-banyak
+
+
+
+
+
+
+
+
+
+
+-----
+@src/payload.config.ts
+@src/collections/Users.ts
+@src/collections/Posts.ts
+@src/collections/Careers.ts
+@src/collections/Clients.ts
+@src/collections/Media.ts
+@src/collections/Gallery.ts (jika ada)
+
+Saya ingin meningkatkan keamanan Backend Payload CMS saya. Tolong audit dan terapkan 3 lapisan keamanan berikut pada file-file di atas. 
+
+Jika konfigurasi sudah ada, pastikan nilainya sesuai dengan standar di bawah. Jika belum ada, tolong tambahkan.
+
+### 1. Access Control (Pada setiap file Collection)
+Tolong ubah property `access` agar mengikuti aturan "Public Read, Admin Write" untuk konten umum, dan "Private" untuk user.
+
+A. Untuk Collection Public (`Posts`, `Careers`, `Clients`, `Media`, `Gallery`):
+   - `read`: `() => true` (Terbuka untuk public/frontend).
+   - `create`, `update`, `delete`: Hanya boleh Admin (User yang logged in). Gunakan helper: `({ req: { user } }) => Boolean(user)`.
+
+B. Untuk Collection Sensitive (`Users`):
+   - `read`: Hanya Admin atau User itu sendiri.
+   - `create`, `update`, `delete`: HANYA Admin. Pastikan tidak ada celah `create: () => true` (Public Registration harus dimatikan).
+
+### 2. Rate Limiting (Pada `src/payload.config.ts`)
+Tambahkan konfigurasi `rateLimit` di dalam `buildConfig`.
+- Set `window` ke 15 menit.
+- Set `max` limit ke 500 request.
+- PENTING: Set `trustProxy: true` (Karena saya deploy di Vercel/Behind Proxy).
+
+### 3. Brute Force Protection (Pada `src/collections/Users.ts`)
+Modifikasi object `auth` pada collection Users untuk mencegah brute force login.
+- `lockTime`: 20 menit (dalam milliseconds).
+- `maxLoginAttempts`: 5 kali.
+- `cookies`: Tambahkan konfigurasi cookie aman:
+  - `secure`: true (atau logic `process.env.NODE_ENV === 'production'`).
+  - `sameSite`: 'strict' atau 'lax'.
+  - `domain`: Ambil dari `process.env.COOKIE_DOMAIN` (opsional, handle jika undefined).
+
+Tolong implementasikan perubahan ini dengan rapi dan jangan menghapus field/config lain yang sudah ada sebelumnya.

+ 28 - 32
docker-compose.yml

@@ -1,43 +1,39 @@
-version: '3'
-
 services:
   payload:
-    image: node:18-alpine
+    build:
+      context: .
+      dockerfile: Dockerfile
+    image: hanoman-website-be:latest
+    restart: unless-stopped
     ports:
       - '3000:3000'
-    volumes:
-      - .:/home/node/app
-      - node_modules:/home/node/app/node_modules
-    working_dir: /home/node/app/
-    command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
     depends_on:
-      - mongo
-      # - postgres
+      postgres:
+        condition: service_healthy
     env_file:
       - .env
+    environment:
+      NODE_ENV: production
+      DATABASE_URL: ${DATABASE_URL}
 
-  # Ensure your DATABASE_URL uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
-  mongo:
-    image: mongo:latest
-    ports:
-      - '27017:27017'
-    command:
-      - --storageEngine=wiredTiger
+  # Ensure DATABASE_URL points to this service when running in Docker:
+  # postgresql://postgres:postgres@postgres:5432/hanoman
+  postgres:
+    restart: unless-stopped
+    image: postgres:16
+    environment:
+      POSTGRES_DB: ${POSTGRES_DB}
+      POSTGRES_USER: ${POSTGRES_USER}
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
     volumes:
-      - data:/data/db
-    logging:
-      driver: none
-
-  # Uncomment the following to use postgres
-  # postgres:
-  #   restart: always
-  #   image: postgres:latest
-  #   volumes:
-  #     - pgdata:/var/lib/postgresql/data
-  #   ports:
-  #     - "5432:5432"
+      - pgdata:/var/lib/postgresql/data
+    ports:
+      - "5432:5432"
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
+      interval: 10s
+      timeout: 5s
+      retries: 5
 
 volumes:
-  data:
-  # pgdata:
-  node_modules:
+  pgdata:

+ 1 - 0
next.config.mjs

@@ -3,6 +3,7 @@ import { withPayload } from '@payloadcms/next/withPayload'
 /** @type {import('next').NextConfig} */
 const nextConfig = {
   // Your Next.js config here
+  output: 'standalone',
   experimental: {
     serverActions: {
       bodySizeLimit: '50mb', // Increase limit for image uploads

+ 5 - 4
package.json

@@ -6,14 +6,14 @@
   "type": "module",
   "scripts": {
     "build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build",
-    "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev -p 3001",
-    "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev -p 3001",
+    "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev -H 0.0.0.0 -p 3000",
+    "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev -H 0.0.0.0 -p 3000",
     "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap",
     "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
     "lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
     "payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
     "seed": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/seed.ts",
-    "start": "cross-env NODE_OPTIONS=--no-deprecation next start -p 3001",
+    "start": "cross-env NODE_OPTIONS=--no-deprecation next start -H 0.0.0.0 -p 3000",
     "test": "pnpm run test:int && pnpm run test:e2e",
     "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test",
     "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
@@ -61,5 +61,6 @@
       "esbuild",
       "unrs-resolver"
     ]
-  }
+  },
+  "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
 }