Configuring Fail2Ban on Debian Squeeze

This always start with a package installation:

$ aptitude install fail2ban

Then I simply create a local configuration file where I’ll put all my custom config:

$ touch /etc/fail2ban/jail.local

Here is the content of that file:

[DEFAULT]
# Do not filter connexion from my apartment and from the server itself
ignoreip  = 127.0.0.1 88.123.123.123 91.123.123.123
# Ban for a week
bantime   = 604800
maxretry  = 3
destemail = kevin@deldycke.com
banaction = iptables-allports
action    = %(action_mwl)s

[ssh]
enabled  = true
port     = 22
maxretry = 2

[ssh-ddos]
enabled = true
port     = 22

[apache]
# Apache basic auth
enabled   = true
maxretry  = 3
# Ban for 1 hour
bantime   = 3600

[apache-noscript]
enabled = true

[apache-overflows]
enabled = true

[apache-badbots]
enabled  = true
filter   = apache-badbots
port     = http,https
action   = iptables-allports
logpath  = /var/log/apache*/*access.log
maxretry = 1

[apache-nohome]
enabled  = true
filter   = apache-nohome
port     = http,https
action   = iptables-allports
logpath  = /var/log/apache*/*access.log
maxretry = 1

[exim]
enabled  = true
filter   = exim
port     = smtp,ssmtp
action   = iptables-allports
logpath  = /var/log/exim*/rejectlog
maxretry = 1

[exim-relay]
enabled  = true
filter   = exim-relay
port     = smtp,ssmtp
action   = iptables-allports
logpath  = /var/log/exim*/rejectlog
maxretry = 1

While adjusting Fail2Ban, I was surprised by how sensitive this software is. It can just refuse to start without any notice in the log or on the command line. Even if its log_level variable is set to 4 (= DEBUG) in /etc/fail2ban/fail2ban.conf.

In such a case, a sure way to find the culprit is to use a brute force debugging method: first set all the enabled variable of your jail.local‘s sections to false. Then activate one section after another until Fail2Ban refuse to restart.

For me, the problem was that I forgot to add my custom exim-relay filter to Fail2Ban. So I fixed my issue by creating an empty file at /etc/fail2ban/filter.d/exim-relay.conf in which I pasted the following content:

# Based on default exim.conf filter by Cyril Jaquier
# Real life exemaple:
# 2009-07-02 08:16:42 H=118-167-129-21.dynamic.hinet.net (91.121.198.84) [118.167.129.21] F=<titieueue@hotmail.com> rejected RCPT <s2288@mail2000.com.tw>: relay not permitted

[Definition]

# Option:  failregex
# Notes.:  regex to match use of my exim mail server as a relay it does not
#          allow.
# Values:  TEXT
#
failregex = \[<HOST>\] .*(?:relay not permitted)

# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
ignoreregex =

Speaking of custom filters, here is one to filter DFind scans (file located at /etc/fail2ban/filter.d/apache-w00tw00t.conf):

# Based on http://howflow.com/tricks/block_w00tw00t_scan_hosts_with_fail2ban
# Real life exemaple:
# [Sat Jun 27 16:43:08 2009] [error] [client 94.23.57.77] client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /w00tw00t.at.ISC.SANS.DFind:)

[Definition]

# Option:  failregex
# Notes.:  regex to match the w00tw00t scan messages in the logfile.
# Values:  TEXT
failregex = ^.*\[client <HOST>\].*w00tw00t\.at\.ISC\.SANS\.DFind.*

# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
ignoreregex =

And here is the corresponding section from my jail.local file:

