<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://xodhiambo.com/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xodhiambo.com/" rel="alternate" type="text/html" /><updated>2026-07-14T10:12:06+00:00</updated><id>https://xodhiambo.com/blog/feed.xml</id><title type="html">Xavier Odhiambo</title><subtitle>Senior Backend &amp; Platform Engineer in Nairobi. Python, AWS, Ansible, AI/LLM. Most recently a Senior Software Engineer at Search Atlas; previously three years at Code for Africa building civic-tech data platforms.</subtitle><author><name>Xavier Odhiambo</name><email>xavierfrank4@gmail.com</email></author><entry><title type="html">NordVPN on MikroTik with IKEv2: A Complete RouterOS 6.49 Setup Guide</title><link href="https://xodhiambo.com/blog/nordvpn-mikrotik-ikev2-setup/" rel="alternate" type="text/html" title="NordVPN on MikroTik with IKEv2: A Complete RouterOS 6.49 Setup Guide" /><published>2026-07-13T00:00:00+00:00</published><updated>2026-07-13T00:00:00+00:00</updated><id>https://xodhiambo.com/blog/nordvpn-mikrotik-ikev2-setup</id><content type="html" xml:base="https://xodhiambo.com/blog/nordvpn-mikrotik-ikev2-setup/"><![CDATA[<p>I recently put my home network behind a MikroTik router running RouterOS <strong>6.49</strong>, with all
LAN traffic exiting through NordVPN over IKEv2/IPsec. This is the full setup, starting from a
router freshly <strong>reset to defaults (defconf)</strong>. Because defconf already builds the bridge,
LAN/WAN interface lists, DHCP, NAT, and a sane firewall, most of the work is
<strong>verify + a few small changes</strong> — nothing gets recreated from scratch.</p>

<p>Three phases:</p>

<ol>
  <li><strong>Phase 1</strong> – Verify the default config, then apply 3 deltas (DNS, FastTrack, WiFi).</li>
  <li><strong>Phase 2</strong> – NordVPN (IKEv2/IPsec) so <em>all</em> LAN traffic exits through the VPN.</li>
  <li><strong>Phase 3</strong> – Switch the ISP box to <strong>bridge mode</strong> + authenticate with <strong>PPPoE</strong>.</li>
</ol>

<blockquote>
  <p><strong>Assumptions in this guide</strong></p>
  <ul>
    <li>VPN server: <strong><code class="language-plaintext highlighter-rouge">uk2156.nordvpn.com</code></strong> — swap in any hostname from NordVPN’s server picker</li>
    <li>LAN subnet (defconf default): <strong>192.168.88.0/24</strong>, router at <strong>192.168.88.1</strong></li>
    <li>WAN = <code class="language-plaintext highlighter-rouge">ether1</code> · LAN = <code class="language-plaintext highlighter-rouge">bridge</code> (ether2–5 + wlan)</li>
    <li>Do Phase 2 <strong>while internet works</strong> so the router can fetch the certificate.</li>
    <li>Keep a <strong>Winbox</strong> session open (connects via MAC) so a bad firewall edit can’t lock you out.</li>
  </ul>
</blockquote>

<hr />

<h2 id="️-three-gotchas-that-silently-break-this-exact-setup">⚠️ Three gotchas that silently break this exact setup</h2>

<ul>
  <li><strong>FastTrack bypasses IPsec</strong> → fixed in Delta 2 below (defconf ships a FastTrack rule).</li>
  <li><strong>DNS leaks</strong> → fixed in Delta 1 (+ Phase 2.7).</li>
  <li><strong>No kill switch by default</strong> → optional rule in Phase 2.8.</li>
</ul>

<hr />

<h2 id="phase-1--verify-defconf-then-apply-3-deltas">Phase 1 — Verify defconf, then apply 3 deltas</h2>

<h3 id="1a-verify-what-defconf-already-created">1A. Verify what defconf already created</h3>

