Skip to content
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
43 changes: 42 additions & 1 deletion denyhosts_server/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import config
import database
import models
from models import Cracker, Report, Legacy
from models import Cracker, Report, Legacy, ClientVersion
import utils

def get_cracker(ip_address):
Expand All @@ -50,6 +50,47 @@ def handle_report_from_client(client_ip, timestamp, hosts):
utils.unlock_host(cracker_ip)
logging.debug("Done adding report for {} from {}".format(cracker_ip,client_ip))


def get_client_version(client_ip):
return ClientVersion.find(
where=['ip_address=?', client_ip],
limit=1
)


@inlineCallbacks
def handle_version_report_from_client(client_ip, version_info, timestamp):
if not utils.is_valid_ip_address(client_ip):
logging.warning("Illegal remote ip address {}".format(client_ip))
raise Exception("Illegal remote IP address \"{}\".".format(client_ip))

dh_version = version_info[1]
py_version = version_info[0]
try:
client_report = yield get_client_version(client_ip)
if client_report is None:
logging.debug("Adding version report for {}".format(client_ip))
save_version = ClientVersion(
ip_address=client_ip,
first_time=timestamp,
latest_time=timestamp,
python_version=py_version,
denyhosts_version=dh_version,
total_reports=1
)
yield save_version.save()
else:
logging.debug('Updating Client Report: {}'.format(client_report))
client_report.latest_time = timestamp
client_report.python_version = py_version
client_report.denyhosts_version = dh_version
client_report.total_reports = client_report.total_reports + 1
yield client_report._update()

except Exception as e:
logging.exception('Error in Version Reporting: {}'.format(e))
logging.debug("Done adding report from {}".format(client_ip))

# Note: lock cracker IP first!
# Report merging algorithm by Anne Bezemer, see
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=622697
Expand Down
26 changes: 25 additions & 1 deletion denyhosts_server/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def _remove_tables(txn):
txn.execute("DROP TABLE IF EXISTS legacy")
txn.execute("DROP TABLE IF EXISTS history")
txn.execute("DROP TABLE IF EXISTS country_history")
txn.execute("DROP TABLE IF EXISTS client_version")

def _evolve_database_initial(txn, dbtype):
if dbtype=="sqlite3":
Expand Down Expand Up @@ -145,6 +146,28 @@ def _evolve_database_v8(txn, dbtype):
print("Fixing up historical data...")
stats.fixup_history_txn(txn)


def _evolve_database_v9(txn, dbtype):
global _quiet
if dbtype == "sqlite3":
autoincrement = "AUTOINCREMENT"
elif dbtype == "MySQLdb":
autoincrement = "AUTO_INCREMENT"

if dbtype in ['MySQLdb', 'sqlite3']:
txn.execute("""CREATE TABLE client_version (
id INTEGER PRIMARY KEY {},
ip_address VARCHAR(50),
first_time INTEGER,
latest_time INTEGER,
python_version VARCHAR(15),
denyhosts_version VARCHAR(25),
total_reports INTEGER
)""".format(autoincrement))
txn.execute("CREATE INDEX denyhosts_version_count ON client_version(total_reports)")
txn.execute("CREATE INDEX denyhosts_ip_version ON client_version(denyhosts_version, ip_address)")


_evolutions = {
1: _evolve_database_v1,
2: _evolve_database_v2,
Expand All @@ -153,7 +176,8 @@ def _evolve_database_v8(txn, dbtype):
5: _evolve_database_v5,
6: _evolve_database_v6,
7: _evolve_database_v7,
8: _evolve_database_v8
8: _evolve_database_v8,
9: _evolve_database_v9
}

_schema_version = len(_evolutions)
Expand Down
14 changes: 14 additions & 0 deletions denyhosts_server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,34 @@

from twistar.dbobject import DBObject


class ClientVersion(DBObject):
TABLENAME = 'client_version'
column_names = ['ip_address', 'first_time', 'latest_time', 'python_version', 'denyhosts_version', 'total_reports']

def __str__(self):
return "ClientVersion({},{},{},{},{},{},{})".format(
self.id, self.ip_address, self.first_time, self.latest_time,
self.python_version, self.denyhosts_version, self.total_reports
)


class Cracker(DBObject):
HASMANY=['reports']
column_names=['ip_address','first_time', 'latest_time', 'resiliency', 'total_reports', 'current_reports']

def __str__(self):
return "Cracker({},{},{},{},{},{})".format(self.id,self.ip_address,self.first_time,self.latest_time,self.resiliency,self.total_reports,self.current_reports)


