GUIDE / VPN / IPSEC

IPsec.

IPsec is the right pick for permanent site-to-site tunnels — especially when one end isn't a RouterOS router. This page sets up an IKEv2 site-to-site between two routers. Road-warrior IKEv2 is similar shape but uses EAP or certificate auth; if you want that, the WireGuard path is shorter.

v6 vs v7 syntax

RouterOS v6
RouterOS v7

Topology

Two routers, each with a LAN behind it. Goal: any host on LAN-A can reach any host on LAN-B over an encrypted tunnel between the routers' WAN interfaces.

  LAN-A 10.0.10.0/24 ─── [Router-A: 203.0.113.1] ═══tunnel═══ [Router-B: 198.51.100.1] ─── LAN-B 10.0.20.0/24
  

Step 1 — Profile (IKE phase 1 parameters)

Set a strong default profile. RouterOS ships a "default" profile with older crypto for compatibility; create your own.

v6 3 lines · 120 bytes
/ip ipsec profile add name=mtkf-strong \
  hash-algorithm=sha256 enc-algorithm=aes-256 \
  dh-group=modp2048 lifetime=1h
v7 3 lines · 120 bytes
/ip/ipsec/profile add name=mtkf-strong \
  hash-algorithm=sha256 enc-algorithm=aes-256 \
  dh-group=modp2048 lifetime=1h

AES-256 + SHA-256 + DH group 14 (modp2048) is the minimum to use in 2026. Stronger options exist (AES-GCM, ecp384) — match what the other end supports. The same profile is used on both routers.

Step 2 — Peer (the other router)

On Router-A:

v6 2 lines · 100 bytes
/ip ipsec peer add name=to-site-b address=198.51.100.1/32 \
  profile=mtkf-strong exchange-mode=ike2
v7 2 lines · 100 bytes
/ip/ipsec/peer add name=to-site-b address=198.51.100.1/32 \
  profile=mtkf-strong exchange-mode=ike2

On Router-B, mirror with the other router's IP. Both ends must agree on the profile parameters; a mismatch shows up as "no proposal chosen" in the log.

Step 3 — Identity (auth + PSK)

v6 2 lines · 105 bytes
/ip ipsec identity add peer=to-site-b auth-method=pre-shared-key \
  secret="<long-random-shared-secret>"
v7 2 lines · 105 bytes
/ip/ipsec/identity add peer=to-site-b auth-method=pre-shared-key \
  secret="<long-random-shared-secret>"

Generate a long shared secret (32+ random bytes, base64-encoded). Store it the same way you'd store an API key — anyone with the secret can impersonate either end. Certificate auth is also supported (auth-method=rsa-signature) and recommended once you have PKI infrastructure.

Step 4 — Proposal (IKE phase 2 transforms)

v6 3 lines · 129 bytes
/ip ipsec proposal add name=mtkf-strong \
  auth-algorithms=sha256 enc-algorithms=aes-256-cbc \
  pfs-group=modp2048 lifetime=30m
v7 3 lines · 129 bytes
/ip/ipsec/proposal add name=mtkf-strong \
  auth-algorithms=sha256 enc-algorithms=aes-256-cbc \
  pfs-group=modp2048 lifetime=30m

Step 5 — Policy (which traffic to encrypt)

On Router-A:

v6 3 lines · 140 bytes
/ip ipsec policy add peer=to-site-b proposal=mtkf-strong \
  src-address=10.0.10.0/24 dst-address=10.0.20.0/24 \
  tunnel=yes action=encrypt
v7 3 lines · 140 bytes
/ip/ipsec/policy add peer=to-site-b proposal=mtkf-strong \
  src-address=10.0.10.0/24 dst-address=10.0.20.0/24 \
  tunnel=yes action=encrypt

Mirror on Router-B with src + dst swapped (B's LAN as src, A's LAN as dst). The traffic that matches src→dst gets encrypted into the tunnel.

NAT exemption

If Router-A NATs everything outbound (most consumer setups do), the LAN-A → LAN-B traffic will hit the masquerade rule BEFORE it hits the IPsec policy. Add an exemption rule above masquerade:

v6 3 lines · 157 bytes
/ip firewall nat add chain=srcnat \
  src-address=10.0.10.0/24 dst-address=10.0.20.0/24 action=accept \
  comment="IPsec — do not NAT site-to-site traffic"
v7 3 lines · 157 bytes
/ip/firewall/nat add chain=srcnat \
  src-address=10.0.10.0/24 dst-address=10.0.20.0/24 action=accept \
  comment="IPsec — do not NAT site-to-site traffic"

Place this rule ABOVE the masquerade rule. Without it, packets from LAN-A → LAN-B leave Router-A with a source of 203.0.113.1, IPsec's policy doesn't match, and the packet sails out the WAN unencrypted.

Firewall rules

v6 6 lines · 454 bytes
# Accept IKE (UDP/500) + IPsec NAT-T (UDP/4500) + ESP (50) from peer
/ip firewall filter add chain=input action=accept protocol=udp dst-port=500,4500 src-address=198.51.100.1
/ip firewall filter add chain=input action=accept protocol=ipsec-esp src-address=198.51.100.1
# Accept tunnel-decapsulated traffic
/ip firewall filter add chain=forward action=accept ipsec-policy=in,ipsec
/ip firewall filter add chain=forward action=accept ipsec-policy=out,ipsec
v7 4 lines · 348 bytes
/ip/firewall/filter add chain=input action=accept protocol=udp dst-port=500,4500 src-address=198.51.100.1
/ip/firewall/filter add chain=input action=accept protocol=ipsec-esp src-address=198.51.100.1
/ip/firewall/filter add chain=forward action=accept ipsec-policy=in,ipsec
/ip/firewall/filter add chain=forward action=accept ipsec-policy=out,ipsec

Verify

v6 3 lines · 89 bytes
/ip ipsec active-peers print
/ip ipsec installed-sa print
/log print where topics~"ipsec"
v7 3 lines · 89 bytes
/ip/ipsec/active-peers print
/ip/ipsec/installed-sa print
/log print where topics~"ipsec"

A healthy tunnel shows an established peer and SA entries in both directions. "No proposal chosen" in the log means the two ends disagree on profile or proposal parameters; "no policy found" means the policy entries don't mirror correctly between the routers.