Tunnel SSTP MikroTik: Jalur Cadangan Router di Balik CGNAT plus Alert WhatsApp

Tunnel SSTP MikroTik tembus CGNAT lewat TCP 443 sebagai jalur cadangan L2TP dengan alert WhatsApp

Bayangkan Anda setiap hari lewat jalan tol ke kantor. Suatu pagi petugas menutup gerbang tol tanpa pemberitahuan, padahal jalan raya biasa di sebelahnya tetap lancar. Kalau Anda tidak pernah menyiapkan rute cadangan, Anda hanya bisa menunggu di depan palang. Begitulah nasib router yang memakai tunnel L2TP di balik CGNAT. Oleh karena itu, kami memasang tunnel SSTP MikroTik lewat TCP 443 sebagai jalan raya cadangan, lengkap dengan perpindahan otomatis dan alert WhatsApp.

Tutorial ini berasal dari gangguan nyata di jaringan ISP kecil yang kami kelola. Beberapa router MikroTik di lokasi pelanggan terhubung ke server billing dan RADIUS lewat CHR (Cloud Hosted Router) yang punya IP publik. Router-router itu memakai Starlink dan ISP rumahan, sehingga semuanya berada di balik CGNAT. Selain itu, semua perintah di artikel ini sudah berjalan di produksi atau sudah kami uji di VPS uji. Namun, kami mengganti IP, nama user, dan nama lokasi dengan nilai contoh.

Arsitektur tunnel SSTP MikroTik lewat TCP 443 sebagai cadangan L2TP yang diblokir CGNAT, menuju CHR dan server RADIUS
L2TP tetap jalur utama. SSTP menunggu dalam keadaan nonaktif dan baru menyala saat L2TP gagal.

Kenapa L2TP Gagal di Balik CGNAT?

CGNAT (Carrier-Grade NAT) berarti operator menaruh ratusan pelanggan di belakang satu IP publik. Router Anda tidak punya IP publik sendiri, dan semua koneksi keluar melewati NAT milik operator. Ciri paling mudah: hop kedua traceroute berupa alamat 100.64.x.x.

Masalahnya, NAT operator memperlakukan UDP dengan sangat pelit. Sesi UDP cepat kedaluwarsa, dan pada beberapa segmen IP publik balasan UDP bahkan tidak pernah kembali. Padahal L2TP/IPsec, WireGuard, dan OpenVPN mode UDP bergantung penuh pada UDP. Sebaliknya, TCP 443 adalah port HTTPS yang menjadi tumpuan semua situs web, sehingga operator hampir tidak mungkin mengganggunya.

Protokol tunnelTransportNasib di balik CGNAT
L2TP/IPsecUDP 500, 4500, 1701Sering gagal, tergantung segmen IP operator
WireGuardUDP (port bebas)Rawan putus saat sesi UDP kedaluwarsa
OpenVPN (UDP)UDP 1194Sama rawannya dengan WireGuard
SSTPTCP 443 (TLS)Hampir selalu lolos, mirip trafik HTTPS biasa

Dengan demikian, SSTP menjadi pilihan cadangan yang paling masuk akal. RouterOS v6 dan v7 sama-sama punya SSTP client dan server bawaan, jadi Anda tidak perlu memasang paket tambahan.

Contoh Kasus: Ping Lolos, Tunnel Tetap Mati

Pada 3 September 2026, tunnel L2TP di dua lokasi kami tidak bisa naik selama berjam-jam. Anehnya, router tetap hidup dan internet pelanggan normal. Ping ke CHR pun lolos 10 dari 10 dengan latensi sekitar 50 ms. Namun, log router menunjukkan siklus connecting lalu terminating setiap 30 detik.

Diagnosa CGNAT: ping ke CHR lolos, traceroute hop 100.64.0.1, dan log IPsec L2TP gagal sebelum memakai tunnel SSTP MikroTik
Tangkapan asli dari router lokasi B saat gangguan, IP CHR dan user disensor.

