tail -f findings.out

Troubleshooting with MySQL binary logs

MySQL replication has a wide range of uses, including debugging applications writing to the DB being replicated. Once set up, all statements “that update data or potentially could have updated it” are stored in one or more binary logs (MySQL manual on the binary log). So if something happens to your data and your application level logs aren’t cutting it, you can use the binary logs of the underlying DB as a source of information.

Assuming you have binary logging turned on, you first need to know where the binary logs are. The query “show binary logs;” will give you the filenames and sizes, but not the path. If you only set “log-bin” in my.cnf then the binary logs are placed in your data directory by default (find this by running “show variables like ‘datadir’;”). Otherwise they will be wherever you specified with “log-bin = /some/path”. If you passed the value via a command line option, run “ps aux | grep mysql” to view the options passed. If all else fails, run “sudo updatedb && locate mysqld-bin”.

Now how to view these logs? There is in fact a handy utility for just this purpose: mysqlbinlog. Just pass it the name of the log file and it returns the text version. So if you only had a single search you could use:

1
sudo mysqlbinlog /var/lib/mysql/mysqld-bin.000001 | grep foobar

If you had a number of searches you could redirect to a file for processing at your own pace:

1
sudo mysqlbinlog /var/lib/mysql/mysqld-bin.000001 > mysqld-bin.000001_plain.txt

This gives you the ability to search across all of the statements affecting your DB, as well as across all of the data coming in.

Tags: , ,
September 7, 2009 - 10:32 AM No Comments

Watching a fork bomb explode

Fork bombFork bombs are simple pieces of programming logic that can bring down the mightiest of servers in seconds (in fact, the faster the server, the faster it goes down). They are quite easy to protect against, but some operating systems aren’t protected by default. Below I provide an example of how fast an unprotected server is taken down by a fork bomb “going off”, as well as how to protect your own servers.

A fork bomb is simply a running process that forks other running processes, creating an exponential number of processes and rapidly using up all available processes. In other words: a program that spawns so many programs that the system becomes unusable, a denial of service attack. Fork bombs are a species of wabbit, a class of programs utilizing infinite self-replication but that aren’t worms or viruses. This bash implementation is the most famous:

1
:(){ :|:& };:

While fairly hard to read for those uninitiated in the school of arcane Bashery, it is elegant, and I find it quite beautiful. This page explains how it works in some detail. In short, a function is defined that calls itself, pipes its output to another instance of itself, then pushes that child into the background, allowing the function definition to terminate. Once the parent function is then called… the fun begins. In a somewhat less obscure representation:

1
2
3
bomb() {
 bomb | bomb &
}; bomb

There are implementations for many languages listed here. They work when an operating system doesn’t set a limit on how many processes a given user can spawn. To see if a system does have a limit set, run:

1
2
ulimit -u
unlimited

This was on Ubuntu 9.10 Desktop. Checking RHEL 5.2, CentOS 5 and 5.2, and Ubuntu 7.10 Server, there was a limit, on the order of tens of thousands of processes. Not limiting the number appropriately is unwise, for there definitely is a limit on that resource, in direct proportion with the amount of memory on your system. The file /proc/sys/kernel/threads-max provides the default allowed amount based on your physical memory. However, the 2.6.x kernel allows this value to be changed at runtime. For more information, check out TLDP’s section on kernel process management.

The Test

Now for the test. I had a server that needed to be decommissioned. It hadn’t been used for some time, everything needed was pulled off, sensitive things removed. All that was left was to press “Cancel Account” with the hosting company. But why such a boring demise? Instead I decided to test how fast a fork bomb would make the box unusable. I first created a file, “heartbeat”:

1
2
3
4
5
6
#!/bin/bash
while [ 1 ]
    do
        date; uptime
        sleep 1
    done

I would call this script on the remote server from my box. This would show me that it was alive every second, as well as what the load average was on the box. Then I created a fork bomb script, using the more memorable Python implementation:

1
2
3
4
5
#!/usr/bin/env python
import os
print "Bombs away."
while True:
    os.fork()

This too would be called on the remote server from my box. I started the heartbeat in one terminal, then fired off the fork bomb script in another. The result:

fork-bombed-small

