Automating custom routes and DNS setup on Windows
4 min read

Automating custom routes and DNS setup on Windows

How I automated setting up custom routes and DNS for FortiClient SSL VPN on Windows 10
Automating custom routes and DNS setup on Windows

One of the problems I've faced working from home for the last one year is the rigidity of the VPN software used at my work. If we were using something like OpenVPN, then I could modify the client config to setup any overrides I wanted to the network routing table or DNS, but we use FortiClient SSL VPN, which does not have such a functionality. Also, I've been using Windows 10 on my work setup for some time now because WSL works very well for me.

But first, why did I even need to modify the routing table or DNS at all?

  1. I use a slightly non-standard network setup for my home, and one of my home subnets actually clashes with one of the routed work subnets (which I don't need). So, the easy solution for me is to change this routing table entry to what works for me.
  2. I use diversion on my home router to do central ad-blocking, and wanted to leverage that even when connected to the work VPN.

Here is the PowerShell script that does the changes I want:

Start-Transcript -Append -Path "C:\Users\srijan\Apps\network-post-connect.log"

if( (Get-NetConnectionProfile -InterfaceAlias Wi-Fi).Name -eq "Home Wifi" ) {
    echo "Home Wifi is connected"
    $FortinetAdapter = Get-NetAdapter -InterfaceDescription "Fortinet SSL*"
    if($FortinetAdapter.Status -eq "Up") {
        echo "Work VPN is connected"
        $FortinetAdapter | Set-DnsClientServerAddress -ServerAddresses ("192.168.2.1", "8.8.8.8")
        echo "[OK] DNS server set to 192.168.2.1,8.8.8.8"
        Get-NetRoute -DestinationPrefix 192.168.2.0/24 -RouteMetric 0 | Set-NetRoute -RouteMetric 500
        echo "[OK] 192.168.2.0/24 routed locally"
    }
    else {
        echo "Work VPN is not connected. Doing nothing."
    }
}
else {
    "Home Wifi is not connected. Doing nothing."
}

Stop-Transcript
network-post-connect.ps

Explanation of what it does:

  1. Uses Start-Transcript and Stop-Transcript to log the output to a file.
  2. Checks if the system is connected to SSID "Home Wifi".
  3. If so, checks if the adapter with description with the pattern "Fortinet SSL*" is up
  4. If so, changes the DNS server address
  5. In the routing table, it increases the route metric of 192.168.2.0/24 with metric 0 - sets it to 500. This route is added by FortiClient, which I wanted to de-prioritize.

From the Set-NetRoute docs:

The computer selects the route with the lowest combined value.

Now, we just need to setup some automation to run this whenever the VPN is connected. For this, I used Windows Task Scheduler. Whenever windows activates any network profile, a Microsoft-Windows-NetworkProfile/Operational log is generated with Event Id 10000. Windows Task Scheduler has the capability to schedule a task to be run whenever any event is triggered.

Here are some screenshots of the task configuration:

Here's the exported XML of the task:

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2021-01-19T23:24:57.7398228</Date>
    <Author>srijan</Author>
    <Description>Currently:
1. Set local DNS
2. Route 192.168.2.0/24 locally</Description>
    <URI>\Network post connect automation</URI>
  </RegistrationInfo>
  <Triggers>
    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription>&lt;QueryList&gt;&lt;Query Id="0" Path="Microsoft-Windows-NetworkProfile/Operational"&gt;&lt;Select Path="Microsoft-Windows-NetworkProfile/Operational"&gt;*[System[Provider[@Name='Microsoft-Windows-NetworkProfile'] and EventID=10000]]&lt;/Select&gt;&lt;/Query&gt;&lt;/QueryList&gt;</Subscription>
      <Delay>PT10S</Delay>
    </EventTrigger>
  </Triggers>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>powershell</Command>
      <Arguments>-File C:\Users\srijan\Apps\network-post-connect.ps1 -WindowStyle Hidden</Arguments>
    </Exec>
  </Actions>
</Task>

I have gotten used to the ease of setting up things like this for Linux, but was pleasantly surprised that it's easy enough for Windows as well. Windows Task Scheduler actually supports a lot of different conditionals for tasks as well. For example, only starting the task if the computer has been idle for some time, or only starting if connected to AC power, etc..

Let me know in the comments if you think there is an easier way, or if you have any improvement suggestions for the above.

Follow me on Twitter