<p>All of these ship with the default configuration — verify them, but create <strong>nothing</strong> here;
do not re-add.</p>

<table>
  <thead>
    <tr>
      <th>What</th>
      <th>Expected value</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Interface lists</td>
      <td><code class="language-plaintext highlighter-rouge">WAN</code>, <code class="language-plaintext highlighter-rouge">LAN</code> (defconf)</td>
      <td>✅ leave as-is</td>
    </tr>
    <tr>
      <td>Bridge ports</td>
      <td>ether2–5 <strong>+ wlan1 + wlan2</strong> on <code class="language-plaintext highlighter-rouge">bridge</code>; ether1 outside</td>
      <td>✅ leave as-is</td>
    </tr>
    <tr>
      <td>List members</td>
      <td>ether1→WAN, bridge→LAN</td>
      <td>✅ leave as-is</td>
    </tr>
    <tr>
      <td>LAN IP</td>
      <td><code class="language-plaintext highlighter-rouge">192.168.88.1/24</code> on bridge</td>
      <td>✅ leave as-is</td>
    </tr>
    <tr>
      <td>DHCP server</td>
      <td><code class="language-plaintext highlighter-rouge">defconf</code> on bridge, pool <code class="language-plaintext highlighter-rouge">default-dhcp</code>, 10m lease</td>
      <td>✅ leave as-is</td>
    </tr>
    <tr>
      <td>NAT</td>
      <td>masquerade <code class="language-plaintext highlighter-rouge">out-interface-list=WAN</code> <code class="language-plaintext highlighter-rouge">ipsec-policy=out,none</code></td>
      <td>✅ <strong>do not touch</strong> (see note)</td>
    </tr>
    <tr>
      <td>Forward ipsec</td>
      <td>rules #6/#7 accept <code class="language-plaintext highlighter-rouge">ipsec-policy in/out</code></td>
      <td>✅ VPN traffic pre-authorized</td>
    </tr>
    <tr>
      <td>WAN client</td>
      <td>DHCP client on ether1, <code class="language-plaintext highlighter-rouge">use-peer-dns=yes</code></td>
      <td>⚠️ changed in Delta 1</td>
    </tr>
    <tr>
      <td>FastTrack</td>
      <td>rule <strong>#8</strong> <code class="language-plaintext highlighter-rouge">action=fasttrack-connection</code></td>
      <td>⚠️ disabled in Delta 2</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><strong>Why NAT stays untouched:</strong> the defconf masquerade carries <code class="language-plaintext highlighter-rouge">ipsec-policy=out,none</code>, so it
deliberately skips traffic headed into the VPN tunnel (that traffic gets its own dynamic
IPsec NAT). This is exactly what we want — editing it would break the tunnel’s NAT.</p>
</blockquote>

<blockquote>
  <p><strong>Before Phase 2:</strong> make sure the WAN DHCP client is <strong>bound</strong> (<code class="language-plaintext highlighter-rouge">/ip dhcp-client print</code> →
status <code class="language-plaintext highlighter-rouge">bound</code>, and a dynamic address appears under <code class="language-plaintext highlighter-rouge">/ip address print</code>). The router needs
real internet to fetch the cert.</p>
</blockquote>

<h3 id="1b-the-only-3-changes-you-make-in-phase-1">1B. The only 3 changes you make in Phase 1</h3>

<h4 id="delta-1--stop-isp-dns--give-the-router-its-own-resolver">Delta 1 — Stop ISP DNS + give the router its own resolver</h4>

<p><code class="language-plaintext highlighter-rouge">use-peer-dns=no</code> stops your ISP injecting DNS. The <code class="language-plaintext highlighter-rouge">servers</code> line lets the <strong>router itself</strong>
resolve names (needed to fetch the cert and reach the NordVPN server). Your LAN clients’
DNS is set separately in Phase 2.7 so it rides the tunnel.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip dhcp-client set [find interface=ether1] use-peer-dns=no
/ip dns set servers=1.1.1.1,8.8.8.8 allow-remote-requests=yes
</code></pre></div></div>

