Community discussions

MikroTik App
 
msatter
Forum Guru
Forum Guru
Topic Author
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Taking over outgoing WG connections made by external client

Thu Jan 13, 2022 1:16 am

I made this script earlier for NordVPN but that was not needed anymore and I tried earlier to connect a Privadoe WG connection but that was setup dynamically and so you have to do some manual configuration before running the script.

When the external client connects on Windows it creates a config file in directory: C:\Program Files (x86)\PrivadoVPN\Config\PrivadoVPN_Wireguard.conf and you have to copy some information into the script underneath every time you want to takeover the WG connection. The shown privatekey is altered and is also generated ever new connection by the client.

It is a reused script and has more features than needed. It can be more direct because it is know which interface is to be targeted and now if the ports are the same as before it won't apply the new config.
# Free to use. No reselling or commercial usage without an explicit written agreement by me! 
# Created by Blacklister version: 20220120-0.93
# WireGuard implementation by Mikrotik does not adapt listening port to the one used by the VPN provider.
# This because it can't iniate the connection you need first to login, this is first done by an external client (Linux/Windows/MacOS).
# That external client creates the connection and configfile used, this script take that connection over so that this device can use it.
# You first have to find that configfile and drop it it in files on the router.
#
# Extra functionality:
# Read a WireGuard config file and changes the Wireguard entries and the addresses for that VPN connection
# Directly underneath you have to enter the name of the file, the name you want to use for the VPN. Those lines are marked with: # <---
#
# version 0.91 : updated script to use SET instead of ADD and using [find xxx=$nameVPN] to edit the correct lines. Also remove a lot of obsolete code.
# version 0.92 : added checks for config file and if the lines are present that are being changed.
# version 0.93 : added a check if there is an WG connection active that can be taken over matching the configfile
# version 0.931: correct swapping name and interface which caused an error 
 
