Skip to content

Commit

Permalink
Merge pull request #52 from zallison/baseline_settings
Browse files Browse the repository at this point in the history
Baseline settings
  • Loading branch information
Dr-Syn committed Feb 8, 2017
2 parents d96fff2 + 5273706 commit 301252f
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 10 deletions.
15 changes: 10 additions & 5 deletions foghornd.tac
Expand Up @@ -12,7 +12,7 @@ from twisted.names import dns, cache

from twisted.web import server as webserver

from foghornd import Foghorn, FoghornSettings
from foghornd import Foghorn, FoghornSettings, Cache
from foghornd.foghornrpc import FoghornXMLRPC
from foghornd.foghorndnsserverfactory import FoghornDNSServerFactory

Expand All @@ -36,9 +36,10 @@ def foghord_service():
# create a resource to serve static files
foghorn = Main()
servers = []

fcache = Cache.Cache()
fcache.foghorn = foghorn.foghorn
factory = FoghornDNSServerFactory(
caches=[cache.CacheResolver()],
caches=[fcache],
clients=[foghorn.foghorn]
)
factory.noisy = False
Expand All @@ -51,8 +52,12 @@ def foghord_service():

if not addresses:
for iface in netifaces.interfaces():
for addr in netifaces.ifaddresses(iface)[netifaces.AF_INET]:
addresses.append(addr["addr"])
try:
for addr in netifaces.ifaddresses(iface)[netifaces.AF_INET]:
addresses.append(addr["addr"])
except KeyError:
# No address for this interface
pass

for listen in addresses:
udp_protocol = dns.DNSDatagramProtocol(controller=factory)
Expand Down
2 changes: 1 addition & 1 deletion foghornd/ACL.py
Expand Up @@ -12,7 +12,7 @@ def __init__(self, settings={}):
acl_settings = settings.acl
# By default deny unless we have a whitelist
self.default = acl_settings.get("default", False)
for acl in ["a", "aaaa", "mx", "srv"]:
for acl in ["a", "aaaa", "mx", "srv", "ptr"]:
acl_name = "allow_%s" % acl
self.acls[acl_name] = {}

Expand Down
18 changes: 18 additions & 0 deletions foghornd/Cache.py
@@ -0,0 +1,18 @@
#!/usr/bin/env python

"""Cache"""

from twisted.names.cache import CacheResolver


class Cache(CacheResolver):

foghorn = None
peer_address = None

def query(self, query, timeout=0):
self.foghorn.run_hook("query", self.peer_address, query)
resp = CacheResolver.query(self, query, timeout)
if resp and self.foghorn:
self.foghorn.run_hook("cache", query)
return resp
11 changes: 7 additions & 4 deletions foghornd/foghorn.py
Expand Up @@ -22,15 +22,19 @@ class Foghorn(object):
baseline = False
hook_types = ["init", "query", "failed_acl", "no_acl",
"passed", "upstream_error", "sinkhole", "refused",
"whitelist", "blacklist", "greylist_passed", "greylist_failed"]
"whitelist", "blacklist", "greylist_passed", "greylist_failed",
"cache"]
hooks = {}
acl_map = {dns.A: "allow_a", dns.AAAA: "allow_aaaa",
dns.MX: "allow_mx", dns.SRV: "allow_srv"}
dns.MX: "allow_mx", dns.SRV: "allow_srv",
dns.PTR: "allow_ptr"}

resolver = None

