Mikro ERP SQL Server'a Web'den Güvenli Bağlantı: Cloudflare Tunnel Rehberi

Ofisteki Mikro ERP SQL Server'a web uygulamanızdan nasıl bağlanırsınız? Port açmadan, VPN olmadan Cloudflare Tunnel ile güvenli bağlantı kurma adımları.

Sorun: ERP Ofiste, Uygulama Bulutta

Web uygulamanız Vercel’de veya Cloudflare’da çalışıyor. Ama ERP sisteminiz (Mikro, Logo, Netsis…) şirket ofisindeki sunucuda, SQL Server üzerinde.

Bu ikisi nasıl konuşacak?

YaklaşımSorun
SQL Server’ı internete aç🔴 Güvenlik felaketi
VPN bağlantısı🟡 Karmaşık, yavaş
Her gün Excel aktar🟡 Eski veri, hatalı

Çözüm: Cloudflare Tunnel

Cloudflare Tunnel, ofisteki sunucu ile bulut arasında şifreli bir tünel oluşturur. Sunucuda hiçbir port açmanız gerekmez.

Web Uygulaması → Cloudflare → Tünel → Ofisteki Proxy → SQL Server
(Bulut)          (Güvenlik)   (Şifreli)  (Node.js)      (Mikro ERP)

Güvenlik katmanları:

  1. Cloudflare Firewall — DDoS ve saldırı koruması
  2. API Key — Her istek doğrulanır
  3. Parametreli sorgular — SQL injection koruması
  4. Sadece SELECT — Veri değiştirme yasak

Nasıl Kurulur?

1. Sunucuya Cloudflare Tunnel Kurun

# cloudflared kurulumu
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
chmod +x cloudflared

# Tunnel oluştur
./cloudflared tunnel create erp-tunnel
./cloudflared tunnel route dns erp-tunnel erp-api.firma.com

Tunnel yapılandırması:

# config.yml
tunnel: TUNNEL_ID
ingress:
  - hostname: erp-api.firma.com
    service: http://localhost:3500
  - service: http_status:404

2. Node.js Proxy Sunucusu

SQL Server’a doğrudan bağlanmak yerine, arada bir proxy katmanı kullanıyoruz. Bu proxy sadece SELECT sorgularına izin verir:

const express = require('express');
const sql = require('mssql');
const app = express();

// API Key kontrolü
function apiKeyKontrolu(req, res, next) {
  const key = req.headers['x-api-key'];
  if (key !== process.env.API_KEY) {
    return res.status(401).json({ error: 'Yetkisiz' });
  }
  next();
}

// SQL Server bağlantısı
const pool = new sql.ConnectionPool({
  server: 'localhost',
  database: 'ERP_DB',
  user: process.env.DB_USER,
  password: process.env.DB_PASS,
  pool: { max: 10, min: 2 },
});
pool.connect();

// Sorgu endpoint'i
app.post('/query', apiKeyKontrolu, async (req, res) => {
  const { query, params } = req.body;

  // GÜVENLİK: Sadece SELECT
  if (!/^\s*SELECT/i.test(query)) {
    return res.status(403).json({ error: 'Sadece SELECT sorgularına izin var' });
  }

  try {
    const request = pool.request();
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        request.input(key, value);
      });
    }
    const result = await request.query(query);
    res.json(result.recordset);
  } catch (err) {
    res.status(500).json({ error: 'Sorgu başarısız' });
  }
});

app.listen(3500);

3. Web Uygulamasından API Çağrısı

async function erpSorgusu(sql, params) {
  const response = await fetch('https://erp-api.firma.com/query', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({ query: sql, params }),
  });
  return response.json();
}

// Kullanım
const cariler = await erpSorgusu(
  'SELECT cari_kodu, cari_adi FROM CARI_HESAPLAR WHERE cari_kodu LIKE @prefix',
  { prefix: 'MRK%' }
);

Karşılaştığımız Sorunlar

Uzun Süren Sorgular

Cari yaşlandırma gibi ağır sorgular 3-4 dakika sürebilir. HTTP bağlantısı kopar. Çözüm: İsteği kabul edip arka planda çalıştırma, frontend’den polling ile sonucu alma.

Çoklu Veritabanı

Her şubenin ayrı Mikro veritabanı var. Şube parametresine göre doğru veritabanına bağlanma gerekiyor.

Türkçe Tarih Sorunu

SET LANGUAGE Turkish tarih formatlarını bozuyor. Detaylı yazımıza bakın.

Güvenlik Kontrol Listesi

Üretime almadan önce:

  • API Key 64+ karakter ve rastgele mi?
  • Sadece SELECT’e izin veriliyor mu?
  • Parametreli sorgular kullanılıyor mu?
  • .env dosyası .gitignore’da mı?
  • Rate limiting aktif mi?

Bu mimariyle web uygulamanız ofisteki ERP verilerine güvenle erişir — hiç port açmadan, VPN kurmadan.


Bu yazı AstaFlow Case Study serisinin bir parçasıdır.