]> git.ipfire.org Git - ipfire.org.git/commitdiff
location: Show blacklist status of IP addresses
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 19 Oct 2018 11:32:01 +0000 (12:32 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 19 Oct 2018 11:32:01 +0000 (12:32 +0100)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/backend/geoip.py
src/scss/style.scss
src/templates/location/lookup.html
src/web/location.py

index c9af6369984b7479902190de0131b957ba53d298..beac83bed06f2660cc0c9fb2691355a78e85ad4a 100644 (file)
 #!/usr/bin/python
 
+import ipaddress
+import logging
+import pycares
 import re
+import socket
+import tornado.gen
+import tornado.platform.caresresolver
 
 from . import countries
 
+from .decorators import *
 from .misc import Object
 
+BLACKLISTS = (
+       "access.redhawk.org",
+       "all.spamblock.unit.liu.se",
+       "b.barracudacentral.org",
+       "bl.deadbeef.com",
+       #"bl.emailbasura.org",
+       "bl.spamcannibal.org",
+       "bl.spamcop.net",
+       "blackholes.five-ten-sg.com",
+       #"blackholes.mail-abuse.org",
+       "blacklist.sci.kun.nl",
+       "blacklist.woody.ch",
+       "bogons.cymru.com",
+       "bsb.spamlookup.net",
+       "cbl.abuseat.org",
+       #"cbl.anti-spam.org.cn",
+       #"cblless.anti-spam.org.cn",
+       #"cblplus.anti-spam.org.cn",
+       #"cdl.anti-spam.org.cn",
+       #"combined.njabl.org",
+       "combined.rbl.msrbl.net",
+       "csi.cloudmark.com",
+       "db.wpbl.info",
+       #"dialups.mail-abuse.org",
+       "dnsbl-1.uceprotect.net",
+       "dnsbl-2.uceprotect.net",
+       "dnsbl-3.uceprotect.net",
+       "dnsbl.abuse.ch",
+       "dnsbl.cyberlogic.net",
+       "dnsbl.dronebl.org",
+       "dnsbl.inps.de",
+       "dnsbl.kempt.net",
+       #"dnsbl.njabl.org",
+       "dnsbl.sorbs.net",
+       "dob.sibl.support-intelligence.net",
+       "drone.abuse.ch",
+       "dsn.rfc-ignorant.org",
+       "duinv.aupads.org",
+       #"dul.blackhole.cantv.net",
+       "dul.dnsbl.sorbs.net",
+       "vdul.ru",
+       "dyna.spamrats.com",
+       "dynablock.sorbs.net",
+       #"dyndns.rbl.jp",
+       "dynip.rothen.com",
+       "forbidden.icm.edu.pl",
+       "http.dnsbl.sorbs.net",
+       "httpbl.abuse.ch",
+       "images.rbl.msrbl.net",
+       "ips.backscatterer.org",
+       "ix.dnsbl.manitu.net",
+       "korea.services.net",
+       "mail.people.it",
+       "misc.dnsbl.sorbs.net",
+       "multi.surbl.org",
+       "netblock.pedantic.org",
+       "noptr.spamrats.com",
+       "opm.tornevall.org",
+       "orvedb.aupads.org",
+       "pbl.spamhaus.org",
+       "phishing.rbl.msrbl.net",
+       "psbl.surriel.com",
+       "query.senderbase.org",
+       #"rbl-plus.mail-abuse.org",
+       "rbl.efnetrbl.org",
+       "rbl.interserver.net",
+       "rbl.spamlab.com",
+       "rbl.suresupport.com",
+       "relays.bl.gweep.ca",
+       "relays.bl.kundenserver.de",
+       #"relays.mail-abuse.org",
+       "relays.nether.net",
+       "residential.block.transip.nl",
+       #"rot.blackhole.cantv.net",
+       "sbl.spamhaus.org",
+       #"short.rbl.jp",
+       "smtp.dnsbl.sorbs.net",
+       "socks.dnsbl.sorbs.net",
+       "spam.abuse.ch",
+       "spam.dnsbl.sorbs.net",
+       "spam.rbl.msrbl.net",
+       "spam.spamrats.com",
+       "spamguard.leadmon.net",
+       "spamlist.or.kr",
+       "spamrbl.imp.ch",
+       "tor.dan.me.uk",
+       "ubl.lashback.com",
+       "ubl.unsubscore.com",
+       "uribl.swinog.ch",
+       #"url.rbl.jp",
+       "virbl.bit.nl",
+       #"virus.rbl.jp",
+       "virus.rbl.msrbl.net",
+       "web.dnsbl.sorbs.net",
+       "wormrbl.imp.ch",
+       "xbl.spamhaus.org",
+       "zen.spamhaus.org",
+       "zombie.dnsbl.sorbs.net",
+)
+
+class Resolver(tornado.platform.caresresolver.CaresResolver):
+       def initialize(self, **kwargs):
+               super().initialize()
+
+               # Overwrite Channel
+               self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb, **kwargs)
+
+       @tornado.gen.coroutine
+       def query(self, name, type=pycares.QUERY_TYPE_A):
+               # Create a new Future
+               fut = tornado.gen.Future()
+
+               # Perform the query
+               self.channel.query(name, type, lambda result, error: fut.set_result((result, error)))
+
+               # Wait for the response
+               result, error = yield fut
+
+               # Handle any errors
+               if error:
+                       # NXDOMAIN
+                       if error == pycares.errno.ARES_ENOTFOUND:
+                               return
+
+                       # Ignore responses with no data
+                       elif error == pycares.errno.ARES_ENODATA:
+                               return
+
+                       raise IOError(
+                               "C-Ares returned error %s: %s while resolving %s"
+                               % (error, pycares.errno.strerror(error), name)
+                       )
+
+               # Return the result
+               return result
+
+
 class GeoIP(Object):
+       @lazy_property
+       def resolver(self):
+               return Resolver(tries=2, timeout=1, domains=[])
+
+       def lookup(self, address):
+               return Address(self.backend, address)
+
        def guess_address_family(self, addr):
                if ":" in addr:
                        return 6
@@ -51,3 +202,78 @@ class GeoIP(Object):
 
        def get_country_name(self, code):
                return countries.get_name(code)
+
+
+class Address(Object):
+       def init(self, address):
+               self.address = ipaddress.ip_address(address)
+
+       def __str__(self):
+               return "%s" % self.address
+
+       @property
+       def reverse_pointer(self):
+               return self.address.reverse_pointer
+
+       @property
+       def family(self):
+               if isinstance(self.address, ipaddress.IPv6Address):
+                       return socket.AF_INET6
+               elif isinstance(self.address, ipaddress.IPv4Address):
+                       return socket.AF_INET
+
+       # Blacklist
+
+       def _make_blacklist_rr(self, blacklist):
+               rr = self.reverse_pointer.split(".")
+
+               # Remove in-addr.arpa or ip6.arpa
+               for i in range(2):
+                       rr.pop()
+
+               # Append new suffix
+               rr.append(blacklist)
+
+               return ".".join(rr)
+
+       @tornado.gen.coroutine
+       def _resolve_blacklist(self, blacklist):
+               # Get resource record name
+               rr = self._make_blacklist_rr(blacklist)
+
+               # Get query type from IP protocol version
+               if self.family == socket.AF_INET6:
+                       type = pycares.QUERY_TYPE_AAAA
+               elif self.family == socket.AF_INET:
+                       type = pycares.QUERY_TYPE_A
+               else:
+                       raise NotImplementedError("Unknown IP protocol")
+
+               # Run query
+               try:
+                       res = yield self.backend.geoip.resolver.query(rr, type=type)
+               except IOError as e:
+                       logging.warning(e)
+
+                       return None, "%s" % e
+
+               # Not found
+               if not res:
+                       return False, None
+
+               # If the IP address is on a blacklist, we will try to fetch the TXT record
+               reason = yield self.backend.geoip.resolver.query(rr, type=pycares.QUERY_TYPE_TXT)
+
+               # Take the first reason
+               if reason:
+                       for i in reason:
+                               return True, i.text.decode()
+
+               # Blocked, but no reason
+               return True, None
+
+       @tornado.gen.coroutine
+       def get_blacklists(self):
+               blacklists = yield { bl : self._resolve_blacklist(bl) for bl in BLACKLISTS }
+
+               return blacklists
index dcfa203b82929137e1faba39b545bef6207242e1..82efcd76839dea12fa9bf350bef7f9cfb3322433 100644 (file)
@@ -75,7 +75,7 @@ body {
 
 .list-group {
        .list-group-item {
-               @extend .inverse;
+               color: $body-bg;
        }
 }
 
index 862e95cfdd39cdc4bc986fd533e21083e7364b86..e2bfab6b3998a99c8858a79d1cb38b93925dc13a 100644 (file)
@@ -7,7 +7,7 @@
 
        <div class="row justify-content-center">
                <div class="col-12 col-md-6">
-                       <div class="card">
+                       <div class="card mb-4">
                                {% if peer and peer.latitude and peer.longitude %}
                                        <div class="card-img-top">
                                                {% module Map(peer.latitude, peer.longitude) %}
                                        </dl>
                                </div>
                        </div>
+
+                       {% if blacklists %}
+                               <div class="card mb-4">
+                                       <div class="card-body">
+                                               <h5 class="card-title mb-0">{{ _("Blacklists") }}</h5>
+                                       </div>
+
+                                       <ul class="list-group list-group-flush">
+                                               {% for bl in sorted(blacklists) %}
+                                                       {% if blacklists[bl] %}
+                                                               {% set code, reason = blacklists[bl] %}
+
+                                                               <li class="list-group-item {% if code %}list-group-item-danger{% else %}list-group-item-success{% end %}">
+                                                                       <p class="mb-0">{{ bl }}</p>
+
+                                                                       {% if reason %}
+                                                                               <small class="text-muted">{{ reason }}</small>
+                                                                       {% end %}
+                                                               </li>
+                                                       {% end %}
+                                               {% end %}
+                                       </ul>
+                               </div>
+                       {% end %}
                </div>
        </div>
 {% end block %}
index a9e5dc17ab68c1c1e3c9ff20acb8313943424246..ecac92b01d4b81ddd2c720720348c4321a427061 100644 (file)
@@ -1,6 +1,8 @@
 
 #!/usr/bin/python
 
+import logging
+import tornado.gen
 import tornado.web
 
 from . import handlers_base as base
@@ -11,9 +13,17 @@ class IndexHandler(base.BaseHandler):
 
 
 class LookupHandler(base.BaseHandler):
+       @tornado.gen.coroutine
        def get(self, address):
                peer = self.geoip.get_all(address)
                if peer:
                        peer["country_name"] = self.geoip.get_country_name(peer.country)
 
-               self.render("location/lookup.html", address=address, peer=peer)
+               # Lookup address
+               address = self.geoip.lookup(address)
+
+               # Lookup blacklists
+               blacklists = yield address.get_blacklists()
+
+               self.render("location/lookup.html",
+                       address=address, blacklists=blacklists, peer=peer)