Sunday 26 April 2020

Tiny Rsyslog Container Service

Using buildah we can create tiny containers.  Consider a RHEL 7 Rsyslog central logging service in a 164MB container, without doing crazy unsupported stuff.

Why?  Because containers should be:
  • tiny, minimal attack service and resource friendly;
  • easy to rebuild, infrastructure as code;
Environments disconnected from the Internet present challenges to mirror and maintain updated base container images.  The "From Scratch" style of container images means those environments can leverage existing YUM repositories to build and rebuild up-to-date images.

Below is a Bash shell script to build the Rsyslog container for you.  The script includes instructions on how to test and clean up the containers and images afterwards.  It also includes two different sets of "run" commands that:
  • leaves the collected logs inside the container, not very useful but simple.
  • exposes the collected logs through a volume which bind mounts between the container and its host.

#!/bin/bash

# Prerequisits:
#   * RHEL 7 server or similar.  Tested with RHEL 7 Server.
#   * buildah package to build the image.
#   * podman package to test the image.
#   * Run this script as the root user.

# Install the required software on RHEL 7 host.
# ---------------------------------------------
# subscription-manager repos --enable=rhel-7-server-rpms --enable=rhel-7-server-extras-rpms --enable=rhel-7-server-optional-rpms
# yum install buildah podman

# Author: spuddidit
# Date:  24/4/2020

# Default values for arguments.
imageName='spud_rsyslog'
port=5140


Usage () {
  echo "Usage:  $0 [ -h ] [ -n IMAGE_NAME ] [ -p PORT ]"
  echo "Options:"
  echo -e "\t-h\t\tDisplay this help message."
  echo -e "\t-n IMAGE_NAME\tContainer Image name. (Default: $imageName)"
  echo -e "\t-p PORT\t\tPort rsyslog will listen for TCP & UDP. (Default: $port)"
  echo ""
  exit 1
}


# if [ $# -eq 0 ]; then
#   Usage
# fi

while getopts "hn:p:" opt; do
  case ${opt} in
    h )
      Usage
      ;;
    n )
      imageName=$OPTARG
      ;;
    p )
      port=$OPTARG
      ;;
    \? )
      echo "Invalid option: $OPTARG" 1>&2
      ;;
    : )
      echo "Invalid option: $OPTARG requires an argument" 1>&2
      ;;
  esac
done
shift $((OPTIND -1))


echo 'Create a "from scratch" image.'
container=$(buildah from scratch)
echo 'Mount "from scratch" image.'
scratchmnt=$(buildah mount $container)

echo 'Install the packages:'
#echo -e '\tredhat-release'
echo -e '\trsyslog'
#yum install -y --releasever=7 --installroot=$scratchmnt redhat-release
# install_weak_deps option is not supported in RHEL 7???
# --setopt install_weak_deps=false
yum install -y --quiet --releasever=7 --setopt=reposdir=/etc/yum.repos.d \
            --installroot=$scratchmnt --setopt=cachedir=/var/cache/yum \
            --setopt=override_install_langs=en --setopt=tsflags=nodocs \
            rsyslog #redhat-release

echo 'Configure rsyslog service to receive logs from other hosts.'
cat >$scratchmnt/etc/rsyslog.conf <<EOT
\$ModLoad imudp
\$UDPServerRun ${port}

# Provides TCP syslog reception
\$ModLoad imtcp
\$InputTCPServerRun ${port}


\$template RemoteLogs,"/var/log/remote/%fromhost%_%fromhost-ip%_%PROGRAMNAME%.log"
*.* ?RemoteLogs
& ~
EOT


# :source, !isequal, "localhost" -?RemoteLogs
# :source, isequal, "last" ~


echo 'Cleanup inside the container.'
yum clean all -y --installroot $scratchmnt --releasever 7


echo 'Set the start command.'
buildah config --cmd "/usr/sbin/rsyslogd -n" $container
echo "Set listeners on UDP & TCP ports:  ${port}"
buildah config --port ${port}/tcp $container
buildah config --port ${port}/udp $container
echo "Create an image from the build container."
buildah commit --rm $container ${imageName}:latest

echo -e '\nList all images and highlight the new one.'
echo      '------------------------------------------'
podman images | grep --color -e "${imageName}" -e '^'
echo ''

image_id=$(podman images --quiet --filter reference=$imageName)
cat <<EOT
## Start the Logger Container *without* a Volume

    container=\$(podman run -p 5140:5140 -p 5140:5140/udp -d --name spud-syslog $image_id)

## --OR--  Start the Logger Container *with* a Volume

    container=\$(podman run --volume remote_logs:/var/log/remote -p 5140:5140 -p 5140:5140/udp -d --name spud-syslog $image_id)
    logger_dir=\$(podman inspect \$container | grep remote_logs | grep Source | cut -d\" -f4)


## Send a Message to the Containerised System Logger

    logger -n 127.0.0.1 -P 5140 "andrew was here 2."

## ... *without* a volume - Attach to a Container with a Shell and look at logs.

    podman exec -it --latest /bin/bash
    find /var/log/remote -type f -exec cat {} \;
    exit

## ... -OR - *with* a volume - access the logs from the container host.
    find \$logger_dir -type f -ls


## Cleanup
    podman stop --latest
    podman rm --latest
    podman rmi $image_id

EOT