Tiga petunjuk mengunci diagnosisnya. Pertama, ICMP dan TCP lolos sempurna. Kedua, hop kedua traceroute adalah 100.64.0.1, tanda CGNAT. Ketiga, SPI responder di log IPsec selalu 0000000000000000, artinya balasan CHR tidak pernah sampai. Bahkan setelah kami mematikan IPsec, L2TP tetap gagal. Jadi masalahnya ada di UDP, bukan di enkripsi.

Rancangan Tunnel SSTP MikroTik sebagai Cadangan

Sebelum mengetik perintah, pahami dulu satu keputusan desain yang membuat semuanya sederhana: SSTP memakai PPP secret yang sama dengan L2TP. Karena secret menentukan remote-address, router selalu mendapat IP tunnel yang sama, misalnya 10.9.0.7, entah lewat L2TP maupun SSTP.

Akibatnya, semua yang sudah bergantung pada IP itu tetap berjalan tanpa perubahan. Contohnya dst-nat untuk remote modem pelanggan, NAS di RADIUS, dan route dari server billing. Selanjutnya, kita cukup menyiapkan empat bagian berikut.

BagianLokasiFungsi
SSTP server + sertifikatCHRMenerima tunnel di TCP 443
SSTP client (nonaktif)Router lokasiJalur cadangan yang siap menyala
Route RADIUS sepasangRouter lokasiPaket RADIUS tetap masuk tunnel saat failover
Script + schedulerRouter lokasiMemindah jalur otomatis setelah 3 kali gagal

Langkah 1: Siapkan SSTP Server di CHR

SSTP berjalan di atas TLS, jadi CHR butuh sertifikat. Kita tidak perlu membeli sertifikat karena router client tidak memverifikasinya. Meski begitu, Anda tetap harus menandatangani sertifikat dengan benar.

Buat CA Lalu Sertifikat Server

/certificate
add name=sstp-ca common-name=sstp-ca key-size=2048 days-valid=3650 key-usage=key-cert-sign,crl-sign
sign sstp-ca
add name=sstp-server common-name=203.0.113.7 key-size=2048 days-valid=3650 key-usage=digital-signature,key-encipherment,tls-server
sign sstp-server ca=sstp-ca
print

Urutannya penting. Pertama, kita membuat CA dengan key-usage=key-cert-sign,crl-sign, karena hanya sertifikat jenis ini yang boleh menandatangani dirinya sendiri. Kemudian CA tersebut menandatangani sertifikat server ber-tls-server. Kami sempat mencoba langsung sign sertifikat server tanpa CA, dan RouterOS menolaknya dengan failure: CA not found. Proses sign butuh 10–30 detik, jadi tunggu sampai print menampilkan flag K (punya private key). Di CHR kami, kedua sertifikat berlaku sampai Agustus 2036.

Aktifkan SSTP Server dan Longgarkan PPP Secret

/interface sstp-server server
set enabled=yes port=443 certificate=sstp-server default-profile=billing authentication=mschap2,mschap1 verify-client-certificate=no

/ppp secret
set [find name=router-lokasi-a] service=any
print where name=router-lokasi-a

Profile billing adalah profile milik L2TP, dengan local-address=10.9.0.1. Bagian paling sering terlupa adalah service=any. Panel atau script biasanya membuat secret L2TP dengan service=l2tp, sehingga CHR menolak login yang datang lewat SSTP. Setelah kami mengubahnya, dua protokol bisa memakai secret yang sama.

Selain itu, pastikan port 443 di CHR benar-benar bebas. Matikan service www-ssl di /ip service dan periksa tidak ada dst-nat yang memakai port 443.

Langkah 2: Pasang SSTP Client di Router Lokasi

Di router lokasi, kita membuat SSTP client dengan user dan password yang sama persis dengan L2TP client. Namun, client ini lahir dalam keadaan nonaktif karena tugasnya hanya menunggu.

RouterOS v6

/interface sstp-client
add name=sstp-billing connect-to=203.0.113.7:443 user=router-lokasi-a password=RAHASIA profile=default verify-server-certificate=no add-default-route=no disabled=yes

RouterOS v7

/interface sstp-client
add name=sstp-billing connect-to=203.0.113.7 port=443 user=router-lokasi-a password=RAHASIA profile=default verify-server-certificate=no add-default-route=no disabled=yes

