Solved Shell Script IPC/RPC Messaging Between Different Hosts?

I would like one of my hosts to broadcast occasional periodic signals to a shell script running on other host(s) on the same LAN.

I'm currently reading about various IoT protocols, but I'm wondering what the more traditional approaches were for this kind of messaging.

Ideally, I'd want some kind of key verification to detect and ignore spoofed messages, but perhaps that's not critical for a home network.

I suppose I could just let the "sender" log in over SSH and run a restricted set of commands directly on the "receiver," but are there other ways to do this?
 
There's probably a lot of ways to do this, and I don't know about the traditional way.

I'd probably just use an http listener (e.g. Tcl httpd https://www.tcl-lang.org/software/tclhttpd/ or Python or whatever equivalent) on the main server, and then get the clients to send http requests to it as simply as possible (e.g. curl or wget or whatever.)

On the server I could check the connecting IPs match my expected clients (or if on a LAN, any local IP).

But http etc probably a fairly "heavy" way of doing this (quick to set-up, though!), so could use your own protocol, but then more gubbins to deal with.
 
I use ssh commands for the writing part of it: ssh to the other machine to run a small script, which leaves information in a file.

For reading, have the other machine keep the information in a web server, and read it with wget or curl.
 
A very simple tool provided by the base FreeBSD install is nc (NetCat).

To listen:

nc -l 1987

To send:

nc myhost 1987

So just listen in a loop for messages and that output can be redirected into shell script variables, etc.

Code:
MYVAR="$(nc -l 1987)"

If you want more security, then you can pipe things through OpenSSL (I think OpenSSL even provides an equivalent of netcat). However then SSH is a pretty nice choice (and again, in the base system so no pesky dependencies).
 
I think that the best approach depends a lot on how much custom code you need to execute when dealing with the "signal".

As others have suggested, ssh(1) and nc(1) are great building blocks.

I find perl(1) a powerful and easy tool for writing quick network connected tools. There's plenty of examples if you google "perl socket programming".

Another approach I use to achieve a specific action is ssh(1) login using a "one shot" key, e.g. on my FreeBSD host ritz:
Code:
[ritz.998] # cat /home/shutdown/.ssh/authorized_keys
# This login used in conjunction with the CyberGuard pwrstatd daemon on orac
# which activates /etc/pwrstatd-powerfail.sh and /etc/pwrstatd-lowbatt.sh.
# The user "shutdown@orac" may use the ssh key below to initiate shutdown.
# To shutdown the host ritz form orac: "sudo -u shutdown ssh -n ritz"
# The user shutdown on this host must have sudo with no password required.
command="/usr/local/bin/sudo /sbin/shutdown -p now UPS powerfail",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHIN0FbajCxIgGnVij7P7tR1HNv7jSXJLp3JQOSVRBFj shutdown@orac
Every host on my network has a "shutdown" account, and on the host that runs the CyberGuard pwrstatd daemon, orac:
Code:
[orac.19628] # cat ~shutdown/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHIN0FbajCxIgGnVij7P7tR1HNv7jSXJLp3JQOSVRBFj shutdown@orac

[orac.19631] # cat /etc/pwrstatd-shutdown_peers.sh
#!/bin/sh

# The UPS has raised an alarm (low battery or input power fail).
# Only physical systems sherman (ZFS storage), orac (KVM Virtualisation),
# pi3b, pi4, and the network switches that connect them have UPS power.
# There are also a number of virtual clients on orac (e.g. ritz).
# It's likely that everything on the network not connected to the UPS
# is already dead.  So we take care to avoid hanging.
# The zfs (NFS and iSCSI) server sherman should be last on the PEERS list.
# But, if you put orac on the PEERS list, make sure it's last.
# If you don't put orac in the PEERS list, pwrstatd will shut it down.

PATH="/sbin:/bin:/usr/sbin:/usr/bin"
export PATH

PEERS="ritz fable mith sherman"

logtag=`basename $0`
for host in $PEERS
do
    ping -c1 -W1 $host
    if [ $? -ne 0 ]
    then
        logger -p 'local0.notice' -t "$logtag" "UPS alert: skipping $host (not up)"
    else
        logger -p 'local0.notice' -t "$logtag" "UPS alert: shutting down $host"
        sudo -u shutdown ssh -n shutdown@$host
    fi
done >/dev/null 2>&1
 
Thanks for the advice, everybody:
  • ssh with a dedicated user for simple one-shot events
  • nc over an ssh tunnel for more continuous event streams
  • curl to poll data from a simple web server if needed
  • write something in a better language for more complex needs
 
Thanks for the advice, everybody:
  • ssh with a dedicated user for simple one-shot events
  • nc over an ssh tunnel for more continuous event streams
  • curl to poll data from a simple web server if needed
  • write something in a better language for more complex needs
Oh, one more to add could be to leverage inetd. It basically runs any program (C, awk, sh, etc) but redirects stdin, stdout to a network stream. So you could write your program / test it via the command prompt and then hook it up to inetd to listen / respond to events.

Again, in the base system. Most UNIX-like platforms provide this.
 
Other traditional approaches: mq, mqtt, remote syslog, and so on. The list gets long after a while.
One of the many monitoring systems (you know, an "agent" on the client that reports data periodically to a central server) - there are many of those too.
For configuration management: ansible, cfengine, chef, puppet, and on and on.

It all depends on what you want to do.
 
In case it's useful for someone else, here's a toy script (insecure and single-threaded) using only nc that serves an HTTP page and collects a string from the client:

Bash:
#!/bin/sh

NEWLINE="
"

read_heredoc() {
  read_heredoc_result=""
  while IFS="${NEWLINE}" read -r read_heredoc_line; do
    read_heredoc_result="${read_heredoc_result}${read_heredoc_line}${NEWLINE}"
  done
  eval "$1"'=${read_heredoc_result}'
}

read_heredoc PAGE_STR <<\HEREDOC
<html>
  <head>
    <title>Server</title>
    <link rel="icon" href="data:,">
  </head>
  <body>
    <form action="/">
      <label for="message">Send message to server:</label><br>
      <input type="text" id="message" name="message" value="Hello"><br>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>
HEREDOC

respond() {
  printf "HTTP/1.0 200 OK\r\nContent-Length: %s\r\n\r\n%s" "${#1}" "${1}"
}

request_handler() {
  message=""
  while read -r line; do
    echo "${line}"
    if echo "${line}" | grep -Eq "^GET \/(.*) HTTP.+$"; then
      message=$(echo "${line}" | sed -Enr "s/^GET \/.*message=([^&]*).* HTTP.+$/\1/p")
    fi
    [ "${#line}" -le 1 ] && break
  done
  if [ "${message}" = "" ]; then
    respond "${PAGE_STR}" >"${1}"
    exit 1
  else
    echo "Client sent message: ${message}"
    body="<body>Server says it received message: <strong>${message}</strong></body>"
    respond "${body}" >"${1}"
    exit 0
  fi
}

response_fifo="$(mktemp -u)"
mkfifo -m 600 "${response_fifo}"
while true; do
  echo "Listening for HTTP on port 8080..."
  cat "${response_fifo}" | nc -l 8080 | request_handler "${response_fifo}" && break
done
trap 'rm ${response_fifo}' EXIT INT TERM HUP
 
Back
Top