PLEASE tell me how to make a VPN Kill Switch on ROS 7.15.2

Dear Experts,

I have configured NordVPN on Mikrotik L41G-2axD&FG621-EA. Now I need to configure a kill switch, so that if the IPsec tunnel goes down, the location of the devices connected to the network won’t be revealed. Here is my configuration:

# 2025-03-04 00:22:32 by RouterOS 7.15.2
# software id = 
#
# model = L41G-2axD&FG621-EA
# serial number = 
/interface bridge
add admin-mac=xxxxx auto-mac=no comment=defconf name=bridge
/interface wifi
set [ find default-name=wifi1 ] channel.band=2ghz-ax .skip-dfs-channels=\
    10min-cac .width=20/40mhz configuration.mode=ap .ssid=WWW disabled=no \
    security.authentication-types=wpa2-psk,wpa3-psk .ft=yes .ft-over-ds=yes
/interface lte
# sim not present
set [ find default-name=lte1 ] allow-roaming=no band="" sms-read=no
/interface list
add comment=defconf name=WAN
add comment=defconf name=LAN
/ip ipsec mode-config
add name=NordVPN responder=no src-address-list=local
/ip ipsec policy group
add name=NordVPN
/ip ipsec profile
add name=NordVPN
/ip ipsec peer
add address=xx999.nordvpn.com exchange-mode=ike2 name=NordVPN profile=NordVPN
/ip ipsec proposal
add name=NordVPN pfs-group=none
/ip pool
add name=default-dhcp ranges=192.168.88.10-192.168.88.254
/ip dhcp-server
add address-pool=default-dhcp interface=bridge name=defconf
/queue type
add fq-codel-ecn=no kind=fq-codel name=fq-codel-ethernet-default
/queue interface
set ether1 queue=fq-codel-ethernet-default
set ether2 queue=fq-codel-ethernet-default
set ether3 queue=fq-codel-ethernet-default
set ether4 queue=fq-codel-ethernet-default
/disk settings
set auto-media-interface=bridge auto-media-sharing=yes auto-smb-sharing=yes
/interface bridge port
add bridge=bridge comment=defconf interface=ether1
add bridge=bridge comment=defconf interface=ether2
add bridge=bridge comment=defconf interface=ether3
add bridge=bridge comment=defconf interface=ether4
add bridge=bridge comment=defconf interface=wifi1
/ip neighbor discovery-settings
set discover-interface-list=LAN
/interface list member
add comment=defconf interface=bridge list=LAN
add comment=defconf interface=lte1 list=WAN
/ip address
add address=192.168.88.1/24 comment=defconf interface=bridge network=\
    192.168.88.0
/ip dhcp-server network
add address=192.168.88.0/24 comment=defconf dns-server=192.168.88.1 gateway=\
    192.168.88.1
/ip dns
set allow-remote-requests=yes
/ip dns static
add address=192.168.88.1 comment=defconf name=router.lan
/ip firewall address-list
add address=192.168.88.0/24 list=local
/ip firewall filter
add action=accept chain=input comment=\
    "defconf: accept established,related,untracked" connection-state=\
    established,related,untracked
add action=drop chain=input comment="defconf: drop invalid" connection-state=\
    invalid
add action=accept chain=input comment="defconf: accept ICMP" protocol=icmp
add action=accept chain=input comment=\
    "defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1
add action=drop chain=input comment="defconf: drop all not coming from LAN" \
    in-interface-list=!LAN
add action=accept chain=forward comment="defconf: accept in ipsec policy" \
    ipsec-policy=in,ipsec
add action=accept chain=forward comment="defconf: accept out ipsec policy" \
    ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \
    connection-state=established,related hw-offload=yes
add action=accept chain=forward comment=\
    "defconf: accept established,related, untracked" connection-state=\
    established,related,untracked
add action=drop chain=forward comment="defconf: drop invalid" \
    connection-state=invalid
add action=drop chain=forward comment=\
    "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \
    connection-state=new in-interface-list=WAN
/ip firewall nat
add action=masquerade chain=srcnat comment="defconf: masquerade" \
    ipsec-policy=out,none out-interface-list=WAN
/ip ipsec identity
add auth-method=eap certificate="" eap-methods=eap-mschapv2 generate-policy=\
    port-strict mode-config=NordVPN peer=NordVPN policy-template-group=\
    NordVPN username=xxxxxxxxxxx