Perhatikan bedanya. RouterOS v6 menggabungkan IP dan port dalam connect-to. Sebaliknya, v7 menolak format itu dengan pesan failure: bad address or dns name dan meminta parameter port terpisah. Router hEX kami masih v6.49, sedangkan RB5009 sudah v7.24, jadi script pemasang kami mendeteksi versi lebih dulu.

Opsi add-default-route=no mencegah seluruh internet pelanggan ikut masuk tunnel. Kemudian verify-server-certificate=no membuat client menerima sertifikat buatan sendiri. Trafik tetap terenkripsi TLS, tetapi client tidak memastikan identitas server. Jika Anda ingin lebih ketat, impor sertifikat CA ke router lalu aktifkan verifikasi.

Langkah 3: Pasang Route RADIUS Sepasang

Klaim “IP tunnel tetap sama sehingga semua route tetap berlaku” ternyata hanya benar untuk route yang memakai IP. Route yang gateway-nya berupa nama interface ikut mati bersama interface tersebut.

/ip route
add dst-address=198.51.100.20/32 gateway=l2tp-billing distance=1 comment="Route RADIUS via CHR"
add dst-address=198.51.100.20/32 gateway=sstp-billing distance=2 comment="Route RADIUS via CHR (SSTP)"

Alamat 198.51.100.20 adalah server RADIUS. Dua route ini identik kecuali gateway dan distance. Saat L2TP hidup, route distance 1 aktif. Begitu script mematikan L2TP, route itu menjadi unreachable dan route SSTP distance 2 otomatis mengambil alih. Hasilnya failover dua arah tanpa ECMP dan tanpa script tambahan.

Contoh Kasus: Pelanggan Gagal Login Setelah Failover

Pada 7 September 2026, salah satu router sudah berjalan di SSTP, tetapi pelanggan yang modemnya restart tidak bisa login PPPoE. Log router penuh dengan authentication failed - radius timeout. Ternyata router hanya punya route RADIUS lewat l2tp-billing. Karena interface itu mati, paket RADIUS jatuh ke default route, keluar lewat Starlink, dan tiba di FreeRADIUS dengan IP publik CGNAT.

FreeRADIUS lalu membuang paket itu karena IP tersebut bukan NAS terdaftar. Log server mencatat ribuan baris Ignoring request from unknown client, sementara router hanya melihat timeout. Setelah kami menambah route SSTP di atas, login pelanggan kembali normal.

Langkah 4: Script Failover Otomatis

Sekarang kita butuh “satpam” kecil di router yang memeriksa tunnel setiap 2 menit. Buka Winbox, masuk ke System → Scripts, lalu buat script bernama tbradius-failover-tunnel dengan policy read, write, policy, test. Kami memecah isinya menjadi dua bagian agar mudah Anda pahami. Simpan keduanya berurutan dalam satu script.

Bagian 1: Hitung Kegagalan Berturut-turut

:global tbrGagal
:if ([:typeof $tbrGagal] = "nothing") do={ :set tbrGagal 0 }
:local l2tpId [/interface l2tp-client find name~"billing"]
:local sstpId [/interface sstp-client find name="sstp-billing"]
:if ([:len $l2tpId] > 0 and [:len $sstpId] > 0) do={
  :local lOn ([/interface l2tp-client get $l2tpId disabled] = false)
  :local lRun [/interface l2tp-client get $l2tpId running]
  :local sOn ([/interface sstp-client get $sstpId disabled] = false)
  :local sRun [/interface sstp-client get $sstpId running]
  :if (($lOn and $lRun) or ($sOn and $sRun)) do={
    :set tbrGagal 0
  } else={
    :set tbrGagal ($tbrGagal + 1)

Variabel global tbrGagal menyimpan jumlah kegagalan berturut-turut di antara dua eksekusi. Script menganggap kondisi sehat bila salah satu tunnel enabled sekaligus running. Jika sehat, penghitung kembali ke nol. Jika tidak, penghitung bertambah satu. Filter name~"billing" sengaja sempit agar L2TP lain milik pihak ketiga di router yang sama tidak ikut tersentuh.

Bagian 2: Pindah Jalur Setelah 3 Kali Gagal

    :if ($tbrGagal >= 3) do={
      :set tbrGagal 0
      :if ($lOn) do={
        /interface l2tp-client disable $l2tpId
        /interface sstp-client enable $sstpId
        :log warning "tbradius: L2TP gagal 6 menit, pindah ke SSTP 443"
      } else={
        /interface sstp-client disable $sstpId
        /interface l2tp-client enable $l2tpId
        :log warning "tbradius: SSTP gagal 6 menit, kembali ke L2TP"
      }
    }
  }
}

