Network based message collector with socat - ScienceChronicle
ScienceChronicle
May 30, 2024

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.

  1. Start a new screen session:

    screen -S socat_server
    
  2. Run the socat command within the screen session:

    socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh
    
  3. Detach from the screen session by pressing Ctrl+a then d.

  4. 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.

  1. Start a new tmux session:

    tmux new -s socat_server
    
  2. Run the socat command within the tmux session:

    socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh
    
  3. Detach from the tmux session by pressing Ctrl+b then d.

  4. 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.

  1. Run the socat command with nohup:

    nohup socat -u TCP4-LISTEN:4444,reuseaddr,fork EXEC:./sink2.sh &
    
  2. 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.

  1. 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
    
  2. Reload systemd configuration:

    sudo systemctl daemon-reload
    
  3. Start the service:

    sudo systemctl start socat.service
    
  4. 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:

The most reliable way to get information about the system is to read the following files:

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

Support us

Science Chronicle