/ip ipsec policy
add dst-address=0.0.0.0/0 group=NordVPN proposal=NordVPN src-address=\
    0.0.0.0/0 template=yes
/ipv6 firewall address-list
add address=::/128 comment="defconf: unspecified address" list=bad_ipv6
add address=::1/128 comment="defconf: lo" list=bad_ipv6
add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6
add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6
add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6
add address=100::/64 comment="defconf: discard only " list=bad_ipv6
add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6
add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6
add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6
/ipv6 firewall filter
add action=accept chain=input comment=\
    "defconf: accept established,related,untracked" connection-state=\
    established,related,untracked
add action=drop chain=input comment="defconf: drop invalid" connection-state=\
    invalid
add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\
    icmpv6
add action=accept chain=input comment="defconf: accept UDP traceroute" \
    dst-port=33434-33534 protocol=udp
add action=accept chain=input comment=\
    "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\
    udp src-address=fe80::/10
add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \
    protocol=udp
add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\
    ipsec-ah
add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\
    ipsec-esp
add action=accept chain=input comment=\
    "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec
add action=drop chain=input comment=\
    "defconf: drop everything else not coming from LAN" in-interface-list=\
    !LAN
add action=accept chain=forward comment=\
    "defconf: accept established,related,untracked" connection-state=\
    established,related,untracked
add action=drop chain=forward comment="defconf: drop invalid" \
    connection-state=invalid
add action=drop chain=forward comment=\
    "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6
add action=drop chain=forward comment=\
    "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6
add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \
    hop-limit=equal:1 protocol=icmpv6
add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\
    icmpv6
add action=accept chain=forward comment="defconf: accept HIP" protocol=139
add action=accept chain=forward comment="defconf: accept IKE" dst-port=\
    500,4500 protocol=udp
add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\
    ipsec-ah
add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\
    ipsec-esp
add action=accept chain=forward comment=\
    "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec
add action=drop chain=forward comment=\
    "defconf: drop everything else not coming from LAN" in-interface-list=\
    !LAN
/system note
set show-at-login=no
/tool mac-server
set allowed-interface-list=LAN
/tool mac-server mac-winbox
set allowed-interface-list=LAN

also the IP addresses and the routing table:

[admin@MikroTik] > /ip address print
Columns: ADDRESS, NETWORK, INTERFACE
# ADDRESS          NETWORK       INTERFACE
;;; defconf
0 192.168.88.1/24  192.168.88.0  bridge   


[admin@MikroTik] > /ip route print
Flags: D - DYNAMIC; A - ACTIVE; c - CONNECT
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS      GATEWAY  DISTANCE
DAc 192.168.88.0/24  bridge          0

Many thanks, I spent a lot of time on googling and experimenting, no success thus far :frowning: Everything I tried cuts the entire traffic immediately, even though the VPN tunnel is connected. :frowning:

P. S.: Please kindly provide answers that will be understandable for beginners, my experience with Mikrotik is zero and I am not very good at networking, though I’ve got the very basics down. Thank you!

http://forum.mikrotik.com/t/vpn-killswitch-in-ros7/156763/9

Thanks for you reply! If you have a working solution, could you kindly post it here ?

Schedule this:

:if ([/ip ipsec policy get [find where peer="NordVPN"] ph2-state] = "estabilished") do={
    /ip firewall nat enable [find where comment="defconf: masquerade" and disabled]
} else={
    /ip firewall nat disable [find where comment="defconf: masquerade" and !disabled]
}

paste this for enable the scheduler

/system scheduler
add interval=2s name=disable_NAT_if_IPsec_is_not_estabilished on-event=":if ([/ip ipsec policy get [find where peer=\"NordVPN\"] ph2-state] = \"estabilished\") do={\r\
    \n    /ip firewall nat enable [find where comment=\"defconf: masquerade\" and disabled]\r\
    \n} else={\r\
    \n    /ip firewall nat disable [find where comment=\"defconf: masquerade\" and !disabled]\r\
    \n}" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-date=mar/08/2025 start-time=10:10:10

Thank you !! I was JUST thinking about a similar thing !! Was going to post a new topic in the scripting forum !!
I will try to do it by myself, but if you are online and can answer quickly, I would really appreciate if you could just give the commands:

  1. How to add a comment to the route
  2. How to schedule the script ? I will let it run every second to keep the exposing risk as minimal as possible

And one more question - isn’t it a bit easier to bring down lte1 instead of messing with the routes ? Just my personal idea… Thank you SO MUCH !!!