def __init__(self, settings):
self.settings = settings
self.init_logging()
self.baseline = self.settings.baseline
self.logging = logging.getLogger('foghornd')
signal.signal(signal.SIGUSR1, self.toggle_baseline)
signal.signal(signal.SIGHUP, self.reload)
Expand Down Expand Up @@ -164,7 +168,7 @@ def list_check(self, query):
"""
key = query.name.name
curtime = datetime.now()
if query.type in [dns.A, dns.AAAA, dns.MX, dns.SRV]:
if query.type in [dns.A, dns.AAAA, dns.MX, dns.SRV, dns.PTR]:
if self.listhandler.check_whitelist(query):
self.run_hook("whitelist", self.peer_address, query)
return True
Expand Down Expand Up @@ -225,7 +229,6 @@ def query(self, query, timeout=0):
# Disable the warning that timeout is unused. We have to
# accept the argument.
# pylint: disable=W0613
self.run_hook("query", self.peer_address, query)
# ACL:
try:
if not self.ACL.check_acl(self.acl_map[query.type],
Expand Down
5 changes: 5 additions & 0 deletions foghornd/plugins/hooks/Logging.py
Expand Up @@ -68,3 +68,8 @@ def greylist_failed(self, peer, query, msg):
"""A query has failed the blacklist"""
logger = self.foghorn.logging.debug
logger("greylist_failed: %s - %s - %s" % (peer, query, msg))

def cache(self, query):
"""A query has been responded to from the cache"""
logger = self.foghorn.logger.info
logger("cached: %s" % query)
91 changes: 91 additions & 0 deletions foghornd/plugins/hooks/Stats.py
@@ -0,0 +1,91 @@
""" base --- hooks for foghornd """

import logging
from foghornd.plugins.hooks import HooksBase
from datetime import datetime, timedelta

"""
This module exposes hooks allowing plugins to extend the
functionality of foghorn. This base module contains stubs of all the
hooks available. Please check the documentation for each hook for
more information
"""


class Stats(HooksBase):
"""Hooks for foghorn"""
last = datetime.now()
lists = {}
timeout = 60 # How often to print messages

class TimedList(list):
def __init__(self, timeout=60):
self.timeout = timeout

def _clearold(self):
oldtime = datetime.now() - timedelta(seconds=self.timeout)
self[:] = [x for x in self if x["time"] > oldtime]

def append(self, obj):
list.append(self, {"time": datetime.now(), "obj": obj})
self._clearold()

def checkstat(self, name):
try:
self.lists[name].append(1)
except KeyError:
self.lists[name] = self.TimedList()
self.lists[name].append(1)

if datetime.now() - timedelta(seconds=self.timeout) > self.last:
for log in self.lists.keys():
self.lists[log]._clearold()
self.foghorn.logging.info("{} rate: {} per minute".format(log, len(self.lists[log])))
self.last = datetime.now()

def query(self, peer, query):
"""Called for every query before processing"""
self.checkstat("query")

def failed_acl(self, peer, query):
"""A query has failed the ACL"""
self.checkstat("failed_acl")

def no_acl(self, peer, query):
"""A query type has no ACL"""
self.checkstat("no_acl")

def passed(self, peer, query):
"""A query type has passed greylisting"""
self.checkstat("passed")

def upstream_error(self, peer, query):
"""Failure resolving this query"""
self.checkstat("upstream_error")

def sinkhole(self, peer, query):
"""A query will be sinkholed"""
self.checkstat("sinkhole")

def refused(self, peer, query):
"""A query has been refused"""
self.checkstat("refused")

def whitelist(self, peer, query):
"""A query has passed the whitelist"""
self.checkstat("whitelist")

def blacklist(self, peer, query):
"""A query has failed the blacklist"""
self.checkstat("blacklist")

def greylist_passed(self, peer, query, msg):
"""A query has failed the blacklist"""
self.checkstat("greylist_passed")

def greylist_failed(self, peer, query, msg):
"""A query has failed the blacklist"""
self.checkstat("greylist_failed")

def cache(self, query):
self.checkstat("cache")
4 changes: 4 additions & 0 deletions foghornd/plugins/hooks/__init__.py
Expand Up @@ -66,3 +66,7 @@ def greylist_passed(self, peer, query, msg):
def greylist_failed(self, peer, query, msg):
"""A query has failed the blacklist"""
pass

def cache(self, query):
"""A query has been responded to from the cache"""
pass
8 changes: 8 additions & 0 deletions foghornd/settings.py
Expand Up @@ -192,3 +192,11 @@ def resolver(self):
@resolver.setter
def resolver(self, value):
self.data["resolver"] = value

@property
def baseline(self):
return self.data.get("baseline", False)

@baseline.setter
def baseline(self, value):
self.data["baseline"] = value

0 comments on commit 301252f

Please sign in to comment.