Skip to content

[TASK-TRUST-PROXY-IP-RESOLVER-AND-BILLING-FALLBACK] Resolusi Alamat IP Client di Balik Reverse Proxy Gateway & Normalisasi Fallback Nama Kementerian Billing ​

Modul: Pengangkut | Tipe: Bugfix | Stage Saat Ini: STAGING | Tanggal Dokumen: 2026-10-12

STAGING STAGE (UAT VERIFICATION)

Dokumen ini sedang berada dalam tahap STAGING. Perubahan logika backend, penanganan konfigurasi reverse proxy, dan standardisasi fallback nama kementerian billing telah diterapkan di lingkungan Staging (UAT) untuk proses verifikasi QA.


1. Informasi Tugas & Referensi Bisnis ​

  • Task ID / Nomor Referensi: TASK-TRUST-PROXY-IP-RESOLVER-AND-BILLING-FALLBACK

  • Sumber Instruksi: Internal Fix & Architecture Hardening / Code Maintenance

  • Dokumen Referensi / Lampiran: Tidak ada

  • Task / Dependensi Terkait (Cross-Service): Tidak ada

  • Modul Terkait: Pengangkut

  • Service / Repository: pengangkut-backend

  • PIC Pengembang: Hermansyah Efendi

  • Latar Belakang & Ringkasan Kebutuhan: Sistem backend pengangkut beroperasi di balik infrastruktur multi-layer reverse proxy gateway (Kubernetes Ingress, API Gateway, WAF/Load Balancer). Pencatatan alamat IP client sebelumnya belum dikonfigurasi secara dinamis pada Express trust proxy sehingga rentan mencatat IP internal gateway atau rentan terhadap spoofing header jika membaca X-Real-IP/X-Forwarded-For mentah secara langsung. Dilakukan perbaikan dengan menyediakan parser TRUST_PROXY (mendukung jumlah hop, boolean, atau CIDR), pengamanan resolver getClientIp berbasis req.ip Express, integrasi audit log, serta normalisasi fallback teks nama kementerian (Kemenhub & Kemenkes) pada query draft tagihan billing menjadi format title case.

  • Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):

    • [x] Pembuatan helper parseTrustProxy dan getClientIp pada app/utils/ip-resolver.js
    • [x] Pendaftaran variabel TRUST_PROXY pada validasi schema env (app/utils/env-validator.js), bootstrap server (server.js), dan .env.example
    • [x] Integrasi extractor getClientIp pada HTTP request/response logger (app/middlewares/http_logger.js) dan audit log database (app/middlewares/log.js)
    • [x] Penambahan telemetri diagnostik proxy_debug kondisional khusus environment non-production (dev dan staging)
    • [x] Normalisasi penulisan literal fallback kementerian kode 022 dan 024 pada app/models/billing_models.js
    • [x] Pembuatan unit test komprehensif pada tests/utils/ip-resolver.test.js dan verifikasi regresi tests/services/inquiry-billing.service.test.js

2. Deployment Lifecycle ​

#EnvironmentTanggal & WaktuPICStatusCatatan
1Development2026-10-12 18:34Hermansyah EfendiDEPLOYEDBranch: development
2Staging (UAT)2026-10-12 21:55achmad fauziDEPLOYEDRelease Candidate v1.18.6 via MR !2085. Co-deployed: [TASK-TRUST-PROXY-IP-RESOLVER-AND-BILLING-FALLBACK, ND-274-LNSW.2-2026, ND-374-LNSW.2-2026, TASK-FIX-COMMENTED-BIND-PARAMS-V1.18.4, TASK-UNIFY-DB-POOL-AND-HEALTH-OBSERVABILITY, TASK-DRAGONFLYDB-MIGRATION, TASK-SPLIT-BILLING-202608, FEAT-MANUAL-BILLING-REQUEST, MAINT-SUPERSET-SUMMARY-SHIP-SYNC-SECURITY, REFACTOR-DEPRECATE-POST-STATUS-SERVICE-MIGRATION, TASK-PINO-LOGGER-REFACTOR, ND-304-LNSW.34-2026, ND-256-LNSW.34-2026, ND-183-LNSW.34-2026]
3Production——PENDING—

Riwayat Update Log ​

TanggalEnvironmentPerubahanPIC
2026-10-12StagingPromosi ke STAGING via MR !2085 (Release Candidate v1.18.6)Hermansyah Efendi
2026-10-12DevelopmentInisialisasi draft audit log dari context block backend (commit b73b91f9 s.d. 04b8d93c)Hermansyah Efendi