{
# the name of the config files copied to files in Winbox on this router
:local configname "PrivadoVPN_Wireguard.conf"; # <--- provide the name of the config files that you copied to /files on this router
:local nameVPN    "PrivadoAMS";	               # <--- name for this VPN connection
:local delimiter "\_";	# space
:local fileOK true;	:local wgOK true;	:local wgPeerOK true

:do { :local data [/file/get $configname contents] } on-error={ :set $fileOK false } ; # the name of the Wireguard config which to apply

if ( $fileOK ) do={
:local data [/file/get $configname contents]
:log info "Changing WireGuard settings for $nameVPN, from file $configname" 
# avoiding that a config file has no "\n" (newline) at the end of the file, adding one just to be sure
:set $data "$data\n" 
   
     # reading the config file and extractig the found values
     :while ([:len $data]!=0) do={ # eaten all the data in the file?
       # not comparing the first character because it could be a capital letter 
       :if ($data~"rivate" || $data~"ddress" || $data~"DNS" || $data~"ublic" || $data~"ndpoint" || $data~"alive" || $data~"hared") do={       
        :do {set $name   [:pick $data 0 [:find $data $delimiter]] } on-error={}; # on error avoids any panics
         :set $data [:pick $data ([:find $data $delimiter]+1) [:len $data]]        
        :do {set $isSign [:pick $data 0 [:find $data $delimiter]] } on-error={}; # on error avoids any panics
         :set $data [:pick $data ([:find $data $delimiter]+1) [:len $data]] 
        :do {set $value  [:pick $data 0 [:find $data "\n"]] } on-error={}; # on error avoids any panics
        # put the values into not yet permanent variables  
        if ( $name~"rivate" )    do={ :set $privateKey $value };      if ( $name~"ddress" )    do={ :set $localAddress $value }
        if ( $name~"DNS" )       do={ :set $DNS $value };             if ( $name~"ublic" )     do={ :set $PeerPubKey $value }
        if ( $name~"ndpoint" )   do={ :set $endpointAddress $value }; if ( $name~"alive" )     do={ :set $PersistentKA $value }
        if ( $name~"hared" )     do={ :set $PreShared $value }              
       }; # END-IF - line contains any matching data "~"  
      :set data [:pick $data ([:find $data "\n"]+1) [:len $data]]; # remove the line that has just been examined from the data array
     }; # while
:put "Importing $nameVPN with the following values:"; 	:put "Privatekey \t $privateKey" 
:put "Local-address: \t $localAddress";                 :set $network [:pick $localAddress 0 [:find $localAddress "/"]]
:put "Network: \t $network ";                           :put "DNS WireGuard: \t $DNS"
:set $pingDNS [:pick $DNS 0 [find $DNS ","]];	        :put "Netwatch ping: \t $pingDNS"
:put "PeerPubkey: \t $PeerPubKey";                      :put "PersistentKA: \t $PersistentKA"             
 :set $endpointPort    [:pick $endpointAddress ( [:find $endpointAddress ":"]+1) [:len $endpointAddress]]
 :set $endpointAddress [:pick $endpointAddress 0 [:find $endpointAddress ":"]]
:put "EndpointAddres:  $endpointAddress";               :put "EndpointPort: \t $endpointPort"; :put "PresharedKey: \t $PreShared"

:do { /interface/wireguard      get [find name=$nameVPN] } on-error={ :set $wgOK false }
:do { /interface/wireguard/peer get [find interface=$nameVPN] } on-error={ :set $wgPeerOK false }

:if ( $wgOK && $wgPeerOK ) do={
 # Changing lines
 # Set the WireGuard lines
 :do { /interface/wireguard set comment="$nameVPN Taken over by script " mtu=1420  private-key=$privateKey disable=yes [find name=$nameVPN] } on-error={}
 :delay 300ms; # give some time to set the line
 :do { /interface/wireguard/peer set allowed-address=0.0.0.0/0 comment="taken over GW connection" endpoint-address=$endpointAddress endpoint-port=$endpointPort persistent-keepalive=300 public-key=$PeerPubKey preshared-key=$PreShared [find interface=$nameVPN] } on-error={}
# Set the IP to the addresses
:do { /ip address set address=$localAddress network=$network disable=no [find interface=$nameVPN] } on-error={}

# variables can be reused because the extracted config ones are already applied and obsolete.
# Actual changing the port connecting to the Peer and activating it

      :set $WGport [/interface/wireguard/ get [find name=$nameVPN] listen-port]      
      :if ( :get [/ip/firewall/connection find dst-address~$endpointAddress ] ) do={      
      :foreach connectionID in=[/ip/firewall/connection/ find dst-address~$endpointAddress] do={
       :set $srcAddress [/ip/firewall/connection get $connectionID src-address]
       :set $newListenPort [:pick $srcAddress ([:find $srcAddress ":"]+1) [:len $srcAddress] ]
       :if ( $newListenPort  = $WGport ) do={ :log warning "WireGuard listen port already correct." }
       :if ( $newListenPort != $WGport ) do={
        /interface/wireguard set [find name=$nameVPN] disable=yes; # disable before changing so a new connection is created on the new port
        :delay 300ms; # wait 300 miliseconds
        /interface/wireguard set [find name=$nameVPN] listen-port=$newListenPort disable=no; # enable at the same time
        :if ($logging = yes) do={ :log info "WireGuard: changed listening port $WGport --> $newListenPort of $nameVPN" }
       } else={ /interface/wireguard set disable=no [find name=$nameVPN] }; # EndIf newListenPort and enable cient when nothing has to be changed        
      }; # foreach connenctionID
     } else={ :log error "No matching WireGuard connection found to take-over " }; # EndIf get-find-endpointaddress     
} else={ :log error "WireGuard or it's peer settings line not found or corrupt." }; # EndIf wgOK wgPeerOK
} else={ :log error "WG config file $configfilename not found." }; # EndIf fileOK

if ( $fileOK || $wgOK || $wgPeerOK ) do={} else={ :put "Settings not changed and check the log for the cause!" }

}
Any improvements or remarks, please put those in this tread.

ps. This is only the script to take over a connection and you have to create first the WireGuard and Peer settings. And of course the needed addresses, routing or any other firewall settings.

Update: completed also the dynamic take over of a WG connection and after running block access by the external client on your computer after the Router made the connection. This way the external client can't logout and so disconnect the connection you are using. A short keep-alive in WG ensures you stay connected till a reboot/disconnect.
Update: disabled remove of lines and change add into set. When removing the current settings in the router become invalid.
Update-2: reworked the code and made it SET instead of ADD and removed a lot obsolete code not needed anymore. Only config is read and so the script only has to change one connection instead being able to change many.
Update-3: added checks for config file and if the lines are present that are being changed.
Update-4: added a check if there is an WG connection active that can be taken over matching the config file
Update-5: correct swapping name and interface which caused an error
Last edited by msatter on Fri Jan 28, 2022 6:03 pm, edited 13 times in total.
 
msatter
Forum Guru
Forum Guru
Topic Author
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Re: Taking over outgoing WG connections made by external client

Sat Jan 15, 2022 4:40 pm

