Running Bro on live network traffic in a Docker container

We'll begin with a Dockerfile from which to build an image container Bro and its dependencies. The instructions in the file will tell Docker to build and install Bro from source. We're using the less secure method of setuid for the bro binaries to avoid compatibility issues with some storage backends like AUFS which doesn't support filesystem capabilities(7).

# Bro Sandbox - Bro 2.4.1
#
# VERSION               1.0
FROM      debian
MAINTAINER Jon Schipp <jonschipp@gmail.com>

# Metadata
LABEL program=bro

# Specify container username e.g. training, demo
ENV VIRTUSER bro
# Specify program
ENV PROG bro
# Specify source extension
ENV EXT tar.gz
# Specify Bro version to download and install (e.g. bro-2.3.1, bro-2.4)
ENV VERS 2.4.1
# Install directory
ENV PREFIX /opt/bro
# Path should include prefix
ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PREFIX/bin

# Install dependencies
RUN apt-get update -qq
RUN apt-get install -yq build-essential cmake make gcc g++ flex bison libpcap-dev libgeoip-dev libssl-dev python-dev zlib1g-dev libmagic-dev swig2.0 ca-certificates supervisor --no-install-recommends

# Compile and install bro
USER $VIRTUSER
WORKDIR /home/$VIRTUSER
RUN wget --no-check-certificate https://www.bro.org/downloads/release/$PROG-$VERS.$EXT && tar -xzf $PROG-$VERS.$EXT
WORKDIR /home/$VIRTUSER/$PROG-$VERS
RUN ./configure --prefix=$PREFIX && make
USER root
RUN make install
RUN chmod u+s $PREFIX/bin/$PROG
RUN chmod u+s $PREFIX/bin/broctl
RUN chmod u+s $PREFIX/bin/capstats

# Supervisord
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Cleanup
RUN rm -rf /home/$VIRTUSER/$PROG-$VERS

# Environment
WORKDIR /home/$VIRTUSER
USER root
CMD ["/usr/bin/supervisord","-c","/etc/supervisor/supervisord.conf"]

The environment section from above shows that processes in the container will run as root and that PID 1 (CMD) will be a small init system called supervisord. Supervisord is needed to launch and keep track of Broctl otherwise the container would exit after broctl launches bro because broctl would not have a parent. For this to work we need to include supervisord.conf in the same directory as our Dockerfile so it gets copied into the image we are about to build by the ADD instruction present in the Dockerfile.

[supervisord]
nodaemon=true

[program:bro]
command=/opt/bro/bin/broctl deploy

Let's build the image and then run Bro in a container on live network traffic where the logs are kept persistent by mounting a directory from the host filesystem into the container. I'm also mounting the bro configuration directory which contains configuration files that you may want to edit such as node.cfg, networks.cfg, and broctl.cfg. You can create and mount as many directories as you would like, consider spool for current logs and share/bro/site for local bro configuration.

$ docker build -t bro:2.4.1 .
$ mkdir -p /opt/bro/{logs,etc}
$ docker run --name bro -d --net=host -v /opt/bro/logs:/opt/bro/logs -v /opt/bro/etc:/opt/bro/etc bro:2.4.1

From the commands above we created a Docker image from the Dockerfile and instantiated a container from that image which runs in the background (-d) and uses the host's networking stack (--net=host) to use the available physical interfaces. We're also mounting volumes (-v) as mentioned before where the syntax is $host-volume:$container-volume.

Upon completion verify that Bro is generating logs on the host filesystem and then stop the container so we can have our init system manage the process. Note that you can enter the container while it is running to check on things and modify your configuration with something like

docker exec -it bro:2.4.1 bash

Since we're mounting the logs directory to the host we will check that directory for the presence of Bro logs.

$ ls -l /opt/bro/logs/2015-10-06/
total 10996
-rw-r--r-- 1 root root    319 Oct  6 03:27 communication.03:26:26-03:26:58.log.gz
-rw-r--r-- 1 root root    337 Oct  6 04:00 communication.03:27:01-04:00:00.log.gz
-rw-r--r-- 1 root root 123491 Oct  6 01:00 conn.00:00:00-01:00:00.log.gz
-rw-r--r-- 1 root root 157441 Oct  6 02:00 conn.01:00:00-02:00:00.log.gz
-rw-r--r-- 1 root root 212706 Oct  6 03:00 conn.02:00:00-03:00:00.log.gz
...
$ docker stop bro:2.4.1

The init system example below uses Upstart to manage the container process

description "Bro container"
author "Jon Schipp"
start on filesystem and started docker
stop on runlevel [!2345]
respawn
script
  /usr/bin/docker start -a bro
end script

Now we can use the familiar service commands to manage the container.

start bro
bro start/running, process 32425
$ status bro
bro start/running, process 32425