class Report(DBObject):
BELONGSTO=['cracker']
column_names=['ip_address','first_report_time', 'latest_report_time']

def __str__(self):
return "Report({},{},{},{})".format(self.id,self.ip_address,self.first_report_time,self.latest_report_time)


class Legacy(DBObject):
TABLENAME="legacy"
pass
Expand Down
27 changes: 26 additions & 1 deletion denyhosts_server/peering.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from xmlrpclib import ServerProxy

from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.threads import deferToThread
from twisted.internet.threads import deferToThread

import libnacl.public
import libnacl.utils
Expand Down Expand Up @@ -56,6 +56,30 @@ def send_update(client_ip, timestamp, hosts):
except:
logging.warning("Unable to send update to peer {}".format(peer))

@inlineCallbacks
def send_client_version_update(client_info):
data = {
"ip_address": client_info.ip_address,
"first_time": client_info.first_time,
"latest_time": client_info.latest_time,
'python_version': client_info.python_version,
'denyhosts_version': client_info.denyhosts_version,
'total_reports': client_info.total_reports
}
data_json = json.dumps(data)

for peer in config.peers:
logging.debug("Sending client_version update to peer {}".format(peer))
crypted = _peer_boxes[peer].encrypt(data_json)
base64 = crypted.encode('base64')
try:
server = yield deferToThread(ServerProxy, peer)
yield deferToThread(server.peering.update, _own_key.pk.encode('hex'), base64)
except Exception as e:
logging.exception('Error in Peer Version Reporting: {}'.format(peer))
logging.debug("Done adding peer version reports from {}".format(client_ip))


def decrypt_message(peer_key, message):
peer = None
for _peer in config.peers:
Expand Down Expand Up @@ -93,6 +117,7 @@ def handle_schema_version(peer_key, please):

returnValue(schema_version)


@inlineCallbacks
def handle_all_hosts(peer_key, please):
data = decrypt_message(peer_key, please)
Expand Down
25 changes: 25 additions & 0 deletions denyhosts_server/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ class Server(xmlrpc.XMLRPC):
An example object to be published.
"""

@withRequest
@inlineCallbacks
def xmlrpc_version_report(self, request, version_info):
try:
x_real_ip = request.requestHeaders.getRawHeaders("X-Real-IP")
remote_ip = x_real_ip[0] if x_real_ip else request.getClientIP()
now = time.time()

logging.info("version_report({}) from {}".format(version_info, remote_ip))
yield controllers.handle_version_report_from_client(remote_ip, version_info, now)
try:
client_version_data = yield controllers.get_client_version(remote_ip)
yield peering.send_client_version_update(client_version_data)
except xmlrpc.Fault, e:
raise e
except Exception, e:
logging.warning("Error sending version report")
except xmlrpc.Fault, e:
raise e
except Exception, e:
log.err(_why="Exception in version_report")
raise xmlrpc.Fault(104, "Error version report: {}".format(e))

returnValue(0)

@withRequest
@inlineCallbacks
def xmlrpc_add_hosts(self, request, hosts):
Expand Down
13 changes: 13 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
setuptools ; python_version < '3.0'
twisted
twistar
libnacl
ipaddr ; python_version < '3.0'
GeoIP
jinja2
matplotlib
numpy
minify
pysqlite ; python_version < '3.0'
mysql-python ; python_version < '3.0'
mysqlclient ; python_version >= '3.0'
23 changes: 22 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from denyhosts_server import models
from denyhosts_server import controllers
from denyhosts_server.models import Cracker, Report
from denyhosts_server.models import Cracker, Report, ClientVersion

from twisted.internet.defer import inlineCallbacks, returnValue

Expand Down Expand Up @@ -119,4 +119,25 @@ def test_add_multiple_reports(self):
cracker = yield controllers.get_cracker("192.168.1.1")
self.assertIsNone(cracker, "Maintenance should remove cracker")


class ClientVersionModelsTest(base.TestBase):

client_ip = '64.233.185.100'
python_version = '2.7.14'
denyhosts_version = '3.1.2'

def test_01_add_version_report(self):
now = time.time()
yield ClientVersion(
ip_address=self.client_ip,
first_time=now,
latest_time=now,
python_version=self.python_version,
denyhosts_version=self.denyhosts_version,
total_reports=1
).save()

cv2 = yield controllers.get_client_version(self.client_ip)
yield self.assertIsNotNone(cv2, 'Added report is in database')

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4