Picture of Jürgen Kreileder

Archive for the ‘Debian’ Category

cyrus_sasl patch for Exim 4

The Exim 4 source code supports authentication with SASL since version 4.43. Debian started enabling this feature in exim4_4.50-2. After I’ve had upgraded to that version and replaced my saslauthd authenticators with brand-new cyrus_sasl authenticators, I’ve noticed that auth.log got flooded with entries like ‘exim4: OTP unavailable because can't read/write key database /etc/opiekeys: No such file or directory.’

My exim configuration uses three different cyrus_sasl authenticators and each exim invocation resulted in three of these OTP warnings because exim calls sasl_listmech() for each configured authenticator. It doesn’t specify a limiting mech_list, that means SASL will test which of all installed mechs actually can be used for authentication. Debian’s SASL package includes libotp.so, so it also tries to use OTP which is not configured on my system.

There are two ways to get rid off the warnings:

  • Remove /usr/lib/sasl2/libotp.*. You’ll have to do this after each upgrade of the libsasl2-modules package.
  • Rebuild exim with this patch. The patch specifies a limiting mech_list option for SASL. This limits sasl_listmech() to the mechs used in the exim configuration. Other mechs won’t be tried anymore.

May 3rd, 2005: A slightly modified version of the patch has been integrated into Exim CVS and will be included in the next Debian release of exim4 (see Debian bug #299743)

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.

        ServerName JustForOpeningSyslog
        Redirect permanent /
        ErrorLog syslog

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."
        /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 \
        # Now start mysqlcheck or whatever the admin wants.
  • 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

Exim 4 and Dynamic IP-Addresses

I’ve recently changed my network connection at home to a provider which assigns dynamic addresses. Exim always provided a broken HELO/EHLO name to my smarthost since then because my externally visible hostname changes each time I connect. I’m now using Exim’s Perl interface to lookup the assigned hostname when connecting my smarthost:

  • /etc/exim4/exim.pl:
    Don’t forget to change ppp0 to the interface you want to handle!

    #! /usr/bin/perl
    # Requires libio-interface-perl
    use strict;
    use IO::Socket;
    use IO::Interface;
    sub get_remote_helo_data()
        my $s = IO::Socket::INET->new(Proto => 'udp');
        my $addr = inet_aton($s->if_addr('ppp0'));
        my $hostname = gethostbyaddr($addr, AF_INET);
        $hostname = '' if (!$hostname);
        return $hostname;
  • /etc/exim4/conf.d/main/50_exim4-localconfig_perl:
    perl_at_start = true
    perl_startup = do '/etc/exim4/exim.pl'
  • Add the following code to the appropriate transport, e.g. remote_smtp_smarthost:
    helo_data = \
      ${if >{${strlen:${perl{get_remote_helo_data}}}}{0} \
                     {${perl{get_remote_helo_data}}} \