-2

I am making a network-connected device (using Raspberry Pi 4 inside), and I would like a way for these devices to discover each other (when on the same local subnet). I am vaguely aware of protocols like SSDP / UPnP which can help with things like this, but the complexity and security concerns lead me to believe that that is not the best option.


Update: The feedback I have gotten so far suggests that mDNS & DNS-SD, implemented in existing software like avahi, are far preferable tools for this job. In the process of learning about these, I am finding the following links helpful (if you have more please add them in an answer or comment!):


All of the devices that I am making are running an HTTPS server (nginx) listening on tcp/443 and configured with a (legitimate) certificate valid for hostnames like www.example.com and *.example.com (where example.com is replaced with my own domain name).

The way I am currently trying to support this peer discovery feature is like this:

  1. User connects to web interface of one device (e.g. by knowing the IP a priori)
  2. User triggers "discover peers" feature (e.g. HTTP/WebSockets request)
  3. Device uses nmap's ssl-cert script to scan to identify peers like this:
    nmap --script ssl-cert --script-args=tls.servername=www.example.com <local network>
    
  4. Output is collected, parsed, and filtered then resulting IP address list is displayed to the user.

First off, are there alternative ways of doing this that might be "better" (less "invasive" / noisy, more reliable, etc.)?

Secondly, the output from the nmap command listed in step 3 is hard to parse. It looks something like this (targeting known test server here):

# nmap -p 443 --script ssl-cert --script-args=tls.servername=www.example.com 192.168.1.40
Starting Nmap 7.80 ( https://nmap.org ) at 2023-03-08 13:20 CST
Nmap scan report for 192.168.1.40
Host is up (0.00017s latency).

PORT    STATE SERVICE
443/tcp open  https
| ssl-cert: Subject: commonName=*.example.com
| Subject Alternative Name: DNS:*.example.com, DNS:example.com
| Issuer: commonName=...
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-06-08T00:00:00
| Not valid after:  2023-07-09T23:59:59
| MD5:   ...
|_SHA-1: ...

Nmap done: 1 IP address (1 host up) scanned in 0.90 seconds

If I enable -oG for greppable output it seems that the certificate information gets omitted. (If there is a way to change that I would like to know about it.) So instead I tried piping to grep with the "lines before" option like nmap ...|grep -B 5 "ssl-cert: Subject: commonName=\*.example.com"| and then grepping again and using awk to select only the hostname/IP address. The result is like this:

#!/bin/bash

NMAP=/usr/bin/nmap
HOSTNAME="www.example.com"
TCP_PORT_FOR_HTTPS=443
DESIRED_COMMON_NAME="\*.example.com"
TARGET="192.168.1.30-100"

# Attempt TLS handshake with each host specified by TARGET, using SNI to request HOSTNAME
# Filter results for hosts with certificates matching DESIRED_COMMON_NAME
RESULTS=$($NMAP -p $TCP_PORT_FOR_HTTPS --script ssl-cert --script-args=tls.servername=$HOSTNAME $TARGET \
    |grep -B 5 "Subject: commonName=$DESIRED_COMMON_NAME" \
    |grep "Nmap scan report for" \
    |awk '{split($0, array, "Nmap scan report for "); print "\x27"array[2]"\x27"}' \
)
# Output as JSON array: "'x.x.x.x' 'y.y.y.y'" --> "['x.x.x.x','y.y.y.y']"
echo "[$(echo -n $RESULTS|tr ' ' ,)]"

# Example output: ['192.168.1.37','192.168.1.43','192.168.1.40']

Since I was just trying to get this to work I suspect there is a better / more elegant approach to this out there. Would anyone be willing to provide feedback, suggestions, etc.?

jacobq
  • 132
  • 4
  • Please help me understand how to improve this question. I put a significant amount of time and effort into it and thought it was clear. Too wordy? Too ignorant of service discovery technologies? – jacobq Mar 10 '23 at 03:24
  • It's perfectly acceptable to answer your own question (there is even a badge for it), in this case it probably would be better than editing it in such a way to dramatically change it's nature. – hardillb Mar 12 '23 at 08:43
  • I do not think that my edit changed the nature of the question. I am trying to solve exactly the same problem and so far have two candidate solutions but am not thrilled with either of them. However, I will consider your suggestion of writing up an answer if a more helpful one doesn't get posted soon. – jacobq Mar 13 '23 at 02:38

1 Answers1

0

Just use ssdp/mdns to discover, then try and connect to check the certificate exists and verifies

hardillb
  • 1,552
  • 2
  • 12
  • 23
  • If I understand correctly, this requires setting up another service (SSDP/MDNS) on each device and adding a firewall rule to allow it in, correct? And the advantage is that it is more efficient (multi-cast vs. brute-force unicast scan) and might able to span multiple subnets. Is that right? – jacobq Mar 08 '23 at 20:37
  • 1) why reinvent the wheel 2) anything brute force scanning the network is likely to get flagged as hostile on anything other than a home lan – hardillb Mar 08 '23 at 22:11
  • 1) to use existing firewall configuration and available utilities (already have nmap available on the image) and not need to learn something new (have never used these other discovery tools though am open to trying), 2) yes, though showing the user a warning first will be sufficient in this case, especially since this "scanning" is equivalent to opening a bunch of web browser tabs. – jacobq Mar 08 '23 at 22:16
  • Can you share more information about how to do what you describe? I am exploring using `avahi` for the first time, which seems like it might do this, but I've never used it before. There seem to be many related technologies (e.g. zeroconf, Bonjour, mDNS, DNS-SD, and RFC 3927/IPv4LL), but it's a lot to take in at once, and I'm not sure where to begin. – jacobq Mar 10 '23 at 03:21