Setelah 3 kali gagal (sekitar 6 menit), script mematikan tunnel yang sedang aktif dan menyalakan tunnel lainnya. Logikanya dua arah. Jadi, kalau SSTP juga gagal, 6 menit kemudian router kembali mencoba L2TP. Dengan begitu, router tidak pernah terjebak di satu jalur yang mati. Ambang 3 kali juga mencegah perpindahan berlebihan saat koneksi hanya tersendat sebentar.

Jadwalkan Setiap 2 Menit

/system scheduler
add name=tbradius-failover-cek interval=2m on-event=tbradius-failover-tunnel policy=read,write,policy,test comment="Failover L2TP <-> SSTP"

Scheduler ini menjalankan script setiap 2 menit. Samakan policy scheduler dengan policy script, karena scheduler menjalankan script memakai haknya sendiri. Tanpa hak write, script tidak bisa mengubah interface.

Memeriksa Status Failover

Kami menulis script PHP kecil di server billing yang membaca status tunnel SSTP MikroTik ini lewat RouterOS API. Anda juga bisa melihatnya langsung di terminal router dengan /interface print dan /ip route print where dst-address=198.51.100.20/32.

Status tunnel SSTP MikroTik aktif di router RouterOS v6 dan v7 dengan route RADIUS sepasang dan scheduler failover
Tangkapan asli 3 November 2026: dua router produksi sedang berjalan di SSTP. Nama lokasi, IP, dan user disensor.

Perhatikan dua hal. Pertama, route RADIUS yang aktif selalu mengikuti tunnel yang hidup, baik di v6 (active=true) maupun v7. Kedua, log CHR mencatat IP tunnel 10.9.0.7, sama seperti saat memakai L2TP. Namun, log yang sama juga memperlihatkan connection timeout sesekali. Artinya SSTP pun tidak kebal gangguan, sehingga kita tetap butuh alarm.

Alert WhatsApp Saat Router Offline

Script failover pada tunnel SSTP MikroTik menangani masalah kecil secara otomatis. Sebaliknya, admin tetap perlu tahu bila kedua jalur gagal. Untuk itu, server memeriksa setiap router lewat port API setiap 5 menit, lalu mengirim WhatsApp lewat gateway Fonnte.

Alur waktu failover tunnel SSTP MikroTik setelah 6 menit dan alert WhatsApp setelah 10 menit offline
Ambang alert 10 menit sengaja lebih lama dari waktu failover agar WA tidak berbunyi saat router memulihkan diri.

Versi produksi kami tertanam di aplikasi billing CodeIgniter. Untuk tutorial ini, kami menulis ulang logikanya menjadi satu file PHP mandiri. Kami memecahnya menjadi tiga bagian, dan Anda menyimpan semuanya berurutan dalam monitor-router.php.

Bagian 1: Daftar Router dan Konfigurasi

<?php
// monitor-router.php: cek router lewat port API, kirim WA via Fonnte
date_default_timezone_set('Asia/Jakarta');
$routers = [
    'Router Kantor' => ['10.9.0.7', 8728],
    'Router Gudang' => ['10.9.0.8', 8728],
];
$ambang  = 10;                       // menit offline sebelum alert
$nomor   = '6281234567890';          // nomor WA admin
$token   = getenv('FONNTE_TOKEN') ?: 'ISI_TOKEN_FONNTE';
$apiUrl  = getenv('FONNTE_URL') ?: 'https://api.fonnte.com/send';
$fileLog = __DIR__ . '/router-status.json';

