Introduction

I often deploy and test distributed services on an idle local server, but network issues add significant learning costs. To solve this, I’ve directed all VM or Docker proxies on my local server to my macOS host, which helps avoid many service deployment failures caused by network issues!

However, a problem arises when my macOS connects to the internet through a shared office Wi-Fi with a DHCP-assigned IP address. Setting it manually could cause conflicts, so I wanted to use Consul’s service registration to dynamically register Surge’s HTTP Proxy service.

Surge Script

Add the following settings to your Surge configuration file:

[Script]
consul = type=event,event-name=network-changed,debug=1,script-path=network-changed.js

After saving the configuration file and reloading Surge, you can see the following settings in the UI:

Script Config Script Editor

Surge can modify requests or listen to events through Scripts, then send requests. Surge supports the following script trigger modes:

  • HTTP Request
  • HTTP Response
  • Rule
  • Event
  • DNS
  • Cron
  • Generic

I’m using the Event mode here. Surge exposes two types of events:

  • network-changed
  • notification

When the network changes, the network-changed event is triggered, which then calls my defined Script.

$httpClient.put({
	url: "http://consul.service.betterde.consul:8500/v1/agent/service/register",
	body: {
        "id": "surge-http",
        "name": "surge",
        "port": 6152,
        "address": $network.v4.primaryAddress,
        "check": {
            "Name": "Surge HTTP proxy service check",
            "TCP": $network.v4.primaryAddress + ":6152",
            "Interval": "60s"
        }
    }
}, function(error, response, data) {
    if (error) {
        $done(error);
    } else {
        $done({response});
    }
});

In the script, Surge exposes some global variables. To get the internal IP, you can use $network.v4.primaryAddress, and then use Surge’s built-in $httpClient to register the service with Consul.

Surge Proxy service

Server Configuration

sudo mkdir -p /etc/systemd/resolved.conf.d
sudo vim /etc/systemd/resolved.conf.d/consul.conf

Write the following configuration to the /etc/systemd/resolved.conf.d/consul.conf configuration file:

[Resolve]
DNS=127.0.0.1:8600
DNSSEC=false
Domains=~consul

Note: Since my Consul is installed on a virtual machine, the address here is the local Loopback IP.

Then restart the DNS resolution service:

sudo systemctl restart systemd-resolved

Verifying Results

dig surge.service.betterde.consul

; <<>> DiG 9.18.1-1ubuntu1.3-Ubuntu <<>> surge.service.betterde.consul
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18152
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;surge.service.betterde.consul.	IN	A

;; ANSWER SECTION:
surge.service.betterde.consul. 0 IN	A	10.192.6.137

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Thu Apr 13 13:40:02 CST 2023
;; MSG SIZE  rcvd: 74

As you can see, the resolved IP is indeed my current macOS IP. This way, I can use surge.service.betterde.consul as the proxy server address in Docker or environment variables, instead of the dynamic IP of macOS.

I hope this is helpful, Happy hacking…