3. Analisis Teknis & Rincian Perubahan ​

  • Alur Logika & Bisnis:

    1. Dynamic Trust Proxy (parseTrustProxy): Express memerlukan tipe data Number untuk konfigurasi hop count (jika di-passing String, library proxy-addr menganggapnya subnet yang tidak valid). Helper mengonversi string integer menjadi Number, mempertahankan string format subnet/CIDR, dan menangani nilai boolean dengan fallback default 1.
    2. Anti-Spoofing IP Resolution (getClientIp): Mengandalkan req.ip yang dihitung oleh Express melalui algoritma traversal right-to-left terpercaya. Header buatan klien (seperti X-Real-IP palsu atau injeksi pada segmen kiri X-Forwarded-For) diabaikan secara aman, serta menghapus prefix IPv6-mapped IPv4 ::ffff:.
    3. Audit Trail Alignment: Kolom client pada tabel td_log dan log JSON Pino pada httpLogger diseragamkan menggunakan getClientIp(req).
    4. Draft Billing Fallback Standardization: Mengubah literal string fallback kode KL 022 dan 024 dari huruf kapital penuh (KEMENTERIAN PERHUBUNGAN, KEMENTERIAN KESEHATAN) menjadi title case (Kementerian Perhubungan, Kementerian Kesehatan).
  • Perubahan Database (DDL / DML):

    sql
    -- Tidak ada perubahan skema database (DDL/DML) untuk task ini.
    -- Penyesuaian murni pada layer konfigurasi Express trust proxy, middleware audit log, dan query runtime model billing.
  • Kontrak API & Response Payload: Tidak ada penambahan endpoint baru. Format log HTTP (httpLogger) dan field respons billing disesuaikan:

    GET /inquiry-billing ​

    • Deskripsi: Menampilkan rincian data tagihan pengangkut dengan fallback nama KL berformat title case.
    • Autentikasi / Guard: Bearer Token (doIntrospection, checkAccessAgentByServiceNumber)
    • Request Query: serviceNumber
    • Response Payload (200 OK - Array dataDraftTagihan):
      json
      {
        "status": 200,
        "message": "Data billing ditemukan",
        "data": {
          "dataDraftTagihan": [
            {
              "id_tagihan_hdr": "HDR001",
              "id_tagihan_dtl": "DTL001",
              "trx_id_insw": "1010001",
              "uraian_kl": "Kementerian Perhubungan",
              "status_tagihan": "ACTIVE"
            }
          ]
        }
      }

4. Bukti Teknis (Technical Proof & Audit Trail) ​

Daftar Commit Terkait (Multi-Commit Trail) ​

Commit IDBranchAuthorTanggal & JamRingkasan Perubahan
04b8d93c8ea0a813f0262f7cd4ddcffa8afd9cafdevelopmentHermansyah Efendi2026-10-12 18:34fix(billing): normalize fallback ministry names in draft billing
884a719908c5e31dba7f8994728e541a0787a0cedevelopmentHermansyah Efendi2026-10-07 16:54feat(proxy): support dynamic hop count and cidr for express trust proxy
813ab2c21777e8cf0a79479d1a6e1f8496949f53developmentHermansyah Efendi2026-10-07 15:47feat(logger): conditionally attach proxy debug telemetry in dev and staging
45be17aec47ae000f51efc956376ddcadf7125a5developmentHermansyah Efendi2026-10-05 22:50refactor(logger): remove temporary proxy debug telemetry
b73b91f9c4f1f375762a7dbc1e9af63bb8e7cfbfdevelopmentHermansyah Efendi2026-10-05 16:43fix(logger): resolve real client ip behind reverse proxy gateway

Cuplikan Kode / Implementasi Inti ​

javascript
// app/utils/ip-resolver.js
export const parseTrustProxy = (value, defaultValue = 1) => {
  if (value === undefined || value === null) return defaultValue;
  if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? Math.floor(value) : defaultValue;
  if (typeof value === "boolean") return value;

  if (typeof value === "string") {
    const trimmed = value.trim();
    if (!trimmed) return defaultValue;
    if (trimmed.toLowerCase() === "true") return true;
    if (trimmed.toLowerCase() === "false") return false;
    if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10);
    return trimmed;
  }
  return defaultValue;
};