$status = is_file($fileLog) ? json_decode(file_get_contents($fileLog), true) : [];

Server monitor sebaiknya berada di dalam jaringan tunnel, jadi ia menjangkau router lewat IP 10.9.0.x. Kalau router lewat L2TP maupun SSTP, IP-nya tetap sama, sehingga daftar ini tidak perlu berubah. Token dan URL API bisa Anda timpa lewat environment variable. Kemudian file router-status.json menyimpan kapan router terakhir terlihat dan kapan alert terakhir terkirim.

Bagian 2: Fungsi Cek Port dan Kirim WA

function router_hidup(string $ip, int $port): bool
{
    $sock = @fsockopen($ip, $port, $errno, $errstr, 4);
    if (!$sock) return false;
    fclose($sock);
    return true;
}

function durasi(int $detik): string
{
    $jam   = intdiv($detik, 3600);
    $menit = intdiv($detik % 3600, 60);
    return ($jam > 0 ? "$jam jam " : '') . "$menit menit";
}

function kirim_wa(string $url, string $token, string $nomor, string $pesan): bool
{
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => ['target' => $nomor, 'message' => $pesan, 'countryCode' => '62'],
        CURLOPT_HTTPHEADER     => ['Authorization: ' . $token],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 15,
    ]);
    $hasil = json_decode((string) curl_exec($ch), true);
    curl_close($ch);
    return !empty($hasil['status']);
}

Fungsi router_hidup() cukup membuka koneksi TCP ke port API dengan batas 4 detik. Port yang menjawab berarti tunnel hidup dan router merespons. Selanjutnya, kirim_wa() memanggil endpoint /send Fonnte dengan header Authorization berisi token. Fonnte membalas JSON berisi status true atau false.

Bagian 3: Logika Alert dan Pulih

$now = time();
foreach ($routers as $nama => [$ip, $port]) {
    $s = $status[$nama] ?? ['terakhir' => $now, 'alert' => null];

    if (router_hidup($ip, $port)) {
        if ($s['alert']) {
            $pesan = "✅ *ROUTER PULIH*\n\nRouter *$nama* sudah terhubung kembali.\n"
                   . "Total tidak terhubung: *" . durasi($now - $s['terakhir']) . "*";
            $ok = kirim_wa($apiUrl, $token, $nomor, $pesan);
            echo date('H:i') . " $nama PULIH, WA " . ($ok ? 'terkirim' : 'GAGAL') . "\n";
        }
        $s = ['terakhir' => $now, 'alert' => null];
    } elseif (!$s['alert'] && $now - $s['terakhir'] >= $ambang * 60) {
        $pesan = "⚠️ *ROUTER OFFLINE*\n\nRouter *$nama* tidak terhubung selama *"
               . durasi($now - $s['terakhir']) . "*.\nTerakhir terlihat: " . date('d/m/Y H:i', $s['terakhir']);
        $ok = kirim_wa($apiUrl, $token, $nomor, $pesan);
        $s['alert'] = $now;          // tetap ditandai walau WA gagal
        echo date('H:i') . " $nama OFFLINE, WA " . ($ok ? 'terkirim' : 'GAGAL') . "\n";
    }
    $status[$nama] = $s;
}
file_put_contents($fileLog, json_encode($status, JSON_PRETTY_PRINT));

Penanda alert mencegah pesan berulang setiap 5 menit. Saat router terlihat lagi, script mengirim kabar pulih lalu menghapus penanda. Perhatikan juga bahwa script tetap memasang penanda walaupun WA gagal. Dengan cara ini, gangguan gateway WA tidak berubah menjadi banjir pesan ke nomor yang sama.

Jalankan Lewat Cron dan Uji dengan Gateway Tiruan

# /etc/cron.d/monitor-router
*/5 * * * * www-data php /opt/monitor-router/monitor-router.php >> /var/log/monitor-router.log 2>&1

Pastikan user www-data boleh menulis ke folder /opt/monitor-router. Sebelum memakai token asli, uji dulu dengan server tiruan. Kami mengarahkan FONNTE_URL ke php -S 127.0.0.1:8999 yang hanya mencatat request, lalu memundurkan waktu “terakhir terlihat” 11 menit.

