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
socat
command within the screen session:socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh
-
Detach from the screen session by pressing
Ctrl+a
thend
. -
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
tmux
session:tmux new -s socat_server
-
Run the
socat
command within thetmux
session:socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh
-
Detach from the
tmux
session by pressingCtrl+b
thend
. -
To reattach to the
tmux
session 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
socat
command withnohup
:nohup socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh &
-
Check the output in
nohup.out
or 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