In just a few seconds, the server was down. Not just stunned, mind you. On the floor in another dimension. Expiration was so rapid that my heartbeat script didn’t have time to register and return the clearly heightened load average. And I didn’t get to see the print statement output from the fork bomb script. To fix that, I might have changed line three to:

1
2
3
import sys
print "Bombs away."
sys.stdout.flush()

While we’re on the topic of improvements, I should have recorded the kernel version of the server. I believe it was CentOS 5.X, so likely a 2.6.X kernel. Another useful feature would have been to call both of these scripts from one script, first calling heartbeat, sleeping one second, printing an indicator, then calling the fork bomb script. If anyone tries this and gets a more accurate survival time, drop me a comment.

Protection against fork bombs

While the classic fork bomb shown above is something you aren’t likely to encounter without malicious users hanging around, the same sort of situation can arise by means of programming mistakes and poor test coverage as well. So how do you protect a server against this sort of vulnerability? It’s quite simple: set a limit on the number of processes users can initiate at any one time. I ran across several articles stating that to do this you need to edit /etc/security/limits.conf with sudo. This page describes the syntax of the file. The file should also contain an explanation in the comments at the top of the file. You’d likely want to add something similar to the following:

1
*    -    nproc    200

This will limit the number of processes that each user can initiate to 200. The “-” specifies that the value is both “hard” and “soft”. A hard value can’t be overridden with a higher number by the specified user(s), while a soft one is imposed on the user by default. “hard” and “soft” may be used separately if needed. You can also replace the * wildcard with a specific user or group, say if you have an untrusted group of users.

However, on my system (and a few others I looked at) these directives are ignored. To get the limits enforced, I had to add the following to /etc/profile:

1
ulimit -u 300

This limited the number of processes all users logging in would be able to spawn at any one time. It also capped the limit the user could set for himself.

As to selecting the limit amount, you’ll need to do some testing to fit your needs. On my Ubuntu desktop machines at home, I have 45 – 55 running processes that were spawned by my user at any one time. This includes GNOME, Firefox, conky, IM, twitter client, my terminals, etc. Basically a full-featured modern desktop. The machine serving this blog, without a GUI, has far less processes per user on average. So, something on the order of a few hundred processes is the max you’ll likely need to set. To see how many processes are active from your system’s users, you can use the following:

1
2
3
4
5
6
# View number of processes from all users:
sudo ps aux | sort | cut -f1 -d" " | wc -l
# View all unique users with processes
sudo ps aux | sort | cut -f1 -d" " | uniq
# Count of processes started by specified user:
sudo ps -U root | wc -l

You can also test out various limits and their effects via:

1
ulimit -u NUM

This only sets the limit temporarily. Once you log out, it goes back to the default. To make the change permanent, edit /etc/profile as described above. Once you have a limit in place, running a fork bomb will result in an error about resources not being available, instead of a dead box. Here I switch to a test user, set its limit of number of processes, then run a fork bomb:
Fork bomb with ulimit in place
The bomb continues until it reaches the process cap. At this point you can Ctrl+c out, saving your box.

A benefit to placing the limit in /etc/profile instead of limits.conf is that the root user is not subject to the limits placed in limits.conf. A disadvantage is that you lose the granularity of controlling limits per group and user.

Tags: , , ,
June 10, 2009 - 10:24 PM No Comments

mysql_secure_installation: A useful first step in securing a MySQL server

This won’t be an exhaustive post on how to secure a MySQL server. I just want to mention a useful utility packaged with MySQL server: mysql_secure_installation. Simply run it from the command line once MySQL server is installed and running. It will prompt you with a series of questions, resulting in a more secure setup!

It only covers the very basics, but then you don’t need to remember those at least, and save yourself some typing to boot. Here’s what it does:

  • Set a root password. If you already have it, you’ll need to enter it for the remaining steps.
  • Remove anonymous users
  • Disable non-local root access
  • Remove the test database and access rules related to it
  • Reload privilege tables so the above changes are in effect

A quick and easy way to follow good practices.

Tags: , ,
April 18, 2009 - 9:55 AM No Comments

More Tasty Tips for DenyHosts

