Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expanded logging options #57

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions admin_honeypot/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@


class LoginAttemptAdmin(admin.ModelAdmin):
list_display = ('username', 'get_ip_address', 'get_session_key', 'timestamp', 'get_path')
list_display = ('username', 'password', 'get_ip_address', 'get_session_key', 'timestamp', 'get_path')
list_filter = ('timestamp',)
readonly_fields = ('path', 'username', 'ip_address', 'session_key', 'user_agent')
readonly_fields = ('path', 'username', 'password', 'ip_address', 'session_key', 'user_agent')
search_fields = ('username', 'ip_address', 'user_agent', 'path')

def get_actions(self, request):
Expand Down
28 changes: 28 additions & 0 deletions admin_honeypot/hpf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import socket
import datetime
import json


class hpflogger:
def __init__(self, hpfserver, hpfport, hpfident, hpfsecret, hpfchannel, serverid):
self.hpfserver = hpfserver
self.hpfport = hpfport
self.hpfident = hpfident
self.hpfsecret = hpfsecret
self.hpfchannel = hpfchannel
self.serverid = serverid
self.hpc = None
if (self.hpfserver and self.hpfport and self.hpfident and self.hpfport and self.hpfchannel and self.serverid):
import logging
logging.basicConfig()
import hpfeeds
try:
self.hpc = hpfeeds.new(self.hpfserver, self.hpfport, self.hpfident, self.hpfsecret)
self.status = "Logging to hpfeeds using server: {0}, channel {1}.".format(self.hpfserver, self.hpfchannel)
except (hpfeeds.FeedException, socket.error, hpfeeds.Disconnect):
self.status = "hpfeeds connection not successful"

def log(self, message):
if self.hpc:
message['serverid'] = self.serverid
self.hpc.publish(self.hpfchannel, json.dumps(message))
57 changes: 57 additions & 0 deletions admin_honeypot/listeners.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
import os
from admin_honeypot.signals import honeypot
from admin_honeypot.hpf import hpflogger
from django.conf import settings
from django.core.mail import mail_admins
from django.template.loader import render_to_string
from django.urls import reverse


hpfl = False
if getattr(settings, 'ADMIN_HONEYPOT_REPORT_HPFEEDS', False):
environreq = [
'HPFEEDS_SERVER',
'HPFEEDS_PORT',
'HPFEEDS_IDENT',
'HPFEEDS_SECRET',
'HPFEEDS_CHANNEL',
'SERVERID',
]
if all(var in os.environ for var in environreq):
hpf_server = os.environ.get('HPFEEDS_SERVER', False)
hpf_port = int(os.environ.get('HPFEEDS_PORT'))
hpf_ident = os.environ.get('HPFEEDS_IDENT')
hpf_secret = os.environ.get('HPFEEDS_SECRET')
hpf_channel = os.environ.get('HPFEEDS_CHANNEL')
hpf_serverid = os.environ.get('HPFEEDS_SERVERID')
else:
hpf_server = getattr(settings, 'HPFEEDS_SERVER', False)
hpf_port = getattr(settings, 'HPFEEDS_PORT')
hpf_ident = getattr(settings, 'HPFEEDS_IDENT')
hpf_secret = getattr(settings, 'HPFEEDS_SECRET')
hpf_channel = getattr(settings, 'HPFEEDS_CHANNEL')
hpf_serverid = getattr(settings, 'HPFEEDS_SERVERID')

if hpf_server:
hpfl = hpflogger(hpf_server, hpf_port, hpf_ident, hpf_secret, hpf_channel, hpf_serverid)


def notify_admins(instance, request, **kwargs):
path = reverse('admin:admin_honeypot_loginattempt_change', args=(instance.pk,))
admin_detail_url = 'http://{0}{1}'.format(request.get_host(), path)
Expand All @@ -17,5 +48,31 @@ def notify_admins(instance, request, **kwargs):
message = render_to_string('admin_honeypot/email_message.txt', context).strip()
mail_admins(subject=subject, message=message)


def report_hpfeeds(instance, request, **kwargs):
if getattr(settings, 'ADMIN_HONEYPOT_RECORD_PASSWORD', False):
password_to_store = request.POST.get('password')
else:
password_to_store = None
msg = {
'timestamp:': instance.timestamp.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
'src_ip': request.META.get('REMOTE_ADDR'),
'src_port': request.META.get('SERVER_PORT'),
'user_agent': request.META.get('HTTP_USER_AGENT'),
'path': request.get_full_path(),
'uri': request.build_absolute_uri(),
'body': request.body.decode('utf-8', 'ignore'),
'method': request.method,
'scheme': request.scheme,
'content_type': request.content_type,
'encoding': request.encoding,
'username': request.POST.get('username'),
'password': password_to_store
}
hpfl.log(msg)