OK, I helped an other member and as a result I have now completed the script to read a config file and put that into the Wireguard and addresses config. They have to be manually be enabled after first checking if the used settings are correct. Then you need to do more configuration to be able to use Wireguard but that was not in the scope when writing this script.
The 'dynamic' take over of an existing connection is next and I will post the script for that when ready.
# Free to use. No reselling or commercial usage without an explicit written agreement by me! 
# Created by Blacklister version: 20220120-0.93
# WireGuard implementation by Mikrotik does not adapt listening port to the one used by the VPN provider.
# This because it can't iniate the connection you need first to login, this is first done by an external client (Linux/Windows/MacOS).
# That external client creates the connection and configfile used, this script take that connection over so that this device can use it.
# You first have to find that configfile and drop it it in files on the router.
#
# Extra functionality:
# Read a WireGuard config file and changes the Wireguard entries and the addresses for that VPN connection
# Directly underneath you have to enter the name of the file, the name you want to use for the VPN. Those lines are marked with: # <---
#
# version 0.91 : updated script to use SET instead of ADD and using [find xxx=$nameVPN] to edit the correct lines. Also remove a lot of obsolete code.
# version 0.92 : added checks for config file and if the lines are present that are being changed.
# version 0.93 : added a check if there is an WG connection active that can be taken over matching the configfile
# version 0.931: correct swapping name and interface which caused an error 
 
{
# the name of the config files copied to files in Winbox on this router
:local configname "PrivadoVPN_Wireguard.conf"; # <--- provide the name of the config files that you copied to /files on this router
:local nameVPN    "PrivadoAMS";	               # <--- name for this VPN connection
:local delimiter "\_";	# space
:local fileOK true;	:local wgOK true;	:local wgPeerOK true

:do { :local data [/file/get $configname contents] } on-error={ :set $fileOK false } ; # the name of the Wireguard config which to apply

if ( $fileOK ) do={
:local data [/file/get $configname contents]
:log info "Changing WireGuard settings for $nameVPN, from file $configname" 
# avoiding that a config file has no "\n" (newline) at the end of the file, adding one just to be sure
:set $data "$data\n" 
   
     # reading the config file and extractig the found values
     :while ([:len $data]!=0) do={ # eaten all the data in the file?
       # not comparing the first character because it could be a capital letter 
       :if ($data~"rivate" || $data~"ddress" || $data~"DNS" || $data~"ublic" || $data~"ndpoint" || $data~"alive" || $data~"hared") do={       
        :do {set $name   [:pick $data 0 [:find $data $delimiter]] } on-error={}; # on error avoids any panics
         :set $data [:pick $data ([:find $data $delimiter]+1) [:len $data]]        
        :do {set $isSign [:pick $data 0 [:find $data $delimiter]] } on-error={}; # on error avoids any panics
         :set $data [:pick $data ([:find $data $delimiter]+1) [:len $data]] 
        :do {set $value  [:pick $data 0 [:find $data "\n"]] } on-error={}; # on error avoids any panics
        # put the values into not yet permanent variables  
        if ( $name~"rivate" )    do={ :set $privateKey $value };      if ( $name~"ddress" )    do={ :set $localAddress $value }
        if ( $name~"DNS" )       do={ :set $DNS $value };             if ( $name~"ublic" )     do={ :set $PeerPubKey $value }
        if ( $name~"ndpoint" )   do={ :set $endpointAddress $value }; if ( $name~"alive" )     do={ :set $PersistentKA $value }
        if ( $name~"hared" )     do={ :set $PreShared $value }              
       }; # END-IF - line contains any matching data "~"  
      :set data [:pick $data ([:find $data "\n"]+1) [:len $data]]; # remove the line that has just been examined from the data array
     }; # while
:put "Importing $nameVPN with the following values:"; 	:put "Privatekey \t $privateKey" 
:put "Local-address: \t $localAddress";                 :set $network [:pick $localAddress 0 [:find $localAddress "/"]]
:put "Network: \t $network ";                           :put "DNS WireGuard: \t $DNS"
:set $pingDNS [:pick $DNS 0 [find $DNS ","]];	        :put "Netwatch ping: \t $pingDNS"
:put "PeerPubkey: \t $PeerPubKey";                      :put "PersistentKA: \t $PersistentKA"             
 :set $endpointPort    [:pick $endpointAddress ( [:find $endpointAddress ":"]+1) [:len $endpointAddress]]
 :set $endpointAddress [:pick $endpointAddress 0 [:find $endpointAddress ":"]]
:put "EndpointAddres:  $endpointAddress";               :put "EndpointPort: \t $endpointPort"; :put "PresharedKey: \t $PreShared"

:do { /interface/wireguard      get [find name=$nameVPN] } on-error={ :set $wgOK false }
:do { /interface/wireguard/peer get [find interface=$nameVPN] } on-error={ :set $wgPeerOK false }

:if ( $wgOK && $wgPeerOK ) do={
 # Changing lines
 # Set the WireGuard lines
 :do { /interface/wireguard set comment="$nameVPN Taken over by script " mtu=1420  private-key=$privateKey disable=yes [find name=$nameVPN] } on-error={}
 :delay 300ms; # give some time to set the line
 :do { /interface/wireguard/peer set allowed-address=0.0.0.0/0 comment="taken over GW connection" endpoint-address=$endpointAddress endpoint-port=$endpointPort persistent-keepalive=300 public-key=$PeerPubKey preshared-key=$PreShared [find interface=$nameVPN] } on-error={}
# Set the IP to the addresses
:do { /ip address set address=$localAddress network=$network disable=no [find interface=$nameVPN] } on-error={}

# variables can be reused because the extracted config ones are already applied and obsolete.
# Actual changing the port connecting to the Peer and activating it
 :local logging yes 
 :set $endpointAddress [:tostr [/interface/wireguard/peers/ get [find interface=$nameVPN] endpoint-address]] 
  :do {:if ([:typeof [:toip $endpointAddress]] = "nil") do={:set $endpointAddress [:resolve $endpointAddress]}} on-error={:set $endpointAddress "invalid"};
  :if ($endpointAddress != "invalid") do={
      :set $WGport [/interface/wireguard/ get [find name=$nameVPN] listen-port]
      :if ( :get [/ip/firewall/connection find dst-address~$endpointAddress ] ) do={
      :foreach connectionID in=[/ip/firewall/connection/ find dst-address~$endpointAddress] do={
       :set $srcAddress [/ip/firewall/connection get $connectionID src-address]
       :set $newListenPort [:pick $srcAddress ([:find $srcAddress ":"]+1) [:len $srcAddress] ]
       :if ( $newListenPort  = $WGport ) do={ :set wgPeerOK false; :log warning "WireGuard listen port already correct." }
       :if ( $newListenPort != $WGport && $srcAddress = $endpointAddress ) do={
        /interface/wireguard set [find name=$nameVPN] disable=yes; # disable before changing so a new connection is created on the new port
        :delay 300ms; # wait 300 miliseconds
        /interface/wireguard set [find name=$nameVPN] listen-port=$newListenPort disable=no; # enable at the same time
        :if ($logging = yes) do={ :log info "WireGuard: changed listening port $WGport --> $newListenPort of $nameVPN" }
       } else={ /interface/wireguard set disable=no [find name=$nameVPN] }; # EndIf newListenPort and enable cient when nothing has to be changed        
      }; # foreach connenctionID
     } else={ :log error "No matching WireGuard connection found to take-over " }; # EndIf get-find-endpointaddress     
   } else={ :log error "Invalid WireGuard endpointaddress" };	# if endpointAddress
} else={ :log error "WireGuard or it's peer settings line not found or corrupt." }; # EndIf wgOK wgPeerOK
} else={ :log error "WG config file $configfilename not found." }; # EndIf fileOK
if ( $fileOK || $wgOK || $wgPeerOK ) do={} else={ :put "Settings not changed and check the log for the cause!" }
}
Update 1: I have added a example config for WireGuard so that it now a more or less complete package.
Update 2: added some logging and removal of present settings in the extra config settings.
Last edited by msatter on Thu Jan 27, 2022 12:36 pm, edited 4 times in total.
 