[apache-w00tw00t]
enabled  = true
filter   = apache-w00tw00t
action   = iptables-allports
logpath  = /var/log/apache*/*error.log
maxretry = 1

Using Munin to monitor a Debian Squeeze server

Again, here is a tutorial article exposing the recipe I use to cook a Munin on a Debian Squeeze.

As usual, let’s start by installing the main Munin package:

$ aptitude install munin

FYI, the version that aptitude choose to install was Munin 1.4.5. The default configuration coming along will make it produce graphs and HTML content to /var/cache/munin/www. Now we need to serve these pages via a web server.

As I wanted to play with nginx for a long time, I will use this opportunity to serve Munin’s content. The default version coming with Squeeze is quite old, so we’ll get the latest version from the Dotdeb repository:

$ echo "deb http://packages.dotdeb.org squeeze all" > /etc/apt/sources.list.d/squeeze-dotdeb.list
$ aptitude update
$ aptitude install nginx

And if you don’t want to get those error messages about untrusted packages, don’t forget to add Dotdeb’s keys to your keyring.

We can now test that nginx is working by starting it up then fetch the default served page:

$ /etc/init.d/nginx start
$ wget --output-document=- http://localhost | grep "Welcome to nginx"

Then we’ll disable the default nginx config and create a new one for Munin:

$ rm /etc/nginx/sites-enabled/default
$ touch /etc/nginx/sites-available/munin

In the latter, we put this minimal configuration:

server {
  server_name munin.example.com;
  root /var/cache/munin/www/;
  location / {
    index index.html;
    access_log off;
  }
}

Now we have to activate it before restarting nginx:

$ ln -s  /etc/nginx/sites-available/munin /etc/nginx/sites-enabled/munin
$ /etc/init.d/nginx restart

Now we are free to point our browser to the http://munin.example.com URL to get our graphs.

You’ll see that by default, Munin refer to your machine as localhost.localdomain. It’s time to tweak Munin a little to get nice reports:

$ sed -i 's/\[localhost\.localdomain\]/\[munin\.example\.com\]/g' /etc/munin/munin.conf

By default Munin activate a lot of great graphs. But I always find that some crucial monitoring are missing. Let’s add some more monitoring scripts:

$ aptitude install munin-plugins-extra

Here is a collection of general purpose graphs I automatically add to Munin:

$ ln -s /usr/share/munin/plugins/df_abs  /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/netstat /etc/munin/plugins/
$ echo "[netstat]
user root
" > /etc/munin/plugin-conf.d/netstat

It’s also good to have a clue about your connectivity to the rest of the world:

$ ln -s /usr/share/munin/plugins/ping_  /etc/munin/plugins/ping_google.com
$ ln -s /usr/share/munin/plugins/ping_  /etc/munin/plugins/ping_ovh.fr
$ ln -s /usr/share/munin/plugins/ping_  /etc/munin/plugins/ping_example.com

I also like to have insight about my automated backups:

$ ln -s /usr/share/munin/plugins/ps_ /etc/munin/plugins/ps_duplicity
$ ln -s /usr/share/munin/plugins/ps_ /etc/munin/plugins/ps_sshd

Monitoring temperatures, voltages and other hardware metrics is a must, unless your machine is a virtual server :) :

$ ln -s /usr/share/munin/plugins/cpuspeed         /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/acpi             /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/hddtemp_smartctl /etc/munin/plugins/
$ aptitude install i2c-tools lm-sensors
$ sensors-detect
$ ln -s /usr/share/munin/plugins/sensors_ /etc/munin/plugins/sensors_temp
$ ln -s /usr/share/munin/plugins/sensors_ /etc/munin/plugins/sensors_volt

I sometimes have a Fail2Ban deamon running on a server, so that’s a good thing to monitor it:

$ ln -s /usr/share/munin/plugins/fail2ban /etc/munin/plugins/
$ echo "[fail2ban*]
user root
" > /etc/munin/plugin-conf.d/fail2ban

Having an UPS, it’s good to monitor it too. Here is for the UPS on the local system having the MGE-Ellipse750 ID (as defined in your /etc/nut/ups.conf file):

$ ln -s /usr/share/munin/plugins/nutups_   /etc/munin/plugins/nutups_MGE-Ellipse750_voltages
$ ln -s /usr/share/munin/plugins/nutups_   /etc/munin/plugins/nutups_MGE-Ellipse750_charge
$ ln -s /usr/share/munin/plugins/nutups_   /etc/munin/plugins/nutups_MGE-Ellipse750_freq
$ ln -s /usr/share/munin/plugins/nutups_   /etc/munin/plugins/nutups_MGE-Ellipse750_current
$ ln -s /usr/share/munin/plugins/nut_misc  /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/nut_volts /etc/munin/plugins/
$ echo "[nut*]
user root

[nut_*]
env.upsname MGE-Ellipse750@localhost
" > /etc/munin/plugin-conf.d/nut

And if you have a MySQL server running on the machine, that’s a good idea to get stats:

$ ln -s /usr/share/munin/plugins/mysql_threads     /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/mysql_slowqueries /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/mysql_queries     /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/mysql_bytes       /etc/munin/plugins/

I also use some other Munin plugins coming from Munin exchange:

$ wget http://exchange.munin-monitoring.org/plugins/mysql_size_all/version/1/download --output-document=/usr/share/munin/plugins/mysql_size_all
$ ln -s /usr/share/munin/plugins/mysql_size_all /etc/munin/plugins/

An here is how I monitor my RAID array:

$ wget http://exchange.munin-monitoring.org/plugins/raid/version/3/download --output-document=/usr/share/munin/plugins/raid
$ ln -s /usr/share/munin/plugins/raid /etc/munin/plugins/
$ echo "[raid]
user root
" > /etc/munin/plugin-conf.d/raid

Finally, it’s time to monitor nginx itself:

$ ln -s /usr/share/munin/plugins/nginx_status  /etc/munin/plugins/
$ ln -s /usr/share/munin/plugins/nginx_request /etc/munin/plugins/
$ echo "[nginx_*]
env.url http://localhost/nginx_status
" > /etc/munin/plugin-conf.d/nginx

These two scripts above have some Perl module dependencies:

$ aptitude install libio-all-lwp-perl

If you don’t install the libraries above, you’ll get these kind of errors in /var/log/munin/munin-node.log:

2011/05/03-17:50:10 [2009] Error output from nginx_request:
2011/05/03-17:50:10 [2009]      Can't locate object method "new" via package "LWP::UserAgent" at /etc/munin/plugins/nginx_request line 106.
2011/05/03-17:50:10 [2009] Service 'nginx_request' exited with status 9/0.
2011/05/03-17:50:10 [2009] Error output from nginx_status:
2011/05/03-17:50:10 [2009]      Can't locate object method "new" via package "LWP::UserAgent" at /etc/munin/plugins/nginx_status line 109.
2011/05/03-17:50:10 [2009] Service 'nginx_status' exited with status 2/0.

But for this to work, we have to update the /etc/nginx/sites-enabled/munin file. Now it looks like this:

server {
  server_name munin.example.com;
  root /var/cache/munin/www/;
  # Restrict Munin access
  auth_basic "Restricted";
  auth_basic_user_file /etc/nginx/htpasswd;
  location / {
    index index.html;
    access_log off;
  }
}
server {
  allow 127.0.0.1;
  deny all;
  location /nginx_status {
    stub_status on;
    access_log off;
  }
}

Note that I’ve added a simple HTTP authentication to Munin webpages and restricted access to nginx statistics from the local machine only.

At last, before rebooting Munin and Nginx, make sure all downloaded plugins are executables. This is important and always forgotten:

$ chmod -R 755 /usr/share/munin/plugins/
$ /etc/init.d/nginx restart
$ /etc/init.d/munin-node restart

MGE Ellipse 750 UPS on Debian Squeeze

My home server is protected by an MGE Ellipse 750 UPS for years. I bought it for several reasons: it’s affordable, has good capacity and is Ubuntu certified.

I also read back then rumors implying that Nut’s maintainer was employed by MGE. Having a hardware manufacturer employing a fellow open-source hacker has certainly influenced my purchase decision.

MGE is no more and has been merged with EATON. But my UPS is still supported, and the release of Debian Squeeze is a good opportunity to consolidate my knowledge in the form of this tutorial.

So here is how I setup Nut on Debian Squeeze to monitor my UPS.

First things first, we have to install the main package and its USB driver:

$ aptitude install nut nut-usb

Now let’s configure Nut and run it:

$ sed -i 's/MODE=none/MODE=standalone/g' /etc/nut/nut.conf
$ echo '
[MGE-Ellipse750]
driver = usbhid-ups
port = auto
desc = "MGE UPS Systems"
' >> /etc/nut/ups.conf
$ sed -i 's/# LISTEN 127\.0\.0\.1 3493/LISTEN 127\.0\.0\.1/g' /etc/nut/upsd.conf
$ echo '
[kevin]
password = badpassword
upsmon master
' >> /etc/nut/upsd.users
$ sed -i 's/# NOTIFYCMD \/usr\/local\/ups\/bin\/notifyme/NOTIFYCMD \/sbin\/upssched/g' /etc/nut/upsmon.conf
$ echo '
MONITOR MGE-Ellipse750@localhost 1 kevin badpassword master
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
' >> /etc/nut/upsmon.conf
$ sed -i 's/CMDSCRIPT \/upssched-cmd/CMDSCRIPT \/etc\/nut\/upssched-cmd/g' /etc/nut/upssched.conf
$ sed -i 's/# PIPEFN \/var\/run\/nut\/upssched\/upssched.pipe/PIPEFN \/var\/run\/nut\/upssched.pipe/g' /etc/nut/upssched.conf
$ sed -i 's/# LOCKFN \/var\/run\/nut\/upssched\/upssched.lock/LOCKFN \/var\/run\/nut\/upssched.lock/g' /etc/nut/upssched.conf
$ echo '
AT ONBATT * START-TIMER onbatt 30
AT ONLINE * CANCEL-TIMER onbatt
' >> /etc/nut/upssched.conf
$ echo '
#!/bin/sh
exit 0
' > /etc/nut/upssched-cmd
$ /etc/init.d/nut restart

As you can see you have lots of stuff to configure before Nut can do what it was designed for. But after all of these commands, you should have a working UPS.

You can now test that your system works by using the command below, which list statistics of a given UPS:

$ upsc MGE-Ellipse750@localhost

But in some rare cases, your UPS will not be recognized and you’ll have like me the following messages in your /var/log/syslog:

May  5 16:12:36 paris-server upsmon[10773]: Poll UPS [MGE-Ellipse750@127.0.0.1] failed - Driver not connected

In this case, you should run Nut’s driver in debug mode:

$ /lib/nut/usbhid-ups -DDD -a MGE-Ellipse750
Network UPS Tools - Generic HID driver 0.34 (2.4.3)
USB communication driver 0.31
   0.000000     debug level is '3'
   0.013911     upsdrv_initups...
   0.189541     Checking device (0463/FFFF) (005/003)
   0.189705     - VendorID: 0463
   0.189741     - ProductID: ffff
   0.189767     - Manufacturer: unknown
   0.189794     - Product: unknown
   0.189819     - Serial Number: unknown
   0.189842     - Bus: 005
   0.189862     Trying to match device
   0.189906     Device matches
   0.189954     failed to claim USB device: could not claim interface 0: Operation not permitted
   0.189995     failed to detach kernel driver from USB device: could not detach kernel driver from interface 0: Operation not permitted
   0.190033     failed to claim USB device: could not claim interface 0: Operation not permitted
   0.190070     failed to detach kernel driver from USB device: could not detach kernel driver from interface 0: Operation not permitted
   0.190108     failed to claim USB device: could not claim interface 0: Operation not permitted
   0.190145     failed to detach kernel driver from USB device: could not detach kernel driver from interface 0: Operation not permitted
   0.190181     failed to claim USB device: could not claim interface 0: Operation not permitted
   0.190217     failed to detach kernel driver from USB device: could not detach kernel driver from interface 0: Operation not permitted
   0.190252     Can't claim USB device [0463:ffff]: could not detach kernel driver from interface 0: Operation not permitted

As you can see in messages above, Nut can’t see my UPS. By chance, forcing nut to use the root user let it see my UPS:

$ /lib/nut/usbhid-ups -DDD -u root -a MGE-Ellipse750
Network UPS Tools - Generic HID driver 0.34 (2.4.3)
USB communication driver 0.31
   0.000000     debug level is '3'
   0.001678     upsdrv_initups...
   0.172877     Checking device (0463/FFFF) (005/003)
   1.112408     - VendorID: 0463
   1.112464     - ProductID: ffff
   1.112489     - Manufacturer: MGE OPS SYSTEMS
   1.112516     - Product: ELLIPSE
   1.112542     - Serial Number: BDCJ3800Q
   1.112569     - Bus: 005
   1.112595     Trying to match device
   1.112647     Device matches
   1.112726     failed to claim USB device: could not claim interface 0: Device or resource busy
   1.113239     detached kernel driver from USB device...
   1.251394     HID descriptor, method 1: (9 bytes) => 09 21 00 01 21 01 22 01 03
   1.251460     HID descriptor, method 2: (9 bytes) => 09 21 00 01 21 01 22 01 03
   1.251491     HID descriptor length 769
   1.351379     Report Descriptor size = 769
   1.351456     Report Descriptor: (769 bytes) => 05 84 09 04 a1 01 09 24 a1 00 09 02 a1 00
   1.351509      55 00 65 00 85 01 75 01 95 05 15 00 25 01 05 85 09 d0 09 44 09 45 09 42 0b
(...)

So the issue is now clear and is related to permissions. I was able to fix this issue by changing the permissions on the USB device corresponding to my UPS:

$ chmod 0666 /dev/bus/usb/005/003

Another working way to fix this is to change the group of the device to nut:

$ chown :nut /dev/bus/usb/005/003

BTW, to get the bus number (005 here) and device number (003 in my case) of your UPS, run lsudb:

$ lsusb
Bus 005 Device 003: ID 0463:ffff MGE UPS Systems UPS
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Of course this fix is absolutely temporary, as you’ll need to perform the change above after every reboot. This is far from practical. In fact, as describe in this Fedora 10 bug report, but also in some other Debian bug report, this issue is directly tied to conflicting Udev rules.

Based on clues from these bug reports you can fix Udev using different strategies. As I can’t decide which one is the cleanest, I just did something that is quite brutal, but works. It consist of replacing in /lib/udev/rules.d/91-permissions.rules the line setting rights for USBfs-like devices:

--- /lib/udev/rules.d/91-permissions.rules-orig 2011-05-05 18:49:08.015538434 +0200
+++ /lib/udev/rules.d/91-permissions.rules      2011-05-05 18:49:16.663537978 +0200
@@ -33,7 +33,7 @@

 # usbfs-like devices
 SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", \
-                               MODE="0664"
+                               MODE="0666"

 # serial devices
 SUBSYSTEM=="tty",

Now all you have to do is to unplug the power cord and wait until your machine gracefully shut down as soon as batteries are low ! :)

Setting up HDD’s SMART monitoring on Debian Squeeze

Here is how I setup SMART monitoring and maintenance of an array of Hard-Disk Drives running a Debian Squeeze.

First, let’s install the smartmontools package:

$ aptitude install smartmontools

We now have to check that SMART is activated on our drives:

$ smartctl -a /dev/sdb | grep "SMART support is: Enabled"
$ smartctl -a /dev/sdc | grep "SMART support is: Enabled"
$ smartctl -a /dev/sdd | grep "SMART support is: Enabled"
$ smartctl -a /dev/sde | grep "SMART support is: Enabled"

Nowadays all modern drives are already activated in factories. But in case they’re not you can activate this feature with the following command:

$ smartctl -s on -a /dev/sdb

Now we have to activate the smartd daemon, and let it start automatically when the machine boot:

$ sed -i 's/#start_smartd=yes/start_smartd=yes/g'                           /etc/default/smartmontools
$ sed -i 's/#smartd_opts="--interval=1800"/smartd_opts="--interval=1800"/g' /etc/default/smartmontools

For some reason, I prefer to not let smartd select by itself drives it should check. Here is how to deactivate the auto-detection:

$ sed -i 's/^DEVICESCAN /#DEVICESCAN /g' /etc/smartd.conf

Now let’s specify by hand the way our drives should be monitored and maintained. Here are the lines I added to /etc/smartd.conf:

/dev/sdb -a -o on -S on -s (S/../.././02|L/../../6/03) -m kevin@deldycke.com
/dev/sdc -a -o on -S on -s (S/../.././04|L/../../6/05) -m kevin@deldycke.com
/dev/sdd -a -o on -S on -s (S/../.././06|L/../../6/07) -m kevin@deldycke.com
/dev/sde -a -o on -S on -s (S/../.././08|L/../../6/09) -m kevin@deldycke.com

Finally, we need to restart the SMART service to take into account all our changes:

$ /etc/init.d/smartmontools restart

How-to use GMail to send mails from Debian Squeeze

Here is quick guide on how I configured Exim 4 to let a Debian Squeeze server send mails through a GMail account. This article is just a rip-off of a tutorial I found on the web, which is itself an updated version of a Debian’s Wiki page.

Debian come with Exim (v4.72) pre-installed: it’s the default MTA on this distribution. There is absolutely no need to install extra packages. Let’s start right away by calling Exim’s configuration wizard:

$ dpkg-reconfigure exim4-config

Here are the options I choose in each step of the wizard:

  1. Choose Mail sent by smarthost; received via SMTP or fetchmail.
  2. System mail name: server.deldycke.com.
  3. IP adresses to listen on for incoming SMTP connections: 127.0.0.1 ; ::1 (which is the default proposed value).
  4. Other destinations for which mail is accepted: leave blank.
  5. Machines to relay mail for: leave blank.
  6. Machine handling outgoing mail for this host (smarthost): smtp.gmail.com::587.
  7. Hide local mail name in outgoing mail: No.
  8. Keep number of DNS-queries minimal (Dial-on-Demand): No.
  9. Mailboxes format: mbox.
  10. Split configuration into small files: No.

All these parameters you just answered are saved in the /etc/exim4/update-exim4.conf.conf:

# /etc/exim4/update-exim4.conf.conf
#
# Edit this file and /etc/mailname by hand and execute update-exim4.conf
# yourself or use 'dpkg-reconfigure exim4-config'
#
# Please note that this is _not_ a dpkg-conffile and that automatic changes
# to this file might happen. The code handling this will honor your local
# changes, so this is usually fine, but will break local schemes that mess
# around with multiple versions of the file.
#
# update-exim4.conf uses this file to determine variable values to generate
# exim configuration macros for the configuration file.
#
# Most settings found in here do have corresponding questions in the
# Debconf configuration, but not all of them.
#
# This is a Debian specific file

dc_eximconfig_configtype='smarthost'
dc_other_hostnames=''
dc_local_interfaces='127.0.0.1 ; ::1'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost='smtp.gmail.com:587'
CFILEMODE='644'
dc_use_split_config='false'
dc_hide_mailname='false'
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'

Then I updated the /etc/exim4/exim4.conf.template to add proper handling of GMail SMTP server. Here are the differences between the untouched original exim4.conf.template file and my version:

--- /etc/exim4/exim4.conf.template-orig  2011-05-03 10:49:43.207938577 +0200
+++ /etc/exim4/exim4.conf.template       2011-05-03 10:52:26.235438776 +0200
@@ -1077,15 +1077,11 @@
 # domains, you'll need to copy the dnslookup_relay_to_domains router
 # here so that mail to relay_domains is handled separately.

-smarthost:
-  debug_print = "R: smarthost for $local_part@$domain"
-  driver = manualroute
-  domains = ! +local_domains
-  transport = remote_smtp_smarthost
-  route_list = * DCsmarthost byname
-  host_find_failed = defer
-  same_domain_copy_routing = yes
-  no_more
+send_via_gmail:
+       driver = manualroute
+       domains = ! +local_domains
+       transport = gmail_smtp
+       route_list = * smtp.gmail.com

 .endif

@@ -1632,6 +1628,12 @@
 # to a smarthost. The local host tries to authenticate.
 # This transport is used for smarthost and satellite configurations.

+gmail_smtp:
+       driver = smtp
+       port = 587
+       hosts_require_auth = $host_address
+       hosts_require_tls = $host_address
+
 remote_smtp_smarthost:
   debug_print = "T: remote_smtp_smarthost for $local_part@$domain"
   driver = smtp
@@ -1759,6 +1761,11 @@

 begin authenticators

+gmail_login:
+       driver = plaintext
+       public_name = LOGIN
+       client_send = : system@deldycke.com : XXXXXXXXX
+

 #####################################################
 ### end auth/00_exim4-config_header
@@ -1999,27 +2006,27 @@
                    ^${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}"
 .endif

-login:
-  driver = plaintext
-  public_name = LOGIN
-.ifndef AUTH_CLIENT_ALLOW_NOTLS_PASSWORDS
-  # Return empty string if not non-TLS AND looking up $host in passwd-file
-  # yields a non-empty string; fail otherwise.
-  client_send = "<; ${if and{\
-                          {!eq{$tls_cipher}{}}\
-                          {!eq{PASSWDLINE}{}}\
-                         }\
-                      {}fail}\
-                 ; ${extract{1}{::}{PASSWDLINE}}\
-                ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}"
-.else
-  # Return empty string if looking up $host in passwd-file yields a
-  # non-empty string; fail otherwise.
-  client_send = "<; ${if !eq{PASSWDLINE}{}\
-                      {}fail}\
-                 ; ${extract{1}{::}{PASSWDLINE}}\
-                ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}"
-.endif
+#login:
+#  driver = plaintext
+#  public_name = LOGIN
+#.ifndef AUTH_CLIENT_ALLOW_NOTLS_PASSWORDS
+#  # Return empty string if not non-TLS AND looking up $host in passwd-file
+#  # yields a non-empty string; fail otherwise.
+#  client_send = "<; ${if and{\
+#                          {!eq{$tls_cipher}{}}\
+#                          {!eq{PASSWDLINE}{}}\
+#                         }\
+#                      {}fail}\
+#                 ; ${extract{1}{::}{PASSWDLINE}}\
+#               ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}"
+#.else
+#  # Return empty string if looking up $host in passwd-file yields a
+#  # non-empty string; fail otherwise.
+#  client_send = "<; ${if !eq{PASSWDLINE}{}\
+#                      {}fail}\
+#                 ; ${extract{1}{::}{PASSWDLINE}}\
+#               ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}"
+#.endif
 #####################################################
 ### end auth/30_exim4-config_examples
 #####################################################

Now all we have to do is to regenerate Exim’s configuration and restart the mail server:

$ update-exim4.conf
$ /etc/init.d/exim4 restart

You can then send a dummy email to test your mail system:

$ mail kevin@deldycke.com
Subject: This is an exim test
.
Cc:
Null message body; hope that's ok

And check in the log that everything’s fine:

$ tail -F /var/log/exim4/mainlog
2011-05-03 10:56:32 1QHBPE-0000ne-CW <= root@server.deldycke.com U=root P=local S=362
2011-05-03 10:56:36 1QHBPE-0000ne-CW => kevin@deldycke.com R=send_via_gmail T=gmail_smtp H=gmail-smtp-msa.l.google.com [209.85.227.109] X=TLS1.0:RSA_ARCFOUR_SHA1:16 DN="C=US,ST=California,L=Mountain View,O=Google Inc,CN=smtp.gmail.com"
2011-05-03 10:56:36 1QHBPE-0000ne-CW Completed

Apache commands

  • Hide Subversion and Git directories content (source):
    RedirectMatch 404 /\.(svn|git)(/|$)
    
  • Disable rendering of PHP files coming from imported third party Javascript submodules (context):
    RedirectMatch 404 js-(.*)\.php$
    
  • Redirect any request to current year sub-directory (I used this for a yearly-updated static web page):
    RewriteEngine on
    RewriteRule !^/2010/ /2010/ [R=301,L]
    
  • Here is my template for domain-based virtual host routing:
    # Setup the main website access
    <VirtualHost *:80>
      ServerName example.com
      DocumentRoot /var/www/example
      # Add extra capabilities to let CMS like WordPress manage redirections
      <Directory /var/www/example>
        Options +FollowSymLinks +SymLinksIfOwnerMatch
      </Directory>
    </VirtualHost>
    # Redirect all other access to the website from different domains to the canonical URL
    <VirtualHost *:80>
      ServerName www.example.com
      ServerAlias *.example.com
      ServerAlias example.net *.example.net
      ServerAlias example.org *.example.org
      RedirectMatch permanent (.*) http://example.com$1
    </VirtualHost>
    
  • Insert dynamic headers in HTTP responses depending on the browser:
    BrowserMatchNoCase ".*MSIE\s[1-6].*" IS_DISGUSTING_BROWSER
    Header add X-advice-of-the-day "Save a kitten: use Firefox !" env=IS_DISGUSTING_BROWSER
    
  • Prevent WebDAV connexions (thanks Guillaume!):
    <Location />
      <Limit PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK PATCH>
        # Leaves GET (and HEAD), POST, PUT, DELETE, CONNECT, OPTIONS and TRACE alone
        Order allow,deny
        Deny from all
      </Limit>
    </Location>
    SetEnvIf Request_Method "OPTIONS" CLIENT_PROBE
    Header set Allow "GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE" env=CLIENT_PROBE
    
  • At work, we had to engineer a convoluted software architecture for our intranet to fit the network security policy of our customer. This had a bad side effect of letting the web statistic collector delete all cookies but its own, thus breaking intranet’s authentication. So we (thanks Matthieu!) came up with this unmaintainable hack on Apache side to hide our intranet’s cookies to NedStat’s Javascript embedded code:
    <LocationMatch "/(.*)">
      LoadModule headers_module modules/mod_headers.so
      RequestHeader edit Cookie "(app_cookie_001=[^;]*(; )*)" ""
      RequestHeader edit Cookie "(app_cookie_002=[^;]*(; )*)" ""
      RequestHeader edit Cookie "(app_cookie_003=[^;]*(; )*)" ""
    </LocationMatch>
    
  • Kill all apache processes and restart the service:
    /etc/init.d/apache2 stop ; pkill -9 -u www-data ; /etc/init.d/apache2 restart
    
  • Restart Apache service if no process found:
    [ `ps axu | grep -v "grep" | grep --count "www-data"` -le 0 ] && /etc/init.d/apache2 restart