Then I remembered that fail2ban was installed on my server, which served that kind of purpose for ssh and various other systems. I tried to figure out a way to combine those two applications. Fail2ban reads the system's logs to determine if a connection must be bailed out or not.

So I modified sfDoctrineGuardPlugin to send messages in those logs. Each time that someone was using a wrong password, an entry in the log will be sent. For that, I had to modify plugins/sfDoctrineGuardPlugin/modules/sfGuardAuth/lib/BasesfGuardAuthActions.class.php .

if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('signin'));
      if ($this->form->isValid())
      {
        $values = $this->form->getValues();
        $this->getUser()->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false);
 
        // always redirect to a URL set in app.yml
        // or to the referer
        // or to the homepage
        $signinUrl = sfConfig::get('app_sf_guard_plugin_success_signin_url', $user->getReferer($request->getReferer()));
 
        return $this->redirect('' != $signinUrl ? $signinUrl : '@homepage');
      }
      else
      {
        $values = $request->getParameter('signin');
        openlog('mySystem', LOG_PID, LOG_AUTH);
        syslog(LOG_WARNING, sprintf('Failed password for %s from %s port 80 mySystem', $values['username'], $request->getRemoteAddress()));
        closelog();
      }
    }

The diff :

--- BasesfGuardAuthActions.class-orig.php	2011-05-07 14:01:46.907823042 +0200
+++ BasesfGuardAuthActions.class.php	2011-03-12 11:07:26.107752306 +0100
@@ -41,10 +41,17 @@
         // or to the homepage
         $signinUrl = sfConfig::get('app_sf_guard_plugin_success_signin_url', $user->getReferer($request->getReferer()));
 
         return $this->redirect('' != $signinUrl ? $signinUrl : '@homepage');
       }
+      else
+      {
+        $values = $request->getParameter('signin');
+        openlog('mySystem', LOG_PID, LOG_AUTH);
+        syslog(LOG_WARNING, sprintf('Failed password for %s from %s port 80 mySystem', $values['username'], $request->getRemoteAddress()));
+        closelog();
+      }
     }
     else
     {
       if ($request->isXmlHttpRequest())
       {

Now let's configure Fail2ban. I created I file called mySystem.conf in /etc/fail2ban/filter.d :

# Fail2Ban configuration file
#
# Author: Yoda-BZH
#
# $Revision: 728 $
#
 
[INCLUDES]
 
# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf
 
 
[Definition]
 
_daemon = mySystem
 
# Option:  failregex
# Notes.:  regex to match the password failures messages in the logfile. The
#          host must be matched by a group named "host". The tag "<HOST>" can
#          be used for standard IP/hostname matching and is only an alias for
#          (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values:  TEXT
#
#failregex = ^%(__prefix_line)s(?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
#            ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
failregex =  ^%(__prefix_line)sFailed (?:password|publickey) for .* from <HOST>(?: port \d*)?(?: mySystem\d*)?$
#            ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
#            ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
#            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers$
#            ^%(__prefix_line)sauthentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s*$
#            ^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
#            ^%(__prefix_line)sAddress <HOST> .* POSSIBLE BREAK-IN ATTEMPT!*\s*$
#            ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
 
# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
ignoreregex =

Make fail2ban use this filter in /etc/fail2ban/jail.conf

[mySystem]
enabled  = true
port     = 80
filter   = mySystem
logpath  = /var/log/auth.log
maxretry = 5

Now, just reload Fail2ban using /etc/init.d/fail2ban restart and that's it !

Nota: Yeah it would be better to override the executeSignin method in a separate class, but I was a bit lazy.