Picture of Jürgen Kreileder

Archive for the ‘Security’ Category

Updated MySQL Chroot Script

Debian’s latest MySQL packages are compiled with --with-mysqld-ldflags = -all-static.

That means libc.so.6 is linked statically now. But glibc’s getpwnam and getpwuid implementations still need the shared libraries. The needed libraries must be copied into the chroot because mysqld calls those functions after calling chroot. I’ve updated the mysql-chroot script accordingly.
(The rest of the chroot setup procedure still works as described in Chrooting MySQL on Debian.)

By the way, I’ve filed a wishlist bug at Debian’s BTS (#299265). mysqld should do all /etc/passwd lookups before calling chroot. That way chrooting would work without $CHROOT/etc/passwd and with copying any libraries into the chroot. That’s how Apache and Bind 9 do it.

March 17th, 2005: Debian has removed the -all-static flag again. I’m leaving the additional bits in the chroot script however, just in case the maintainers decide to add the flag again.

PHP Error Logging to syslog from a chroot

Here’s a little trick to log PHP errors to syslog from an apache chroot. Instead of creating a $CHROOT/dev/log socket in the chroot and configuring syslog to listen on that, just define a bogus virtual host that logs to syslog.

<VirtualHost 127.0.0.2:80>
        ServerName JustForOpeningSyslog
        Redirect permanent / http://127.0.0.1/
        ErrorLog syslog
</VirtualHost>

Now apache calls openlog(3) with LOG_NDELAY before being chrooted by libapache2-mod-chroot, and libapache2-mod-php4’s syslog(3) calls work just fine.
(Idea stolen from syslog(3) and chroot(2).)

Chrooting MySQL on Debian

It’s quite easy to chroot bind9 and apache on Debian. (See this page for bind9 and libapache2-mod-chroot or libapache2-mod-security for apache.)

But I’ve found no guide for chrooting MySQL, so here’s my short recipe:

  • Prepare the chroot directory. It’s recommended to use an extra partition/filesystem for it. I will use /srv/mysql (which is an LVM2 partition with an ext3 filesystem on my system) for the rest of the text.
  • Stop MySQL:
    /etc/init.d/mysql stop
  • Copy the databases to new location:
    mkdir -p /srv/mysql/var/lib
    cp -a /var/lib/mysql /srv/mysql/var/lib
  • Copy this script to /etc/default/mysql-chroot
  • Edit /etc/init.d/mysql:
    • Source the mysql-chroot script somewhere at the top:
      …
      test -x /usr/sbin/mysqld || exit 0
      
      . /etc/default/mysql-chroot
      
      SELF=$(cd $(dirname $0); pwd -P)/$(basename $0)
      …
    • Run setup_chroot right in the start section:
      …
      if mysqld_status check_alive nowarn; then
        echo "...already running."
      else
        setup_chroot
        /usr/bin/mysqld_safe > /dev/null 2>&1 &
      …
    • Somehow /var/run/mysqld/mysqld.pid disappears after each start. We have to create it each time, otherwise the stop command won’t work properly:
      …
      if mysqld_status check_alive warn; then
        echo "."
        ln -sf $CHROOT_DIR/var/run/mysqld/mysqld.pid \
                       /var/run/mysqld
        # Now start mysqlcheck or whatever the admin wants.
        /etc/mysql/debian-start
      …
  • In /etc/mysql/debian.cnf, change the socket line to:
    socket = /srv/mysql/var/run/mysqld/mysqld.sock
  • In /etc/mysql/my.cnf:
    • Change the socket line in the [client] section to:
      socket = /srv/mysql/var/run/mysqld/mysqld.sock

      Don’t change the socket lines in the other sections!

    • Add
      chroot = /srv/mysql

      to the [mysqld] section.

  • Prepend /srv/mysql to the log files listed in /etc/logrotate.d/mysql-server
  • Start MySQL:
    /etc/init.d/mysql start
  • Check /var/log/syslog for errors ;-)

March 13th, 2005: I’ve updated the script for newer Debian packages, see Updated MySQL Chroot Script for more information.

July 30th, 2006: These modifications still work fine on the current stable Debian release (3.1, “sarge”). The mysql packages in the testing (“etch”) and unstable (“sid”) distributions of Debian need a few additional changes, I’ll post an updated guide in a few days.

December 30th, 2006: I’ve made an updated guide on how to chroot more recent MySQL packages on Debian and Ubuntu

Mitigating SSH Brute Force Attacks with ipt_recent

As my SSH server only accepts public key based authentication, I’m not really worried about brute force password attacks. But these scans tend to clobber my auth.log. So after some discussion with Andrew Pollock, I’ve written a few custom actions for my shorewall setup. They use the ipt_recent module which allows to track seen IP addresses and match against them using some criteria.

The Limit action can be used to limit accepted connections per IP and timeframe. The hardcoded limit currently is 6 connections per 60 seconds. If an IP tries to connect more often, the attempts will be DROPed.

The Whitelist action provides some simple port-knocking whitelist. If you know the WHITELIST_PORT and can lift the limits imposed by the Limit action for your IP and 60 seconds by connecting to that port.

Here’s how you can integrate those two actions:

  • Create two empty files:
    • shorewall/action.Limit
    • shorewall/action.Whitelist
  • Copy Limit and Whitelist to the shorewall directory
  • Add Limit and Whitelist to shorewall/actions
  • Set WHITELIST_PORT in shorewall/params
  • Use Limit in shorewall/rules, for instance:
    Limit:ULOG:SSH    net  fw  tcp  ssh
    Limit:ULOG:IMAP   net  fw  tcp  imap,imaps
    

    Note: You must use the <action>:<log>:<tag> format for the rules. Limit uses the <tag> for the ipt_recent table name.

  • Optionally add a Whitelist rule:
    Whitelist:ULOG    net  fw
    

If you’re running OpenSSH 3.9 or later, you additionally might want to set MaxAuthTries to 1 (see sshd_config(5)).

May 9th, 2005: I have found a bug in the ipt_recent module, see this article for more information and a fix.