Uji monitor router alert WhatsApp untuk tunnel SSTP MikroTik: pesan offline dan pulih ditangkap gateway tiruan
Tangkapan asli dari VPS uji: alert offline terkirim sekali, tidak berulang, lalu pesan pulih menyusul.

Contoh Kasus: Dua Bulan Alarm di Produksi

Sejak 3 September sampai 2 November 2026, sistem alert produksi kami mengirim 127 pesan “router offline” dan 120 pesan “pulih”. Setelah alert terkirim, 71 dari 120 gangguan pulih dalam 30 menit berikutnya, dengan median 19 menit. Namun, 10 gangguan berlangsung lebih dari 3 jam. Salah satunya router di lokasi lain yang baru pulih 6 jam 29 menit setelah alert pada 29 Oktober. Router itu belum punya scheduler failover, sehingga SSTP tidak pernah menyala.

Kasus lain terjadi pada 6 Oktober. Jalur Starlink ke CHR rusak dua arah, sehingga L2TP dan SSTP sama-sama gagal selama lebih dari 4 jam. Script tetap berpindah bolak-balik setiap 6 menit dan akhirnya tersambung sendiri saat jalur pulih. Masalahnya, seseorang sempat me-reboot MikroTik. Akibatnya, semua sesi PPPoE lepas dan router mencatat 324 kali radius timeout. Sejak itu aturan kami jelas: saat tunnel putus, restart perangkat uplink, jangan restart MikroTik.

Kesalahan Umum

  • Lupa service=any di PPP secret. CHR menolak login SSTP karena secret hanya mengizinkan L2TP.
  • Memakai format connect-to v6 di RouterOS v7. RouterOS v7 butuh IP dan port=443 terpisah.
  • Menandatangani sertifikat server tanpa CA. Hasilnya failure: CA not found. Buat CA dulu, lalu sign ... ca=sstp-ca.
  • Route RADIUS hanya lewat nama interface L2TP. Begitu failover, RADIUS lolos ke WAN dan pelanggan gagal login. Pasang route kembarannya lewat SSTP.
  • Menguji failover lewat tunnel itu sendiri. Saat Anda mematikan L2TP dari sesi yang lewat L2TP, koneksi Anda ikut putus. Kami pernah meninggalkan router dengan kedua interface nonaktif karena hal ini. Uji lewat LAN atau jalur lain.
  • Device-mode menolak scheduler. RouterOS 7 versi baru bisa berjalan di mode=home dengan scheduler=no. Perintah pemasangan tampak berhasil, padahal scheduler tidak pernah ada. Periksa /system device-mode print.
  • Ambang alert lebih pendek dari waktu failover. WA akan berbunyi untuk gangguan yang sebenarnya sudah tertangani otomatis.

Kesimpulan

CGNAT tidak selalu memblokir UDP, tetapi saat itu terjadi, tunnel L2TP bisa mati berjam-jam tanpa pesan error yang jelas. Tunnel SSTP MikroTik di TCP 443 memberi jalur cadangan yang hampir selalu lolos. Kuncinya ada pada satu PPP secret untuk dua protokol, sehingga IP tunnel tidak berubah. Kemudian route RADIUS sepasang menjaga autentikasi pelanggan, dan script kecil memindah jalur otomatis setelah 6 menit.

Terakhir, failover tetap butuh pengawas. Monitor sederhana dengan ambang 10 menit dan alert WhatsApp memberi tahu Anda hanya ketika kedua jalur benar-benar gagal. Dengan demikian, Anda bisa langsung bertindak, misalnya me-restart perangkat uplink, sebelum pelanggan ramai menelepon.

Baca Juga

Referensi: MikroTik Documentation: SSTP, MikroTik Documentation: Certificates, MikroTik Documentation: Scripting, RFC 6598 (ruang alamat 100.64.0.0/10 untuk CGNAT), Fonnte: Sending API Messages.

Ali Akbar

Software Developer yang fokus mengembangkan aplikasi berbasis Web dan Desktop. Senang mempelajari teknologi baru terutama di bidang web design dan web development.

View all posts by Ali Akbar →

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *