remove ratelimit and stats page

ansible-ize
eric 2019-04-11 20:50:58 -04:00
parent 1ea796363b
commit 0f6bcede69
6 changed files with 1 additions and 2878 deletions

View File

@ -44,11 +44,9 @@ from SearchPage import BookSearchPage, AuthorSearchPage, SubjectSearchPage, Book
from BibrecPage import BibrecPage
import CoverPages
import QRCodePage
import StatsPage
import CaptchaPage
import Sitemap
import Formatters
import RateLimiter
import Timer
@ -140,12 +138,6 @@ def main ():
Formatters.init ()
cherrypy.log ("Continuing App Init", context = 'ENGINE', severity = logging.INFO)
try:
cherrypy.tools.rate_limiter = RateLimiter.RateLimiterTool ()
except Exception as e:
tb = traceback.format_exc ()
cherrypy.log (tb, context = 'ENGINE', severity = logging.ERROR)
cherrypy.log ("Continuing App Init", context = 'ENGINE', severity = logging.INFO)
cherrypy.tools.I18nTool = i18n_tool.I18nTool ()
@ -169,8 +161,6 @@ def main ():
cherrypy.engine, params = GutenbergDatabase.get_connection_params (cherrypy.config))
cherrypy.engine.pool.subscribe ()
plugins.RateLimiterReset (cherrypy.engine).subscribe ()
plugins.RateLimiterDatabase (cherrypy.engine).subscribe ()
plugins.Timer (cherrypy.engine).subscribe ()
cherrypy.log ("Daemonizing", context = 'ENGINE', severity = logging.INFO)
@ -263,16 +253,7 @@ def main ():
controller = Page.NullPage ())
d.connect ('stats', r'/stats/',
controller = StatsPage.StatsPage ())
d.connect ('block', r'/stats/block/',
controller = RateLimiter.BlockPage ())
d.connect ('unblock', r'/stats/unblock/',
controller = RateLimiter.UnblockPage ())
d.connect ('traceback', r'/stats/traceback/',
controller = RateLimiter.TracebackPage ())
controller = Page.NullPage (), _static = True)
d.connect ('honeypot_send', r'/ebooks/send/megaupload/{id:\d+}.{filetype}',
controller = Page.NullPage (), _static = True)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +0,0 @@
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
"""
StatsPage.py
Copyright 2009-2014 by Marcello Perathoner
Distributable under the GNU General Public License Version 3 or newer.
The appserver stats page.
"""
from __future__ import unicode_literals
import cherrypy
import BaseSearcher
import TemplatedPage
import asyncdns
import ipinfo
class StatsPage (TemplatedPage.TemplatedPage):
""" Output some statistics. """
CONTENT_TYPE = 'application/xhtml+xml; charset=UTF-8'
FORMATTER = 'html'
def index (self, **kwargs):
""" Output stats. """
backends = int (BaseSearcher.sql_get ("SELECT count (*) from pg_stat_activity"))
active_backends = int (BaseSearcher.sql_get (
"SELECT count (*) - 1 from pg_stat_activity where current_query !~ '^<IDLE>'"))
ipsessions = list (cherrypy.tools.rate_limiter.cache.values ()) # pylint: disable=E1101
adns = asyncdns.AsyncDNS ()
# blocked IPs
blocked = sorted ([s for s in ipsessions if s.get ('blocked', 0) >= 2],
key = lambda s: s.ips.sort_key ())
if 'resolve' in kwargs:
for d in blocked:
if d.ips.ipinfo is None:
d.ips.ipinfo = ipinfo.IPInfo (adns, d.ips.get_ip_to_block ())
# active IPs
active = sorted ([s for s in ipsessions if s.get ('active', False)],
key = lambda s: s.ips.sort_key ())
# busiest IPs
busiest = sorted ([s for s in active if s.get ('blocked', 0) < 2],
key = lambda x: -x.get ('rhits'))[:10]
if 'resolve' in kwargs:
for d in busiest:
if d.ips.ipinfo is None:
d.ips.ipinfo = ipinfo.IPInfo (adns, d.ips.get_ip_to_block ())
# IPs with most sessions
most_sessions = sorted ([s for s in active
if not s.ips.whitelisted and len (s.sessions) > 1],
key = lambda s: -len (s.sessions))[:10]
if 'resolve' in kwargs:
for d in most_sessions:
if d.ips.ipinfo is None:
d.ips.ipinfo = ipinfo.IPInfo (adns, d.ips.get_ip_to_block ())
adns.wait ()
adns.cancel ()
return self.output ('stats',
active = active,
blocked = blocked,
busiest = busiest,
most_sessions = most_sessions,
resolve = 'resolve' in kwargs,
rl = cherrypy.tools.rate_limiter, # pylint: disable=E1101
backends = backends,
active_backends = active_backends)

View File

@ -1,200 +0,0 @@
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
"""
asyncdns.py
Copyright 2013-14 by Marcello Perathoner
Distributable under the GNU General Public License Version 3 or newer.
Higher level interface to the GNU asynchronous DNS library.
"""
from __future__ import unicode_literals
import sys
import time
import adns
# pass this to __init__ to use Google Public DNS
RESOLV_CONF = 'nameserver 8.8.8.8'
# http://www.ietf.org/rfc/rfc1035.txt Domain Names
# http://www.ietf.org/rfc/rfc3490.txt IDNA
# http://www.ietf.org/rfc/rfc3492.txt Punycode
class AsyncDNS (object):
""" An asynchronous DNS resolver. """
def __init__ (self, resolv_conf = None):
if resolv_conf:
self.resolver = adns.init (
adns.iflags.noautosys + adns.iflags.noerrprint,
sys.stderr, # FIXME: adns version 1.2.2 will allow keyword params
resolv_conf)
else:
self.resolver = adns.init (
adns.iflags.noautosys + adns.iflags.noerrprint)
self._queries = {} # keeps query objects alive
def query (self, query, callback, rr = adns.rr.A):
""" Queue a query.
:param query: the query string (may contain unicode characters)
:param callback: function taking a tuple of answers
:param rr: the query rr type code
"""
if rr not in (adns.rr.PTR, adns.rr.PTRraw):
query = self.encode (query)
if rr in (adns.rr.PTR, adns.rr.PTRraw):
self._queries [self.resolver.submit_reverse (query, rr)] = callback, rr
else:
self._queries [self.resolver.submit (query, rr)] = callback, rr
def query_dnsbl (self, query, zone, callback, rr = adns.rr.A):
""" Queue a reverse dnsbl-type query. """
self._queries [self.resolver.submit_reverse_any (query, zone, rr)] = callback, rr
def done (self):
""" Are all queued queries answered? """
return not self._queries
def wait (self, timeout = 10):
""" Wait for the queries to complete. """
timeout += time.time ()
while self._queries and time.time () < timeout:
for q in self.resolver.completed (1):
answer = q.check ()
callback, rr = self._queries[q]
del self._queries[q]
# print (answer)
a0 = answer[0]
if a0 == 0:
callback (self.decode_answer (rr, answer[3]))
elif a0 == 101 and rr == adns.rr.A:
# got CNAME, wanted A: resubmit
self.query (answer[1], callback, rr)
# else
# pass
def decode_answer (self, rr, answers):
""" Decode the answer to unicode.
Supports only some rr types. You may override this to support
some more.
"""
if rr in (adns.rr.A, adns.rr.TXT):
# A records are ip addresses that need no decoding.
# TXT records may be anything, even binary data,
# so leave decoding to the caller.
return answers
if rr in (adns.rr.PTR, adns.rr.PTRraw, adns.rr.CNAME, adns.rr.NSraw):
return [ self.decode (host) for host in answers ]
if rr == adns.rr.MXraw:
return [ (prio, self.decode (host)) for (prio, host) in answers ]
if rr == adns.rr.SRVraw:
return [ (prio, weight, port, self.decode (host))
for (prio, weight, port, host) in answers ]
if rr in (adns.rr.SOA, adns.rr.SOAraw):
return [ (self.decode (mname), self.decode (rname),
serial, refresh, retry, expire, minimum)
for (mname, rname, serial, refresh,
retry, expire, minimum) in answers ]
# unsupported HINFO, RP, RPraw, NS, SRV, MX
return answers
@staticmethod
def encode (query):
""" Encode a unicode query to idna.
Result will still be of type unicode/str.
"""
return query.encode ('idna').decode ('ascii')
@staticmethod
def decode (answer):
""" Decode an answer to unicode. """
try:
return answer.decode ('idna')
except ValueError:
return answer.decode ('ascii', 'replace')
def cancel (self):
""" Cancel all pending queries. """
for q in self._queries.keys ():
q.cancel ()
self._queries.clear ()
def bulk_query (self, query_dict, rr):
""" Bulk lookup.
:param dict: on entry { query1: None, query2: None }
on exit { query1: (answer1, ), query2: (answer2a, answer2b) }
Note: you must call wait () after bulk_query () for the answers to appear
"""
def itemsetter (query):
""" Return a callable object that puts the answer into
the dictionary under the right key. """
def g (answer):
""" Put the answer into the dictionary. """
query_dict[query] = answer
# print "put: " + answer
return g
for query in query_dict.keys ():
if query:
self.query (query, itemsetter (query), rr)
def bulk_query (dict_, rr = adns.rr.A, timeout = 10):
""" Perform bulk lookup. """
a = AsyncDNS ()
a.bulk_query (dict_, rr)
a.wait (timeout)
a.cancel ()
if __name__ == '__main__':
import netaddr
queries = dict ()
for i in range (64, 64 + 32):
ip = '66.249.%d.42' % i # google assigned netblock
queries[ip] = None
bulk_query (queries, adns.rr.PTR)
ipset = netaddr.IPSet ()
for ip in sorted (queries):
if queries[ip] and 'proxy' in queries[ip][0]:
print (ip)
ipset.add (ip + '/24')
for cidr in ipset.iter_cidrs ():
print (cidr)

