Search code examples
node.jswindowsmacosdnscommand-line-interface

Programatically set DNS servers (Windows, MacOS)


I need to programmatically set DNS servers of the host on their active network interfaces (Wi-fi, ethernet, etc) on both Windows, MacOS and as a bonus Linux.

I want to avoid having to manually update/pollute /etc/hosts for my Kubernetes services I am running on my ingress.

Currently, my process is to manually set the DNS server for each person in my team running our app

The problem with this is that it's a manual process, and I am having trouble trying to automate it because the outputs are weirdly formatted and hard to parse. This means I am unable to know which is the proper network interface to use.

Essentially, what needs to be done is the following (on both platforms)

  1. Get the active networks name
  2. Set the DNS servers for the active network to 127.0.0.1 & 8.8.4.4

What is being done manually currently


MacOS:

networksetup -setdnsservers Wi-Fi 127.0.0.1 8.8.8.8
sudo killall -HUP mDNSResponder
  • 127.0.0.1 is the local DNS server running on node that serves the A record for the service
  • 8.8.8.8 is Google's Public DNS Server

Currently, I am assuming the user on MacOS is using the "Wi-Fi" network, but i'd like to determine this programatically


Windows As administrator:

netsh interface show interface

Locate the network connection for which you want the DNS server changed (eg: WiFi).

netsh interface ipv4 add dns "WiFi" 127.0.0.1 index=1
netsh interface ipv4 add dns "WiFi" 8.8.8.8 index=2
ipconfig /flushdns

Solution

  • On macOS, I don't think this will do what you want. When you configure multiple DNS servers on macOS, the system resolver doesn't try them in order, it just fires off requests semi-randomly between the available servers. This means it'll sometimes send off requests for your private servers to the public (Google) server, get told there's no such domain, and stop there. Or it'll send requests for pubic sites to the localhost DNS, and if that doesn't respond properly decide that site doesn't work. Basically, the macOS resolver doesn't do failover.

    Are your private servers under a non-standard TLD or something like that? If so, you might be able to do the job by adding a file under /etc/resolver/ to redirect queries for that TLD to the private DNS server.

    Anyway, in case it is useful, here's a way to detect the primary (active) network interface and set its DNS servers in macOS:

    #!/bin/bash
    
    interfaceDevice=$(netstat -rn | awk '($1 == "default") {print $6; exit}')
    if [[ -z "$interfaceDevice" ]]; then
        echo "Unable to get primary network interface device" >&2
        exit 1
    fi
    
    interfaceName=$(networksetup -listallhardwareports | grep -B1 "Device: $interfaceDevice\$" | sed -n 's/^Hardware Port: //p')
    if [[ -z "$interfaceName" ]]; then
        echo "Unable to get primary network interface name" >&2
        exit 1
    fi
    
    networksetup -setdnsservers "$interfaceName" 127.0.0.1 8.8.8.8