Mikrotik IPSec Tunnel/VPN When Both Sides Have Dynamic IPs/DHCP
At first glance, one would think this is impossible. It is NOT impossible, thanks to some scripting and a couple of free services. This will work for straight IPSec tunnels, PPTP tunnels, IPIP tunnels or even IPIP tunnels encrypted with IPSec 🙂
Step 1 is to figure out what our public IP is and a method to share it with the remote site. We are going to be using dns-o-matic. This is a free service from opendns that allows you to update multiple different dynamic DNS services via a single interface. I’m using dyndns.org for this example. In a nutshell dyndns.org allows you to update a publicly available DNS entry that is a subdomain of dyndns.org. In our example we will use gregsowell-siteA.dyndns.org and gregsowell-siteB.dyndns.org. So, we need a method to update our DNS entry…a SCRIPT!
Mikrotik Dynamic DNS Update Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # DNSoMatic automatic DNS updates # User account info of DNSoMatic :global maticuser "user" :global maticpass "password" :global matichost "Yourhost" # No more changes need :global previousIP # Print values for debug :log info "DNSoMatic: Updating dynamic IP on DNS for host $matichost" :log info "DNSoMatic: User $maticuser y Pass $maticpass" :log info "DNSoMatic: Last IP $previousIP" # get the current IP address from the internet (in case of double-nat) /tool fetch mode=http address="checkip.dyndns.org" src-path="/" dst-path="/dyndns.checkip.html" :local result [/file get dyndns.checkip.html contents] # parse the current IP result :local resultLen [:len $result] :local startLoc [:find $result ": " -1] :set startLoc ($startLoc + 2) :local endLoc [:find $result "</body>" -1] :global currentIP [:pick $result $startLoc $endLoc] :log info "DNSoMatic: IP actual $currentIP" # Touching the string passed to fetch command on "src-path" option :local str "/nic/update?hostname=$matichost&myip=$currentIP&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG" :if ($currentIP != $previousIP) do={ :log info "DNSoMatic: Update need" :set previousIP $currentIP :log info "DNSoMatic: Sending update $currentIP" :log info [ :put [/tool fetch host=MT user=$maticuser password=$maticpass mode=http address="updates.dnsomatic.com" src-path=$str dst-path=$matichost]] :log info "DNSoMatic: Host $matichost updated on DNSoMatic with IP $currentIP" } else={ :log info "DNSoMatic: Previous IP $previousIP and current $currentIP equal, no update need" } |
In order for this script to work correctly, you need to update the dns-o-matic infomation at the top. You will also need to configure DNS servers on your Mikrotik…how else will it resolve the URLs 😉
1 2 | /ip dns set primary-dns=8.8.8.8 secondary-dns=4.2.2.2 |
Once you get your script in, you will need to schedule it to run at whatever interval you prefer. I use a 10 minute interval.
1 2 3 | /system scheduler add comment="" disabled=no interval=10m name=dynamic-dns-schedule on-event=dynamic-dns-script \ start-date=jan/01/1970 start-time=00:00:01 |
Dynamic IPs on both sides with IPSec
SiteA:
Setup Dynamic Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /system script add name=dynamic-dns-script policy=\ ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive source="\ # User account info of DNSoMatic\r\ \n:global maticuser \"user\"\r\ \n:global maticpass \"password\"\r\ \n:global matichost \"gregsowell-sitea.dyndns.org\"\r\ \n# No more changes need\r\ \n\r\ \n:global previousIP\r\ \n\r\ \n# Print values for debug\r\ \n:log info \"DNSoMatic: Updating dynamic IP on DNS for host \$matichost\"\ \r\ \n:log info \"DNSoMatic: User \$maticuser y Pass \$maticpass\"\r\ \n:log info \"DNSoMatic: Last IP \$previousIP\"\r\ \n\r\ \n# get the current IP address from the internet (in case of double-nat)\r\ \n/tool fetch mode=http address=\"checkip.dyndns.org\" src-path=\"/\" dst-\ path=\"/dyndns.checkip.html\"\r\ \n:local result [/file get dyndns.checkip.html contents]\r\ \n\r\ \n# parse the current IP result\r\ \n:local resultLen [:len \$result]\r\ \n:local startLoc [:find \$result \": \" -1]\r\ \n:set startLoc (\$startLoc + 2)\r\ \n:local endLoc [:find \$result \"</body>\" -1]\r\ \n:global currentIP [:pick \$result \$startLoc \$endLoc]\r\ \n:log info \"DNSoMatic: IP actual \$currentIP\"\r\ \n\r\ \n# Touching the string passed to fetch command on \"src-path\" option\r\ \n:local str \"/nic/update\?hostname=\$matichost&myip=\$currentIP&wildcard\ =NOCHG&mx=NOCHG&backmx=NOCHG\"\r\ \n\r\ \n:if (\$currentIP != \$previousIP) do={\r\ \n:log info \"DNSoMatic: Update need\"\r\ \n:set previousIP \$currentIP\r\ \n:log info \"DNSoMatic: Sending update \$currentIP\"\r\ \n:log info [ :put [/tool fetch host=MT user=\$maticuser password=\$maticp\ ass mode=http address=\"updates.dnsomatic.com\" src-path=\$str dst-path=\$\ matichost]]\r\ \n:log info \"DNSoMatic: Host \$matichost updated on DNSoMatic with IP \$c\ urrentIP\"\r\ \n} else={\r\ \n:log info \"DNSoMatic: Previous IP \$previousIP and current \$currentIP \ equal, no update need\"\r\ \n}" /system scheduler add comment="" disabled=no interval=10m name=dynamic-dns-schedule on-event=dynamic-dns-script \ start-date=jan/01/1970 start-time=00:00:01 |
Setup NAT Bypass
1 2 3 4 5 | /ip firewall nat add action=accept chain=srcnat comment="NAT bypass" disabled=no dst-address=\ 192.168.0.0/16 out-interface=ether1 add action=masquerade chain=srcnat comment="default PAT" disabled=no out-interface=\ ether1 |
Setup IPSec Peer
1 2 3 4 5 6 | /ip ipsec peer add address=2.2.2.2/32:500 auth-method=pre-shared-key dh-group=modp1024 \ disabled=no dpd-interval=disable-dpd dpd-maximum-failures=1 \ enc-algorithm=3des exchange-mode=main generate-policy=no hash-algorithm=\ md5 lifebytes=0 lifetime=1d nat-traversal=no proposal-check=obey secret=\ test send-initial-contact=yes |
Setup IPSec Policy
1 2 3 4 5 | /ip ipsec policy add action=encrypt disabled=no dst-address=192.168.2.0/24:any \ ipsec-protocols=esp level=require priority=0 proposal=default protocol=\ all sa-dst-address=2.2.2.2 sa-src-address=1.1.1.1 src-address=\ 192.168.1.0/24:any tunnel=yes |
Now that we have the basics configured, I’m sure you noticed that I put IP addresses in the IPSec peer and policy. The whole point here is that we are running our public side via DHCP, so how does this benefit us? As it is now, it doesn’t. We need another script to update our peer and policy in the event of an IP change.
Your peers and policies are numbered from 0 up. This list is a static list that can be referenced, for our update. The number entry is located right after the word set. In the below scripts, be sure to update it to the proper peer number and policy number. You can figure out their numbers by issuing print commands from a terminal:
1 2 | /ip ipsec peer print /ip ipsec policy print |
You can see that the script resolves the IP address for siteA and siteB, then sets the entries as they should be.
Peer/Policy Update Script
1 2 3 4 | :global LocalSite [:resolve gregsowell-siteA.dyndns.org] :global RemoteSite [:resolve gregsowell-siteb.dyndns.org] /ip ipsec policy set 0 sa-dst-address=$RemoteSite sa-src-address=$LocalSite /ip ipsec peer set 0 address="$RemoteSite/32:500" |
Peer/Policy Update Script – Copy and paste Version
1 2 3 4 5 6 7 8 | /system script add name=dynamic-router-update policy=\ ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive source="\ :global LocalSite [:resolve gregsowell-siteA.dyndns.org]\r\ \n:global RemoteSite [:resolve gregsowell-siteb.dyndns.org]\r\ \n/ip ipsec policy set 0 sa-dst-address=\$RemoteSite sa-src-address=\$Loca\ lSite\r\ \n/ip ipsec peer set 0 address=\"\$RemoteSite/32:500\"" |
You can either create a new schedule to run the peer/policy update, or you can just add the script to your existing schedule, which is what I recommend.
1 2 3 4 5 6 | /system scheduler add comment="" disabled=no interval=10m name=dynamic-dns-schedule on-event=\ "dynamic-dns-script\r\ \ndynamic-router-update" policy=\ ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive \ start-date=jan/01/1970 start-time=00:00:01 |
Site B should configure the same, only in reverse order for the IP addresses.
IPIP with IPSec
SiteA:
The dynamic script and scheduler is the same as above.
Setup IPIP Tunnel Interface
1 2 3 | /interface ipip add comment="" disabled=no local-address=1.1.1.1 mtu=1480 name=ipip1 \ remote-address=2.2.2.2 |
Setup IPSec Peer
1 2 3 4 5 6 | /ip ipsec peer add address=2.2.2.2/32:500 auth-method=pre-shared-key dh-group=modp1024 \ disabled=no dpd-interval=disable-dpd dpd-maximum-failures=1 \ enc-algorithm=3des exchange-mode=main generate-policy=no hash-algorithm=\ md5 lifebytes=0 lifetime=1d nat-traversal=no proposal-check=obey secret=\ test send-initial-contact=yes |
Setup IPSec Policy
1 2 3 4 5 | /ip ipsec policy add action=encrypt disabled=no dst-address=2.2.2.2/32:any ipsec-protocols=esp \ level=require priority=0 proposal=default protocol=ip-encap \ sa-dst-address=2.2.2.2 sa-src-address=1.1.1.1 src-address=1.1.1.1/32:any \ tunnel=no |
We’re going to add an additional step to the update script to take into account the new entries for our policy and for the IPIP interface
You can see that the script resolves the IP address for siteA and siteB, then sets the entries as they should be.
Peer/Policy Update Script
1 2 3 4 5 | :global LocalSite [:resolve gregsowell-siteA.dyndns.org] :global RemoteSite [:resolve gregsowell-siteb.dyndns.org] /ip ipsec policy set 0 sa-dst-address=$RemoteSite sa-src-address=$LocalSite dst-address="$RemoteSite/32:any" src-address="$LocalSite/32:any" /ip ipsec peer set 0 address="$RemoteSite/32:500" /interface ipip set ipip1 local-address=$LocalSite remote-address=$RemoteSite |
Peer/Policy Update Script – Copy and paste Version
1 2 3 4 5 6 7 8 9 10 11 | /system script add name=dynamic-router-update policy=\ ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive source="\ :global LocalSite [:resolve gregsowell-siteA.dyndns.org]\r\ \n:global RemoteSite [:resolve gregsowell-siteb.dyndns.org]\r\ \n/ip ipsec policy set 0 sa-dst-address=\$RemoteSite sa-src-address=\$Loca\ lSite dst-address=\"\$RemoteSite/32:any\" src-address=\"\$LocalSite/32:any\ \"\r\ \n/ip ipsec peer set 0 address=\"\$RemoteSite/32:500\"\r\ \n/interface ipip set ipip1 local-address=\$LocalSite remote-address=\$Rem\ oteSite" |
You can either create a new schedule to run the peer/policy update, or you can just add the script to your existing schedule, which is what I recommend.
1 2 3 4 5 6 | /system scheduler add comment="" disabled=no interval=10m name=dynamic-dns-schedule on-event=\ "dynamic-dns-script\r\ \ndynamic-router-update" policy=\ ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive \ start-date=jan/01/1970 start-time=00:00:01 |
Well, there you have it folks. So if you have DHCP at both ends and you are trying to establish a service that requires IP addressing, you can use this script to make it all work. If you feel so inclined, please leave me some feedback if you found this useful.
Hi Greg,
Thanks for this, it works like a charm.
If i have multiple sites, would i just modify the Peer/Policy Update Script with the “set” to the different tunnel number? ie 0, 1, 2 etc
Wayne,
You are correct sir. 🙂 Just modify the set number to equal which entry you would like to adjust.
I am impressed thanks again for your good work, keep it up!! 🙂
Wayne, thank you sir and good luck!
After running the solution for a while, it seems that the script to update the peer/policy, doesn’t execute properly, if i manually run it then it works?
Strange but any ideas?
So the IP update script is working, but the settings update is failing. I assume you checked your time and date on the run portion of your script? Possibly you have it set to start January 2010 with a repeat every 5 minutes, but the time on your router accidentally was reset to January 1970?
Hi Greg,
Yes the script works, but when scheduled it does work. The dates are correct and it also shows me a run count, so the scheduler is working. Could it be that there is a delay in contacting the DNS server? Btw i have several other scripts including the DDNS script running, and they are all working 100%.
Input is much appreciated
Wayne,
under system -> logging enable script logging. Lets see if anything is being reported. Also, put some informationals in the script every so often so you can see if it is just jamming up on a specific part:
:log info “$LocalSite”
:log info “$RemoteSite”
:log info “got to part1”
:log info “got to part2”
Ok, Have put that in, but i did add static DNS server on the RB’s and seems to be running better.
Thanks.
Thanks Greg for your great tutorials. I am just getting into Mikrotik Scripting so I can update my WAN IP (Dynamic Private via DHCP) in the Policy settings (on remote sites). What Command or method do you recommend to pull the WAN IP as a global variable to have the script set the Source IP in the Policy.
Thanks in advance.
Chris,
Follow the dns update script above. The $currentIP variable is what you are looking for.
Greg,
You don’t know how much you’ve helped me in the past years.. Keep up the good work and have a good new years.
@Chad
Thanks dude. It’s not very often I get a compliment!
Does the script work on 5.2 ?
Cuz I had no luck run it on RB750GL-5.2
@William
I’m not sure sir…I’ve not tested it on V5 code. Try 5.4 as it is the most recent release.
Great videos and information by the way. I owe getting OSPF off the ground on my network to you!
Hi, Greg!
I have a question regarding this dns-o-matic thing. I am not sure what this script in the Step 1 is suppose to do. The script for the Site A seems to me like a simple dyndns.org update script. So why to get that dns-o-matic in the game? I am new with all this scripting and dynamic DNS, so your help would be much appreciated…
btw, I love your videos!
Mario
@Mario
You are correct, it is just a dyndns update script. I just chose to show that one because it updates nearly any provider.
Thanks for clearing it up!
Just a update, I install this script (IPSEC only) in two RG750 v.5.20, I have to modify 3 little things:
– IPSEC peer (port notation changed):
/ip ipsec peer
add address=2.2.2.2/32 port=500 auth-method=pre-shared-key dh-group=modp1024 \
disabled=no dpd-interval=disable-dpd dpd-maximum-failures=1 \
enc-algorithm=3des exchange-mode=main generate-policy=no hash-algorithm=\
md5 lifebytes=0 lifetime=1d nat-traversal=no proposal-check=obey secret=\
test send-initial-contact=yes
– IPSEC policy (port notation changed):
/ip ipsec policy
add action=encrypt disabled=no dst-address=192.168.2.0/24 dst-port=any \
ipsec-protocols=esp level=require priority=0 proposal=default protocol=\
all sa-dst-address=2.2.2.2 sa-src-address=1.1.1.1 src-address=\
192.168.1.0/24 src-port=any tunnel=yes
– Schedule (don’t work with two scripts in a row without run):
/system scheduler
add comment=”” disabled=no interval=10m name=dynamic-dns-schedule on-event=\
“/system script run dynamic-dns-script\r\
\n/system script run dynamic-router-update” policy=\
ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive \
start-date=jan/01/1970 start-time=00:00:01
Obs. If you have a restritive input filter you need to accept udp port 500 and accept ipsec-esp protocol
Thanks Greg.