export const getClientIp = (req) => {
  if (!req) return "";
  const ip = req.ip || req.socket?.remoteAddress || req.connection?.remoteAddress || "";
  if (typeof ip === "string" && ip.startsWith("::ffff:")) {
    return ip.slice(7);
  }
  return typeof ip === "string" ? ip.trim() : "";
};
javascript
// app/models/billing_models.js
// Penyesuaian title case fallback nama KL pada query dataDraftTagihan:
COALESCE(
  c.uraian_kl, 
  case 
    when b.kode_kl = '022' then 'Kementerian Perhubungan' 
    when b.kode_kl = '024' then 'Kementerian Kesehatan' 
    else b.kode_kl 
  end
) as uraian_kl,

5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification) ​

A. Verifikasi Unit Test (Development Stage) ​

  • Test Suite File:
    • tests/utils/ip-resolver.test.js
    • tests/services/inquiry-billing.service.test.js
  • Status Eksekusi CLI: PASSED (21 tests passed)
Cuplikan Kode Unit Test ​
javascript
describe("parseTrustProxy helper", () => {
  it("converts integer string to Number for Express hop count", () => {
    expect(parseTrustProxy("5")).toBe(5);
    expect(parseTrustProxy("1")).toBe(1);
    expect(parseTrustProxy("0")).toBe(0);
  });
  it("returns trimmed CIDR and subnet lists as string", () => {
    const cidr = "10.0.0.0/8, 192.168.0.0/16, loopback";
    expect(parseTrustProxy(cidr)).toBe(cidr);
  });
});

describe("getClientIp resolver", () => {
  it("relies securely on req.ip computed by Express trust proxy", () => {
    const req = {
      ip: "203.0.113.195",
      headers: {
        "x-real-ip": "1.2.3.4",
        "x-forwarded-for": "1.2.3.4, 203.0.113.195",
      },
    };
    expect(getClientIp(req)).toBe("203.0.113.195");
  });
});
Output Raw CLI Runner ​
text
 RUN  v4.1.11 D:/WORK/LNSW/pengangkut-backend

 ✓ tests/utils/ip-resolver.test.js (13 tests) 8ms
 ✓ tests/services/inquiry-billing.service.test.js (8 tests) 15ms

 Test Files  2 passed (2)
      Tests  21 passed (21)
   Start at  20:49:50
   Duration  343ms (transform 109ms, setup 39ms, import 132ms, tests 23ms, environment 0ms)

B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook) ​

  • Cakupan / Surface Pengujian: API / Postman Only & Full E2E (UI + Backend)
  • Kebutuhan Akses / Role Akun: Token bearer valid untuk endpoint berproteksi atau akses endpoint public untuk validasi log HTTP.
  • Hasil Pengujian UAT: PENDING
  • Catatan QA: —
Skenario Pengujian QA ​
  1. Skenario 1 — Resolusi IP Client dengan Multi-Hop Proxy (Happy Path):

    • Pre-kondisi: Konfigurasi environment TRUST_PROXY=2.
    • Trigger / Request: Kirim HTTP request dengan header X-Forwarded-For: 203.0.113.50, 10.244.0.1 (di mana 10.244.0.1 adalah proxy hop terakhir).
    • Ekspektasi Response: HTTP respon normal, pada log console / Loki tercatat field ip: "203.0.113.50".
    • Verifikasi Database / Log: Kolom client pada tabel audit td_log memuat nilai 203.0.113.50.
  2. Skenario 2 — Pencegahan Spoofing Header IP (Security / Edge Case):

    • Pre-kondisi: Request dikirim dengan menyertakan header palsu X-Real-IP: 8.8.8.8 dan X-Forwarded-For: 1.1.1.1, 203.0.113.195.
    • Trigger / Request: Akses endpoint API.
    • Ekspektasi Response: Header X-Real-IP palsu diabaikan oleh resolver; sistem mencatat IP sesuai kalkulasi hop Express (203.0.113.195).
    • Verifikasi Database / Log: Log incoming request tidak terpengaruh injeksi header 8.8.8.8.
  3. Skenario 3 — Format Title Case Fallback Billing (Happy Path):

    • Pre-kondisi: Terdapat tagihan aktif dengan kode KL 022 atau 024 yang memiliki uraian_kl NULL.
    • Trigger / Request: GET /inquiry-billing?serviceNumber={serviceNumber}.
    • Ekspektasi Response: Field uraian_kl bernilai "Kementerian Perhubungan" atau "Kementerian Kesehatan".
    • Verifikasi Database / Log: Tampilan UI billing tidak lagi menampilkan huruf kapital penuh.

Single Source of Truth (SSOT) Arsitektur & Perubahan Teknis Backend