After living with DenyHosts for some time, I have made some additional configuration changes that are quite helpful.

  1. Whitelist known good IPs
    I found after not very long that my home IP address had been added to my webserver’s hosts.deny file by DenyHosts. This was a result of having messed up my own login a few times. This could easily be an issue if you are regularly connecting to the box running DenyHosts (from work, home, etc).
    A simple solution: Add your IP to an allowed-hosts file in your DenyHosts working directory. Once that is in place, you might also need to find and remove the entry for the IP in question from your hosts.deny file (if it has already been added). It would be a wise choice to add known good IPs to allowed-hosts when you first setup DenyHosts. Otherwise, you might find yourself blocked from your server with no way to get access!
    For more details, see the FAQ.
  2. Blacklist bad users
    It is also a good idea to add users you know are not allowed in a restricted-usernames file in your DenyHosts working directory. This keeps users that should never be allowed to login from trying, even once. There are two handy scripts provided to help in generating this list (located in the scripts dir of your DenyHosts working dir).
    The first, restricted_from_passwd.py, scans your /etc/passwd file and outputs users who have nologin set (print daemons, etc). You can redirect this to your restricted-usernames file.
    The second script is only handy after you have been running for some time. restricted_from_invalid.py, when passed your working dir, prints out the users which bad SSH attempts use most often. By default, it prints the top 10. You could also pass it a large number, say 10000, and it would print all the bad users ever attempted since DenyHosts was first started. I take this list, remove the users I have in place and know to be good, and use it as my restricted-usernames list.
  3. Set the purge threshold and purge rate
    In your denyhosts,cfg file, set PURGE_DENY to something reasonable, 1 – 2 weeks perhaps. Otherwise, your hosts.deny might good huge.
    Then, set the PURGE_THRESHOLD to 2 -3. This means that while entries older than your PURGE_DENY will be removed from the deny list, if it occurs more than your PURGE_THRESHOLD, they will not be purged. This is more likely to stop the entries most often attempted.
  4. Rotate log files
    Assuming you have logrotate installed (and why don’t you?), the FAQ has a great example file for adding a rotate entry for DenyHosts.
  5. Sync with central DenyHosts database
    One feature which really makes DenyHosts shine is the ability to share known rogue agents among DenyHosts instances. It is off by default, but to turn it on, edit denyhosts.cfg in your working dir. To enable synchronization, simply uncomment SYNC_SERVER. The value it is set to should be correct. By default, once synchronization is one, every hour the daemon will check with the server specified. It will upload hosts that you have denied, and download hosts that at least 3 others have denied. If you wish, you can tweak the timing of this process, just to upload, the threshold for downloading, etc.
    For more details, see the FAQ.

After these changes are made, be sure to restart DenyHosts via

1
sudo /usr/share/denyhosts/daemon-control restart

.

Tags: , , ,
October 16, 2007 - 6:57 PM No Comments

DenyHosts: Watches your SSH log for you

While I knew that brute-force attacks on SSH servers are very common, I had not taken the time to look at the connection attempt logs on my home servers until recently. To do that on Ubuntu run:

1
sudo tail -n 100 /var/log/auth.log | grep sshd

I was seeing attempts every few seconds for some periods, mostly on non-standard ports!

So far as I knew, no one had gotten through, but why risk the worry. Instead I installed DenyHosts. DenyHosts is a Python script that watches your auth.log, and adds IPs that repeatedly try and fail to connect to the /etc/hosts.deny list, effectively denying them future access.

It’s rather easy to install. There is a package in the repos, but I was unable to get this to work on my servers for some reason (it is still in testing). [EDIT, 2009-08-03: In setting up a new server running Jaunty, I installed from the repo. It worked without issue.] I instead followed this handy tutorial.

It worked flawlessly, with one exception. I had to run

1
sudo touch /etc/hosts.deny

right before starting the service. Otherwise it threw an error that the file did not exist and closed. With the touch, all went fine. That fix was listed in this bug report.

In addition, while editing /usr/share/denyhosts/denyhosts.cfg according to the tutorial I recommend you also change this line:

1
BLOCK_SERVICE  = sshd

to

1
BLOCK_SERVICE  = ALL

This specifies the port to block per IP denied. Instead of just blocking a potentially malicious IP from SSH access, this blocks them from all other services.

Tags: , , ,
October 15, 2007 - 9:47 PM No Comments

Twitter links powered by Tweet This v1.6.1, a WordPress plugin for Twitter.