Log monitor using socat, rtail, nginx and supervisor
Published: 2015-12-03
Tags:
logging,
monitoring,
socat,
rtail,
supervisor,
nginx
Intro
Working as a developer I find myself spending a significant amount of time
debugging software. And for debugging web backend software the web server logs
can be an incredibly useful tool. For a while I've been monitoring my server
logs using something like tail -F /var/log/(something)/error_log
on the
server. However, this is not ideal as I often end up with a bunch of terminal
tabs monitoring different logs and swearing every time my connection drops and
ssh decides to time out. This could be solved with a combination of autossh
and tmux
, but I wanted to be able to search the logs and filter using regular
expressions. Even more importantly, I wanted to share the power of logs with
my colleagues who might not be as enthusiastic about a bunch of terminal tools.
After finding rtail
I realised it covers most of my requirements. It has a
client which takes input from STDIN and sends it via network to the rtail
server. The server collects the log streams coming from the different clients
and serves all of them via HTTP. It allows users to switch between the streams,
filter using regex and some other useful stuff.
My problem with using strainght rtail
was the lack of security. The authors
say you should use it behind a VPN, but that's not something I wanted to do.
Instead, I decided I'm going to place it behind an nginx
reverse proxy to
handle authentication and use socat
to wrap the UDP packages into an SSL
connection as the logs travel between my servers. I ended up using supervisor
to make all of these into services that are started at boot and restarted in
case of crashes.
Setting up socat
The first thing to get working was socat
. Once I know the connection works I
should be able to get rtail
using it. There's a guide that I followed for
creating an SSL connection: Securing Traffic Between two Socat Instances Using
SSL. The only
difference was that I did not want to use SSL for client authentication. That
allows me to skip having to generate certificates for every client.
First step is generating the server key and certificate:
openssl genrsa -out rtail-server.key 1024
Next create a self-signed certificate:
openssl req -new -key rtail-server.key -x509 -days 3653 -out rtail-server.crt
You can safely ignore all the prompts by hitting enter. The next step is generating the .pem file:
cat rtail-server.key rtail-server.crt >rtail-server.pem
The .key and .pem files must remain secret. It's a good idea to make them only readable by you:
chmod 600 rtail-server.key rtail-server.pem
The .pem file is used on the rtail
server and the .crt is used by every
rtail
client. Make sure you use something secure (like scp
) when moving
the .pem file to the server!
Now we can set up the SSL connection between the two servers and make sure everything works. In my case it did not, turns out there was a firewall blocking the port I wanted to use. After sorting out the firewall everything went smoothly. For the sake of argument let's say that we'll be using port 5333 for the SSL connection.
First you have to set up the server end of the SSL connection:
socat OPENSSL-LISTEN:5333,fork,reuseaddr,cert=/path/to/srv-rtail.pem,verify=0 STDIO
Here we're telling socat
we want it to fork for every new client (which means
we can send multiple streams to it at the same time), to authenticate itself
using the .pem file we generated previously and to not authenticate clients.
We connect the stream to STDIO for debugging purposes, but later it will be
pointing at the local rtail
server. On the client side we run the client version:
socat STDIO OPENSSL:my.rtail.server.com:5333,cafile=/path/to/srv-rtail.crt
We're making socat
listen to the STDIO on the client side (which will later
be listening to the output of rtail
client applications on that server) and
stream it to our rtail
server via SSL. It will be using the .crt file to
authenticate the server. If everything is correct you should be able to type
things in on the client side and see them turn up on the server side.
Setting up rtail
Now we can get rtail
using the SSL connection. If rtail
had an option to
use STDIO we could just pipe it to/from socat
. However, rtail
clients
communicate with the server via UDP packets - we need to make socat
listen to
these packets on the client end and reproduce them on the server end. The only
change to the above socat
setup we replace STDIO with the corresponding UDP
setup. Let's say we're using the port 5700 for rtail
UDP communication. On
the server this becomes:
socat OPENSSL-LISTEN:5333,fork,reuseaddr,cert=/path/to/srv-rtail.pem,verify=0 UDP:localhost:5700
And on the client side:
socat UDP-LISTEN:5700 OPENSSL:my.rtail.server.com:5333,cafile=/path/to/srv-rtail.crt
To set up rtail
we need to run the client pointing it to the 5333 UDP port:
rtail --port 5700 --id my-stream-name
and on the server:
rtail-server --web-port 5700 --udp-port 5700
The id on the client side simply helps identify the stream on the web interface.
At this point if you point your browser to the port 5700 of the rtail
server
you should see the rtail
web interface. Test by typing things into the input
stream of the rtail client - it should show up in the web interface under the
stream called "my-stream-name". If you can't see the web interface or nothing
comes through double check the firewall settings.
You can now pipe your log files into the rtail
client from tail
:
tail -F /some/log/file | rtail --port 5700 --id some-log-file-stream
Seting up supervisor
The first time setting this up I just left all the rtail
and socat
instances
running in my tmux
sessions on various servers. Needless to say that's not
sustainable. The easiest solution I've found was to set up supervisor
to
manage these processes.
On the client side we need to run the socat
instance and as many rtail
clients as we need. For one client the configuration would look something like
this:
[program:rtail-client-socat-relay] command = socat UDP-LISTEN:5700 OPENSSL:my.rtail.server.com:5333,cafile=/path/to/srv-rtail.crt autostart = true autorestart = true user = nobody priority = 900 [program:some-stream-rtail] command = /bin/bash -c "tail -F /some/stream/log | /usr/bin/rtail --port 5700 --id some-stream --mute" killasgroup = true autostart = true autorestart = true user = some_stream_owner
Since socat
needs to authenticate the server make sure the user nobody
has
read access to the certificate. In addition to that, the some_stream_owner
user must have read access to the log file. The priority setting defines the
order in which the processes should be started with the lower-numbered ones
being started first. The default is 999, so by setting the socat
connection
priority to 900 we make it start before rtail
.
On the server side we open the SSL connection and start the rtail
server:
[program:rtail-server] command = /usr/local/bin/rtail-server --web-port 5700 --udp-port 5700 autostart = true autorestart = true user = nobody [program:rtail-server-socat-relay] command = /usr/bin/socat OPENSSL-LISTEN:5333,fork,reuseaddr,cert=/path/to/srv-rtail.pem,verify=0 UDP:localhost:5700 autostart = true autorestart = true user = cert_owner
The cert_owner
must have read access to the certificate. The fork
flag
for socat
makes it fork when new connection request is received, create
a new connection and forward the content to the same UDP port rtail
is
listening on. The verify=0
flag tells socat
that the client certificates
don't need to be verified.
If this does not work first make sure running the same commands manually works,
then check permissions and have a look at the log file produced by supervisor
.
It contains details of which processes have started successfully and can help
debug the configuration.
Setting up nginx
The last part is setting up nginx
as a reverse proxy with simple HTTP
authentication. We tell nginx
about the rtail
web service we have running,
then ask for all requests coming to that domain to be redirected to the rtail
service subject to authentication. Here's a good manual on how to generate the
auth file: How To Set Up HTTP Authentication With Nginx On Ubuntu 12.10.
Below is the configuration I ended up with. There is nothing but the auth file
in the /avr/www/mydomain.com/rtail/
folder.
upstream rtail { server 127.0.0.1:5700; } server { server_name rtail.mydomain.com; auth_basic "My rtail service"; auth_basic_user_file /var/www/mydomain.com/rtail/auth; location / { proxy_pass http://rtail; proxy_redirect off; } }
Final remarks
One thing I'd like to see (and quite possibly get involved in myself) is
creating an option that allows the rtail
client to send its data using
standard output. There's really no need to use the UDP stack if we're just
piping the data through socat
anyway. On the server side the socat
instances could dump their data into a named pipe that the rtail
server would
be listening to. I think this would be a cleaner solution.