321
ipinfo.py
View File

@ -1,321 +0,0 @@
#!/usr/bin/env python
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
"""
ipinfo.py
Copyright 2013-14 by Marcello Perathoner
Distributable under the GNU General Public License Version 3 or newer.
Find information about an IP, eg. hostname, whois, DNS blocklists.
The Spamhaus Block List (SBL) Advisory is a database of IP
addresses from which Spamhaus does not recommend the acceptance of
electronic mail.
The Spamhaus Exploits Block List (XBL) is a realtime database
of IP addresses of hijacked PCs infected by illegal 3rd party
exploits, including open proxies (HTTP, socks, AnalogX, wingate,
etc), worms/viruses with built-in spam engines, and other types of
trojan-horse exploits.
The Spamhaus PBL is a DNSBL database of end-user IP address
ranges which should not be delivering unauthenticated SMTP email
to any Internet mail server except those provided for specifically
by an ISP for that customer's use. The PBL helps networks enforce
their Acceptable Use Policy for dynamic and non-MTA customer IP
ranges.
"""
from __future__ import unicode_literals
import asyncdns
# pylint: disable=R0903
class DNSBL (object):
""" Base class for DNS blocklists. """
zone = ''
blackhat_tags = {}
dialup_tags = {}
### TOR ###
# see:
# https://www.torproject.org/projects/tordnsel.html.en
# https://www.dan.me.uk/dnsbl
class TorProject (DNSBL):
""" A TOR exitnode list. """
# note: reverse IP of www.gutenberg.org:80
zone = '80.47.134.19.152.ip-port.exitlist.torproject.org'
blackhat_tags = {
'127.0.0.2': 'TOR',
}
class TorDanme (DNSBL):
""" A TOR exitnode list. """
zone = 'torexit.dan.me.uk'
blackhat_tags = {
'127.0.0.100': 'TOR',
}
### SPAMHAUS ###
# see: http://www.spamhaus.org/faq/answers.lasso?section=DNSBL%20Usage#202
class Spamhaus (DNSBL):
""" A DNS blocklist. """
zone = 'zen.spamhaus.org'
blackhat_tags = {
'127.0.0.2': 'SPAMHAUS_SBL',
'127.0.0.3': 'SPAMHAUS_SBL_CSS',
'127.0.0.4': 'SPAMHAUS_XBL_CBL',
}
dialup_tags = {
'127.0.0.10': 'SPAMHAUS_PBL_ISP',
'127.0.0.11': 'SPAMHAUS_PBL',
}
lookup = 'http://www.spamhaus.org/query/ip/{ip}'
### SORBS ###
# see: http://www.sorbs.net/using.shtml
class SORBS (DNSBL):
""" A DNS blocklist. """
zone = 'dnsbl.sorbs.net'
blackhat_tags = {
'127.0.0.2': 'SORBS_HTTP_PROXY',
'127.0.0.3': 'SORBS_SOCKS_PROXY',
'127.0.0.4': 'SORBS_MISC_PROXY',
'127.0.0.5': 'SORBS_SMTP_RELAY',
'127.0.0.6': 'SORBS_SPAMMER',
'127.0.0.7': 'SORBS_WEB', # formmail etc.
'127.0.0.8': 'SORBS_BLOCK',
'127.0.0.9': 'SORBS_ZOMBIE',
'127.0.0.11': 'SORBS_BADCONF',
'127.0.0.12': 'SORBS_NOMAIL',
}
dialup_tags = {
'127.0.0.10': 'SORBS_DUL',
}
### mailspike.net ###
# see: http://mailspike.net/usage.html
class MailSpike (DNSBL):
""" A DNS blocklist. """
zone = 'bl.mailspike.net'
blackhat_tags = {
'127.0.0.2': 'MAILSPIKE_DISTRIBUTED_SPAM',
'127.0.0.10': 'MAILSPIKE_WORST_REPUTATION',
'127.0.0.11': 'MAILSPIKE_VERY_BAD_REPUTATION',
'127.0.0.12': 'MAILSPIKE_BAD_REPUTATION',
}
### shlink.org ###
# see: http://shlink.org/
class BlShlink (DNSBL):
""" A DNS blocklist. """
zone = 'bl.shlink.org'
blackhat_tags = {
'127.0.0.2': 'SHLINK_SPAM_SENDER',
'127.0.0.4': 'SHLINK_SPAM_ORIGINATOR',
'127.0.0.5': 'SHLINK_POLICY_BLOCK',
'127.0.0.6': 'SHLINK_ATTACKER',
}
class DynShlink (DNSBL):
""" A DNS dul list. """
zone = 'dyn.shlink.org'
dialup_tags = {
'127.0.0.3': 'SHLINK_DUL',
}
### barracudacentral.org ###
# see: http://www.barracudacentral.org/rbl/how-to-usee
class Barracuda (DNSBL):
""" A DNS blocklist. """
zone = 'b.barracudacentral.org'
blackhat_tags = {
'127.0.0.2': 'BARRACUDA_BLOCK',
}
### SHADOWSERVER ###
# http://www.shadowserver.org/wiki/pmwiki.php/Services/IP-BGP
class ShadowServer (DNSBL):
""" A DNS-based whois service. """
zone = 'origin.asn.shadowserver.org'
peer_zone = 'peer.asn.shadowserver.org'
fields = 'asn cidr org2 country org1 org'.split ()
# TEAMCYMRU
# http://www.team-cymru.org/Services/ip-to-asn.html
class TeamCymru (DNSBL):
""" A DNS-based whois service. """
zone = 'origin.asn.cymru.com'
asn_zone = 'asn.cymru.com'
fields = 'asn cidr country registry date'.split ()
class IPInfo (object):
""" Holds DNSBL information for one IP. """
dnsbl = [ Spamhaus, SORBS, MailSpike, BlShlink, DynShlink, Barracuda,
TorProject, TorDanme ]
""" Which blocklists to consider. """
def __init__ (self, aresolver, ip):
self.hostname = None
self.whois = {}
self.blackhat_tags = set ()
self.dialup_tags = set ()
ip = str (ip)
rr = asyncdns.adns.rr
try:
aresolver.query (ip, self._hostnamesetter (), rr.PTR)
for dnsbl in self.dnsbl:
aresolver.query_dnsbl (ip, dnsbl.zone, self._tagsetter (dnsbl))
# ShadowServer seems down: March 2014
aresolver.query_dnsbl (ip, ShadowServer.zone, self._whoissetter_ss (), rr.TXT)
# aresolver.query_dnsbl (ip, TeamCymru.zone, self._whoissetter_tc (aresolver), rr.TXT)
except:
pass
@property
def tags (self):
""" All tags (bad and dialup). """
return self.blackhat_tags | self.dialup_tags
def is_blackhat (self):
""" Return true if this is probably a blackhat IP. """
return bool (self.blackhat_tags)
def is_dialup (self):
""" Test if this IP is a dialup. """
return bool (self.dialup_tags)
def is_tor_exit (self):
""" Test if this is a Tor exit node. """
return 'TOR' in self.blackhat_tags
def _hostnamesetter (self):
""" Return a callable object that puts the answer into
the hostname attribute. """
def g (answer):
""" Store answer. """
self.hostname = answer[0]
return g
@staticmethod
def _filter (answers, tag_dict):
""" Lookup answers in tag_dict, return values of matches. """
return [ tag_dict[ip] for ip in answers if ip in tag_dict ]
def _tagsetter (self, dnsbl):
""" Return a callable object that puts the answer into
our *tags attributes. """
def g (answer):
""" Store answer. """
self.blackhat_tags.update (self._filter (answer, dnsbl.blackhat_tags))
self.dialup_tags.update (self._filter (answer, dnsbl.dialup_tags))
return g
@staticmethod
def _decode_txt (answer):
""" Helper: decode / unpack whois answer. """
try:
answer = answer[0][0].decode ('utf-8')
except UnicodeError:
answer = answer[0][0].decode ('iso-8859-1')
answer = answer.strip ('"').split ('|')
return [ a.strip () for a in answer if a ]
def _whoissetter_ss (self):
""" Return a callable object that puts the answer into
the whois dict. """
def g (answer):
""" Store answer. """
self.whois = dict (zip (ShadowServer.fields, self._decode_txt (answer)))
return g
def _whoissetter_tc (self, aresolver):
""" Return a callable object that puts the answer into
the right attribute. """
def g (answer):
""" Store answer. """
self.whois = dict (zip (TeamCymru.fields, self._decode_txt (answer)))
self.whois['org'] = None
# maybe there's still more info?
aresolver.query ('AS' + self.whois['asn'] + '.' + TeamCymru.asn_zone,
self._whoissetter_tc2 (), asyncdns.adns.rr.TXT)
return g
def _whoissetter_tc2 (self):
""" Return a callable object that puts the answer into
the right attribute. """
def g (answer):
""" Store answer. """
self.whois['org'] = self._decode_txt (answer)[-1]
return g
if __name__ == '__main__':
import sys
# test IP 127.0.0.2 should give all positives
a = asyncdns.AsyncDNS (asyncdns.RESOLV_CONF)
i = IPInfo (a, sys.argv[1])
a.wait ()
a.cancel ()
print ('hostname: %s' % i.hostname)
for k in sorted (i.whois.keys ()):
print ("%s: %s" % (k, i.whois[k]))
for tag in sorted (i.tags):
print (tag)
if i.is_blackhat ():
print ('BLACKHAT')
if i.is_dialup ():
print ('DUL')