msatter
Forum Guru
Forum Guru
Topic Author
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Re: Taking over outgoing WG connections made by external client

Mon Jan 17, 2022 3:23 pm

Completed both scripts, the first one (dynamic) takes over a running connection that goes through the router after once up in connections the external client has to be hindered to disconnect the grabbed connection.

The second script is for setting up a WG connection from also a config file that can be then activated.

Example of a config files:
[Interface]
PrivateKey = private key
Address = 10.10.10.10/32
DNS = 198.0.0.1,198.0.0.2

[Peer]
PublicKey = public key
AllowedIPs = 0.0.0.0/1, 128.0.0.0/1, ::/1, 8000::/1
Endpoint = 11.22.33.44:51820
PersistentKeepalive = 300
 
msatter
Forum Guru
Forum Guru
Topic Author
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Re: Taking over outgoing WG connections made by external client

Mon Jan 31, 2022 12:48 pm

I have now tested with taking over a dynamic connection and it works fine and the connection istaken over.

Saddly Privado in my case terminate the connection within a day andyou hsve to start the procedure again. This makes Privado unusable in my view, to use it in a router when you don't get a private key from them to use.
 
lahor
just joined
Posts: 18
Joined: Mon Jan 31, 2022 11:09 pm

Re: Taking over outgoing WG connections made by external client

Mon Jan 31, 2022 11:22 pm

Hi,This script is a great idea to make it easier to enter the conf file into the router. The only problem is that I keep getting an error:WireGuard or it's peer settings line not found or corrupt.
The config file is ok.
Can you help?
 
msatter
Forum Guru
Forum Guru
Topic Author
Posts: 2897
Joined: Tue Feb 18, 2014 12:56 am
Location: Netherlands / Nīderlande

Re: Taking over outgoing WG connections made by external client

Fri Feb 11, 2022 7:16 pm

Remember that there must be a active connection to be taken over.

Who is online

Users browsing this forum: No registered users and 20 guests