UDP-accessible update-SRV-resource-record API

Continuing the discussion from SRV Resource Record supported?:
It’s just a crazy idea but a feature to update SRV Resource records using some UDP-based protocol would enable quite elegant NAT hole punching for custom services.

  • The DDNS manager server would need to get information about the affected SRV resource record (Name, Priority, Weight) and the received UDP datagram’s sender port number. The [Server] (target hostname) field could either be supplied by the client or deduced from the sender’s IP address (reverse lookup - could cause problems if multiple hostnames have the same IP address assigned).
  • The datagram’s content either has to be digitally signed somehow or a more complex handshake and challenge-response algorithm could be used that would need retransmission logic and so on.

Personally I do have some services in mind where this could be useful but I can live without this feature too.

That’s a very interesting approach. The open UDP port could be used for OpenVPN or WireGuard. If we want to use asymetric crypto to sign the packets, we could use the already supported ed25519 keys.

We could send UDP packets with JSON data followed by a signature to a known port.

{
  "recordID": 123,
  "timestamp": 1597406942,
  "nonce": 94952458,
  "key": "AAAAC3NzaC1lZDI1NTE5AAAAIAMKzDsw5g6jZ/o293bCI5P6MMr/ZKY30eeEVgS1DwLX"
}

If you have a service bound to a UDP port, you need to use raw sockets to send update packets with that source port.

1 Like

@ju93 For which operating system do you need a client? Linux, BSD (like Mac), or Windows?

All of my services are hosted on Linux machines.
I could create my own updater client - just tell me the preferred programming language and I will publish the source code. AFAIK Python should be suitable.

The server component seems to be functional so far. The payload is JSON with the following structure:

{
  "fqdn": "_sip._tcp.example.com",
  "priority": 10,
  "weight": 1,
  "ts": 12345678,
  "key": "AAAAC3NzaC1lZDI1NTE5AAAAIAMKzDsw5g6jZ/o293bCI5P6MMr/ZKY30eeEVgS1DwLX"
}

followed by a null byte followed by the binary signature. I’ll put my test code in a go binary.

1 Like

I’ve uploaded the client code to Github and also built binaries: https://github.com/dynv6/srv-updater
Happy testing!

1 Like

Sucessfully tested two main cases and an accidential negative-case:

  • Using a key where the public key is not registered for SSH access fails as expected.
  • Using a key that is registered works.
  • Providing the wrong “weight” parameter results in not updating any SRV resource record as expected.

I didn’t watch the actual network traffic. My NAT somehow mapped the ports 1:1 so I couldn’t get any port numbers on the other end different from what I provided as parameter.

Time To Live in my NAT table is 60 seconds only, regardless of -interval parameter.

Pitfalls:
srv-updater doesn’t work on Raspbian Buster (golang version 1.11 doesn’t know about “os.UserHomeDir”) without manually installing a more recent version of go.

Thanks for your feedback.

Most NAT implementations try to keep port number. In a test with cellular network and tethering the public port number was different.

The default timeout in Linux for UDP connection tracking is 30 seconds (/proc/sys/net/netfilter/nf_conntrack_udp_timeout). In that case we should change the default transmission interval to a little bit less (25 seconds?).

You’re right - os.UserHomeDir was introduced in Go 1.12. I think it’s fine to follow the Go release policy and only support 1.14 and 1.15:

Each major Go release is supported until there are two newer major releases.

1 Like