Fail2ban + Django convenient format

You want to use Django auth failures to generate fail2ban bans to protect against brute-forcing of your passwords. Nothing on django packages... So, you: wrap your login handler with a decorator that logs out messages on failures...

def log_login_failures( function ):
    auth_log = logging.getLogger( 'django.auth' )
    def wrapped( request, *args, **named ):
        result = function( request, *args, **named )
        if request.method == 'POST' and result.status_code != 302:
                "Login Fail %r by %s",
                request.POST.get( 'username' ),
        return result 
    return wrapped

Then you configure your logging to send those messages to a specific log...

    'version': 1,
    'formatters': {
        'auth': {
            'class': 'logging.Formatter',
            'format': '%(created)s|%(levelname)s|%(message)s',
    'handlers': {
        'auth': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'maxBytes': 2*1024*1024,
            'backupCount': 1,
            'formatter': 'auth',
            'filename': AUTH_LOG_FILE,
    'loggers': {
        'django.auth': {
            'handlers': ['auth'],
            'level': 'INFO',
            'propagate': False,

Add a filter to /etc/fail2ban/filters.d/django-auth.conf


daemon = gunicorn_django
failregex = Login Fail.*by <HOST>

And add a scanner to your /etc/fail2ban/jail.local

enabled = true 
logpath = /var/blog/log/django-auth.log
filter = django-auth
action = iptables-allports

And then you think... where is the pre-built app for this? Surely this can't be something that gets rebuilt by every single Django + Fail2ban user?


  1. Peter Hansen

    Peter Hansen on 06/11/2013 5:16 p.m. #

    I'm not familiar with the intricacies of fail2ban configuration, but it strikes me as odd that the [django-auth] section would have an entry "filters = django-auth", and the other file which is named filters.d and would therefore (I assume) have filters in it does not have any clear reference to or from the stuff in jail.local. Is there a mistake? If not, I wonder why that "filters=" line is even there.

  2. Mike Fletcher

    Mike Fletcher on 06/11/2013 9:24 p.m. #

    The filters are generic, basically catching some set of text in a log file, without any specification of what log file. Its job is to pull out a "host" value from a log message that means "failed login". You would often have these shared along with the software involved, so your ssh daemon would define filters that match its logging output for an access failure.

    The jail.local is a (local) configuration saying "hey, apply that filter to a particular log file, and do this if it matches". In this case, it says to scan the given log file with the filter we just defined, and if it matches, ban that ip address on all ports.

  3. Peter Hansen

    Peter Hansen on 06/13/2013 1:54 p.m. #

    Mike, understood, but note that you say "with the filter we just defined" yet there's no obvious way based on what you showed that the jail.local entry points to the filter. I'm guessing what's missing is that in filters.d, your filter file is actually named django-auth. Is that what's missing? Otherwise I see no reference from jail file to *that* filter.

  4. Mike Fletcher

    Mike Fletcher on 06/13/2013 2:05 p.m. #

    Ah, now I understand the problem. I've corrected the post to mention the name of the file, which was, as you surmised, django-auth.conf. Thanks.

Comments are closed.


Pingbacks are closed.