Network based message collector with socat
Posted on May 30, 2024 • 4 minutes • 788 words
Table of contents
Overview
The simplest message collector code can be implemented with socat:
socat -u TCP4-LISTEN:4444,reuseaddr,fork OPEN:/tmp/log.txt,creat,append
It will listen on tcp port 4444, it can accept multiple simultaneous connections which guarantees no connection is refused and it will write the data recieved on the port to /tmp/log.txt, appending to the file if it already exists or creating a new if it does not.
The sender can be implemented in many ways, for example:
using nc:
echo "Hello, World!" | nc -q 0 localhost 4444
The -q 0 makes nc to close the connection after the data is sent.
using socat:
echo "hello world" | socat - TCP4:localhost:4444
using python:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 4444))
s.sendall(b"hello world\n")
s.close()
using /dev/tcp:
echo "Hello, World!" > /dev/tcp/localhost/4444
The later method is delightful as it does not depend on any utility.
Improvement
The above collector can be easily abused. Opened to the external world, anyone can send to it ton of data and fill the hard disk causing denial of service. So we want to implement a simple protection with a keyword.
Let’s create a bash script, sink2.sh:
#!/bin/bash
# This script reads from:
# socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC ./sink2.sh
# Send to socat:
# cat testnfo.txt > /dev/tcp/localhost/4444
input_allowed=0
line_number=0
keyword=RX203
while IFS= read -r line; do
(( line_number++ ))
if [[ $line_number == 1 ]]; then
if [[ $line == $keyword ]]; then
input_allowed=1
fi
fi
if [[ $input_allowed == 1 ]]; then
echo $line_number: $line
fi
done
and run it with socat as
socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh
Now, let’s try to send some data to it:
echo "Hello, World!" > /dev/tcp/localhost/4444
Nothing is printed.
Now lets send a keyword defined as RX203 to the collector:
echo -n "RX203\nHello, World!" > /dev/tcp/localhost/4444
Ok, we got the message.
So, our sink2.sh script checks if the first line of the input equals to keyword and if not, it does not read and does not echo the rest of the input, thus preventig “unauthorized” messages from logging.
Making socat behave as a daemon
To ensure socat runs persistently and behaves like a server, you can use several methods.
1. Using screen
screen allows you to run socat in a detached session that will persist even if you log out.
-
Start a new screen session:
screen -S socat_server -
Run the
socatcommand within the screen session:socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh -
Detach from the screen session by pressing
Ctrl+athend. -
To reattach to the screen session later, use:
screen -r socat_server
2. Using tmux
tmux is similar to screen and allows for managing multiple terminal sessions.
-
Start a new
tmuxsession:tmux new -s socat_server -
Run the
socatcommand within thetmuxsession:socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh -
Detach from the
tmuxsession by pressingCtrl+bthend. -
To reattach to the
tmuxsession later, use:tmux attach -t socat_server
3. Using nohup
nohup allows you to run commands that persist even after you log out.
-
Run the
socatcommand withnohup:nohup socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh & -
Check the output in
nohup.outor redirect it to a file:nohup socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh > socat.log 2>&1 &
4. Using Systemd (for a more robust solution)
Creating a systemd service ensures that socat restarts automatically if it stops and starts on boot.
-
Create a systemd service file (e.g.,
/etc/systemd/system/socat.service):[Unit] Description=Socat TCP server [Service] ExecStart=/usr/bin/socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh Restart=always [Install] WantedBy=multi-user.target -
Reload systemd configuration:
sudo systemctl daemon-reload -
Start the service:
sudo systemctl start socat.service -
Enable the service to start on boot:
sudo systemctl enable socat.service
Gathering system info with minimal tools
Linux may have but may have not installed utilities which provide system information:
lscpu: info about cpuhwinfo: a spectrum of system informationinxi: a spectrum of system informationdmidecode: a spectrum of system informationcpuid: info about cpuuname: info about os version
The most reliable way to get information about the system is to read the following files:
/etc/os-release: precise informatio about OS version/proc/cpuinfo: information about CPU/proc/meminfo: information about RAM
On the other hand, there is no /proc file with similar to df -h output. But df is installed by default on most linux distros.
To create short messages which fit our socat collector, we may process the rich information from the files extracting the most important.
OS version
Compress all line of /etc/os-release to pipe-separated line.
cat /etc/os-release | awk '{ acc=acc "|" $0 } END {print acc}'
CPU info
Extract only model name from the /proc/cpuinfo. All the details can be found by the model.
cat /proc/cpuinfo | grep -i "Model name" | uniq | awk -F': ' '{print $2}'
RAM info
Extract only total RAM available from /proc/meminfo:
cat /proc/meminfo | awk '/MemTotal:/ {print $2}'
Share
Tags
Counters