if getattr(settings, 'ADMIN_HONEYPOT_EMAIL_ADMINS', True):
honeypot.connect(notify_admins)

if hpfl:
conn = honeypot.connect(report_hpfeeds)
23 changes: 12 additions & 11 deletions admin_honeypot/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.1.7 on 2019-03-11 15:12

from django.db import models, migrations
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='LoginAttempt',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('username', models.CharField(max_length=255, null=True, verbose_name='username', blank=True)),
('ip_address', models.IPAddressField(null=True, verbose_name='ip address', blank=True)),
('session_key', models.CharField(max_length=50, null=True, verbose_name='session key', blank=True)),
('user_agent', models.TextField(null=True, verbose_name='user-agent', blank=True)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(blank=True, max_length=255, null=True, verbose_name='username')),
('password', models.CharField(blank=True, max_length=255, null=True, verbose_name='password')),
('ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='ip address')),
('session_key', models.CharField(blank=True, max_length=50, null=True, verbose_name='session key')),
('user_agent', models.TextField(blank=True, null=True, verbose_name='user-agent')),
('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='timestamp')),
('path', models.TextField(null=True, verbose_name='path', blank=True)),
('path', models.TextField(blank=True, null=True, verbose_name='path')),
],
options={
'ordering': ('timestamp',),
'verbose_name': 'login attempt',
'verbose_name_plural': 'login attempts',
'ordering': ('timestamp',),
},
bases=(models.Model,),
),
]
20 changes: 0 additions & 20 deletions admin_honeypot/migrations/0002_auto_20160208_0854.py

This file was deleted.

1 change: 1 addition & 0 deletions admin_honeypot/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

class LoginAttempt(models.Model):
username = models.CharField(_("username"), max_length=255, blank=True, null=True)
password = models.CharField(_("password"), max_length=255, blank=True, null=True)
ip_address = models.GenericIPAddressField(_("ip address"), protocol='both', blank=True, null=True)
session_key = models.CharField(_("session key"), max_length=50, blank=True, null=True)
user_agent = models.TextField(_("user-agent"), blank=True, null=True)
Expand Down
9 changes: 8 additions & 1 deletion admin_honeypot/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import django
from admin_honeypot.forms import HoneypotLoginForm
from admin_honeypot.models import LoginAttempt
from admin_honeypot.signals import honeypot
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import redirect_to_login
from django.shortcuts import redirect
Expand Down Expand Up @@ -43,12 +43,19 @@ def form_valid(self, form):
return self.form_invalid(form)

def form_invalid(self, form):
if getattr(settings, 'ADMIN_HONEYPOT_RECORD_PASSWORD', False):
password_to_store = self.request.POST.get('password')
else:
password_to_store = None
instance = LoginAttempt.objects.create(
username=self.request.POST.get('username'),
password=password_to_store,
session_key=self.request.session.session_key,
ip_address=self.request.META.get('REMOTE_ADDR'),
user_agent=self.request.META.get('HTTP_USER_AGENT'),
path=self.request.get_full_path(),
)
honeypot.send(sender=LoginAttempt, instance=instance, request=self.request)
if getattr(settings, 'ADMIN_HONEYPOT_LIMIT_DB', False):
instance.delete()
return super(AdminHoneypot, self).form_invalid(form)
19 changes: 19 additions & 0 deletions docs/manual/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,25 @@ Default: ``True``
Used to determine whether or not to email site admins on login attempts. Set
to ``False`` to disable admin emails.

**ADMIN_HONEYPOT_RECORD_PASSWORD**

Default: ``False``

Used to determine whether or not passwords entered will be recorded. If the honeypot is installed a working site and people have access to the database, then this is a bad idea. Otherwise, set to ``True`` to collect passwords.

**ADMIN_HONEYPOT_REPORT_HPFEEDS**

Default: ``False``

Used to determine whether or not to send login attempts and other requests to an hpfeeds broker.

**ADMIN_HONEYPOT_LIMIT_DB**

Default: ``False``

Stores only necessary information in the local DB. This is useful if logging elsewhere (e.g. to hpfeeds) and local disk space is limited.


Signals
=======

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pytest-cov
pytest-django
pytest-pep8
pytest-pythonpath
hpfeeds3==0.9.6
10 changes: 10 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@
]

ADMIN_HONEYPOT_EMAIL_ADMINS = True
ADMIN_HONEYPOT_RECORD_PASSWORD = False
ADMIN_HONEYPOT_REPORT_HPFEEDS = False
ADMIN_HONEYPOT_LIMIT_DB = False

HPFEEDS_SERVER = 'localhost'
HPFEEDS_PORT = 20000
HPFEEDS_IDENT = 'ident'
HPFEEDS_SECRET = 'secret'
HPFEEDS_CHANNEL = 'channel'
HPFEEDS_SERVERID = 'id'