And a second “one more question” :slight_smile: Do I have to define your code snippet as a custom script ? Thanks!

  1. I use winbox, not the command line.
    The dynamic route created by LTE lost the comment everytime.

    \

  2. Is not cpu expensive, so every 2 second suffice.
    I add the “copy & paste” for create the schedule

  3. If you bring down the LTE, how the IPsec can go up?
    I miss something on your config? Is not the LTE your wan source? I do not see any DHCP client, PPPoE client, route, etc.

Do I have to define your code snippet as a custom script?
What? I do not understand the mean…

@rextended - I always reply with quote. Otherwise I am not sure that the person will be notified about my reply.

Can I please ask you why you are pulling back from your suggestion with checking the established state of the VPN connection ?
Could you kindly reply to the questions in my previous post ? Many thanks!

Sorry, I apologize for not being clear. I meant if I have to use the following:

/system script
add name=check-vpn=read,write source="<YOUR CODE HERE>"

I added the answers in the post after your questions.

The previous posts have been integrated.
Always check the IPsec status, only that it is no longer based on commenting dynamic routes…

The scheduler is put on the same previous topic where is the script.

I am not sure if it will work with this command:

[admin@MikroTik] > /ip ipsec policy print
Flags: T - TEMPLATE; D - DYNAMIC, A - ACTIVE; * - DEFAULT
Columns: PEER, TUNNEL, SRC-ADDRESS, DST-ADDRESS, PROTOCOL, ACTION, LEVEL, PH2-COUNT

PEER TUNNEL SRC-ADDRESS DST-ADDRESS PROTOCOL ACTION LEVEL PH2-COUNT

0 T * ::/0 ::/0 all
1 T 0.0.0.0/0 0.0.0.0/0 all
2 D NordVPN yes 10.6.0.17/32 0.0.0.0/0 all encrypt unique 1

[admin@MikroTik] > /ip ipsec policy get [find where peer=“NordVPN”]
[admin@MikroTik] >

I am also using Winbox :slight_smile: Thanks to you, guys. Otherwise it’s really impossible to work with this router!

/ip ipsec policy get [find where peer=“NordVPN”]
alone do not do nothing

:put [/ip ipsec policy get [find where peer="NordVPN"] ph2-state]

OK, you modified this post after I replied to it :slight_smile:

Me too :slight_smile:

Yes, you are right. Then something else that knocks out the entire internet connection. Maybe a simple firewall rule that kills everything. Or a bad route. You name it, you are the expert here :slight_smile:

I am not sure. I think there was some DHCP in the standard configuration of the router. The only thing I can do is to download the entire configuration and post it again.

I mean this:
/system script
add name=check-vpn policy=read,write source="

@rextended - THANK YOU SO MUCH !!!!!! You made my day and even my week !!!!!

It works !! I would really say LIKE A CHARM !!! There is just one little thing that is a bit strange.

I have a ping 8.8.8.8 command running. I bring down the VPN:

/ip ipsec peer disable [find]

The connection immediately gets cut off. The ping command times out. When I reactivate the VPN connection with peer enable, ping 8.8.8.8 still keeps timing out.
I have to run a ping google.com command, it works fine and pings are returned. After that ping 8.8.8.8 works as well. Would be very nice if you help me understand
why it is acting like that. THANKS !!!

The connection needs to be re-established to ping 8.8.8.8

Of course I did reestablish the connection. It shows established for 30-50 seconds or more.
Even when I try to open a website in a browser, it doesn’t work, until I make ping google.com on the OS prompt.
This is not a problem for me. What really counts is that the internet is cut off when there is no VPN tunnel.
However I am curious what is the reason for this unexpected behavior. Thank you!

PLEASE do not quote SO USELESSLY…

The actual ping/https process have it’s connection taken down, for restart ping/https from that process, stop and start again, for create a new connection.

The fix is close… (wait…)

add this on “disable NAT” part of the script… do not alter timeout, is here for logical reason…

/ip firewall connection
:foreach idc in=[find where timeout>4] do={
    remove [find where .id=$idc]
}

so, alter the script form this:

    /ip firewall nat disable [find where comment="defconf: masquerade" and !disabled]
}

to this:

    /ip firewall nat disable [find where comment="defconf: masquerade" and !disabled]
    /ip firewall connection
    :foreach idc in=[find where timeout>4] do={
        remove [find where .id=$idc]
    }
}

On this way do two tings: disable NAT for do not use regular LTE, instead of IPsec, ad remove all broken connections.