<h4 id="delta-2--disable-fasttrack-or-the-vpn-gets-bypassed">Delta 2 — Disable FastTrack (or the VPN gets bypassed)</h4>

<p>On a stock defconf this is <strong>rule #8</strong>. The command finds it by action, so you don’t need
the number:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip firewall filter disable [find action=fasttrack-connection]
</code></pre></div></div>

<p>(The dynamic “dummy fasttrack counter” rule #0 stays — it does nothing once #8 is disabled.)</p>

<h4 id="delta-3--enable--configure-wifi-routeros-649--wireless-package">Delta 3 — Enable + configure WiFi (RouterOS 6.49 = <code class="language-plaintext highlighter-rouge">wireless</code> package)</h4>

<p>On a dual-band router (<code class="language-plaintext highlighter-rouge">wlan1</code> + <code class="language-plaintext highlighter-rouge">wlan2</code> — defconf already puts both in the bridge, but
disabled), run all of the following. Change the SSIDs/password to your liking:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># set the WiFi password / encryption on the default security profile
/interface wireless security-profiles
set default mode=dynamic-keys authentication-types=wpa2-psk wpa2-pre-shared-key="YourWiFiPassword"

# 2.4 GHz radio (wlan1)
/interface wireless
set wlan1 disabled=no mode=ap-bridge ssid="MyWiFi" band=2ghz-b/g/n security-profile=default

# 5 GHz radio (wlan2)
set wlan2 disabled=no mode=ap-bridge ssid="MyWiFi-5G" band=5ghz-a/n/ac security-profile=default
</code></pre></div></div>

<blockquote>
  <p>Both wlan interfaces are already bridge ports on defconf, so no bridge-port commands are needed.</p>
</blockquote>

<p><strong>Stop here and confirm normal internet works</strong> (wired + WiFi) before touching the VPN.</p>

<hr />

<h2 id="phase-2--nordvpn-full-tunnel-ikev2--ipsec-eap">Phase 2 — NordVPN full tunnel (IKEv2 / IPsec EAP)</h2>

<p>IKEv2 EAP is fully supported on 6.49. You need your <strong>NordVPN service credentials</strong>
(NOT your account email/password): Nord Account → <strong>NordVPN</strong> → <strong>Manual setup / Service
credentials</strong> → copy <strong>Username</strong> + <strong>Password</strong>.</p>

<h3 id="21-install-the-nordvpn-root-certificate">2.1 Install the NordVPN root certificate</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/tool fetch url="https://downloads.nordcdn.com/certificates/root.der"
/certificate import file-name=root.der passphrase=""
</code></pre></div></div>

<h3 id="22-ipsec-profile-proposal-policy-group--template">2.2 IPsec profile, proposal, policy group + template</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip ipsec profile
add name=NordVPN

/ip ipsec proposal
add name=NordVPN pfs-group=none

/ip ipsec policy group
add name=NordVPN

/ip ipsec policy
add dst-address=0.0.0.0/0 src-address=0.0.0.0/0 group=NordVPN proposal=NordVPN template=yes
</code></pre></div></div>

<h3 id="23-which-traffic-uses-the-tunnel-the-whole-lan">2.3 Which traffic uses the tunnel (the whole LAN)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip firewall address-list
add address=192.168.88.0/24 list=LOCAL
</code></pre></div></div>

<h3 id="24-mode-config-requests-settings-from-nord-bound-to-the-lan">2.4 Mode config (requests settings from Nord; bound to the LAN)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip ipsec mode-config
add name=NordVPN responder=no src-address-list=LOCAL
</code></pre></div></div>

<h3 id="25-peer--identity-insert-your-service-usernamepassword">2.5 Peer + identity (insert YOUR service username/password)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip ipsec peer
add address=uk2156.nordvpn.com exchange-mode=ike2 name=NordVPN profile=NordVPN

/ip ipsec identity
add peer=NordVPN auth-method=eap eap-methods=eap-mschapv2 certificate="" \
  generate-policy=port-strict mode-config=NordVPN policy-template-group=NordVPN \
  username="YOUR_NORD_SERVICE_USERNAME" password="YOUR_NORD_SERVICE_PASSWORD"
</code></pre></div></div>

<h3 id="26-verify-the-tunnel">2.6 Verify the tunnel</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip ipsec policy print          ;# PH2 State: established
/ip ipsec active-peers print    ;# shows uk2156.nordvpn.com connected
/ip firewall nat print          ;# a dynamic IPsec NAT rule appears
</code></pre></div></div>

<p>From a LAN client, an IP-check site should show the VPN server’s location (UK / London for
this server); latency will rise (expected).</p>

<h3 id="27-recommended-push-nordvpn-dns-to-clients-so-lookups-ride-the-tunnel">2.7 (Recommended) Push NordVPN DNS to clients so lookups ride the tunnel</h3>

<p>This hands your LAN clients NordVPN’s DNS directly via DHCP. Because the queries are sourced
from the LAN (192.168.88.x), they travel through the tunnel instead of leaking out the WAN:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip dhcp-server network set [find address=192.168.88.0/24] dns-server=103.86.96.100,103.86.99.100
</code></pre></div></div>

<p>Clients pick this up on their next lease renewal (reconnect WiFi / <code class="language-plaintext highlighter-rouge">ipconfig /renew</code>, or wait
out the 10-minute lease).</p>

<blockquote>
  <p>Note: the router’s <em>own</em> DNS lookups and the IPsec negotiation use the real WAN by design —
that’s required for the tunnel to come up. Your clients’ DNS is what rides the tunnel.</p>
</blockquote>

<h3 id="28-optional-kill-switch--drop-laninternet-when-the-tunnel-is-down">2.8 (Optional) Kill switch — drop LAN→internet when the tunnel is down</h3>

<p>The defconf forward chain has <strong>no explicit LAN→WAN accept rule</strong> — it lets LAN traffic out
via the implicit accept at the end of the chain. So you just <strong>add</strong> this rule and it lands at
the bottom, which is exactly the right spot. No <code class="language-plaintext highlighter-rouge">move</code> needed.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip firewall filter
add chain=forward action=drop src-address=192.168.88.0/24 out-interface-list=WAN \
  ipsec-policy=out,none comment="VPN kill switch"
</code></pre></div></div>

<p>How it behaves: tunnel <strong>up</strong> → LAN traffic matches the ipsec accept (rule #7) before reaching
this; tunnel <strong>down</strong> → LAN traffic has no ipsec policy, falls to this rule, and is dropped
(fail-closed) instead of leaking to the plain internet.</p>

<blockquote>
  <p>This only affects LAN client traffic (<code class="language-plaintext highlighter-rouge">src-address=192.168.88.0/24</code>, forward chain). The
router’s own IPsec negotiation and DNS are in other chains, so the tunnel can still connect.
To temporarily allow normal internet again: <code class="language-plaintext highlighter-rouge">/ip firewall filter disable [find comment="VPN kill switch"]</code></p>
</blockquote>

<hr />

<h2 id="phase-3--isp-router-to-bridge-mode--pppoe">Phase 3 — ISP router to bridge mode + PPPoE</h2>

<p>Do this <strong>after</strong> Phases 1–2 are confirmed working. On defconf you don’t create a WAN list —
you just re-point its member from <code class="language-plaintext highlighter-rouge">ether1</code> to the new PPPoE interface.</p>

<h3 id="31-on-the-isp-router">3.1 On the ISP router</h3>

<p>Enable <strong>bridge mode</strong> (“modem only” / disable its routing + DHCP). Internet drops until 3.2.</p>

<h3 id="32-create-the-pppoe-client-on-ether1-use-your-isp-pppoe-login">3.2 Create the PPPoE client on ether1 (use your <strong>ISP</strong> PPPoE login)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/interface pppoe-client
add name=pppoe-out1 interface=ether1 disabled=no \
  user="ISP_PPPOE_USERNAME" password="ISP_PPPOE_PASSWORD" \
  add-default-route=yes use-peer-dns=no
</code></pre></div></div>

<h3 id="33-remove-the-old-dhcp-wan-and-re-point-the-existing-wan-list-member">3.3 Remove the old DHCP WAN and re-point the existing WAN list member</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip dhcp-client remove [find interface=ether1]

/interface list member
remove [find interface=ether1 list=WAN]
add interface=pppoe-out1 list=WAN
</code></pre></div></div>

<p>NAT, firewall, and the NordVPN peer all follow the <code class="language-plaintext highlighter-rouge">WAN</code> list, so they update automatically.
The IPsec tunnel re-establishes once PPPoE is up.</p>

<h3 id="34-mss-clamping-important-for-pppoe--ipsec">3.4 MSS clamping (important for PPPoE + IPsec)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip firewall mangle
add chain=forward action=change-mss new-mss=clamp-to-pmtu passthrough=yes \
  protocol=tcp tcp-flags=syn out-interface-list=WAN comment="MSS clamp for PPPoE/VPN"
</code></pre></div></div>

<h3 id="35-verify">3.5 Verify</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/interface pppoe-client print     ;# status: connected, has an IP
/ip ipsec active-peers print      ;# NordVPN reconnected
</code></pre></div></div>

<hr />

<h2 id="quick-troubleshooting">Quick troubleshooting</h2>

<table>
  <thead>
    <tr>
      <th>Symptom</th>
      <th>Likely cause / fix</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">PH2</code> not established</td>
      <td>Wrong <strong>service</strong> creds (not account login); cert not imported; clock wrong → set NTP.</td>
    </tr>
    <tr>
      <td>Internet works but IP isn’t the VPN’s</td>
      <td>Traffic bypassing IPsec — usually leftover <strong>FastTrack</strong> (Delta 2) or wrong <code class="language-plaintext highlighter-rouge">LOCAL</code> subnet.</td>
    </tr>
    <tr>
      <td>Tunnel up but no client internet</td>
      <td>DNS — confirm Delta 1 / 2.7 and clients use <code class="language-plaintext highlighter-rouge">192.168.88.1</code>.</td>
    </tr>
    <tr>
      <td>Pages half-load after PPPoE</td>
      <td>Missing <strong>MSS clamp</strong> (3.4).</td>
    </tr>
    <tr>
      <td>Locked out after firewall change</td>
      <td>Reconnect with <strong>Winbox via MAC</strong>, fix rule order.</td>
    </tr>
    <tr>
      <td>Temporarily disable VPN</td>
      <td><code class="language-plaintext highlighter-rouge">/ip ipsec identity disable [find peer=NordVPN]</code> (disable kill-switch rule first).</td>
    </tr>
  </tbody>
</table>

<h2 id="switch-nordvpn-country-later">Switch NordVPN country later</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/ip ipsec peer set [find name=NordVPN] address=NEWSERVER.nordvpn.com
</code></pre></div></div>]]></content><author><name>Xavier Odhiambo</name><email>xavierfrank4@gmail.com</email></author><category term="mikrotik" /><category term="routeros" /><category term="nordvpn" /><category term="ikev2" /><category term="networking" /><summary type="html"><![CDATA[Step-by-step guide to connecting a MikroTik router to NordVPN using IKEv2/IPsec on RouterOS 6.49 — full configuration chain, from the default config to a full VPN tunnel with kill switch, plus PPPoE and troubleshooting.]]></summary></entry></feed>