X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=hippotat.git;a=blobdiff_plain;f=hippotatlib%2F__init__.py;h=2fa300831e6bc186edf165c5b12eef7ed2d4e3f7;hp=bd5d2c3cf58262989707c279c00691e5a969a29b;hb=ef04103340f9c2f2666ea3a12bf6420758f2da6d;hpb=4652e3827272085a9604176b3e8b772217730ed8 diff --git a/hippotatlib/__init__.py b/hippotatlib/__init__.py index bd5d2c3..2fa3008 100644 --- a/hippotatlib/__init__.py +++ b/hippotatlib/__init__.py @@ -50,6 +50,9 @@ from functools import partial import collections import time +import hmac +import hashlib +import base64 import codecs import traceback @@ -139,6 +142,7 @@ port = 80 vroutes = '' ifname_client = hippo%%d ifname_server = shippo%%d +max_clock_skew = 300 #[server] or [] overrides ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip,%(ifname)s %(rnets)s @@ -157,7 +161,7 @@ vvnetwork = 172.24.230.192 # [] -# password = # used by both, must match +# secret = # used by both, must match [LIMIT] max_batch_down = 262144 @@ -367,6 +371,34 @@ def crash_on_critical(event): if event.get('log_level') >= LogLevel.critical: crash(twisted.logger.formatEvent(event)) +#---------- authentication tokens ---------- + +_authtoken_digest = hashlib.sha256 + +def _authtoken_time(): + return int(time.time()) + +def _authtoken_hmac(secret, hextime): + return hmac.new(secret, hextime, _authtoken_digest).digest() + +def authtoken_make(secret): + hextime = ('%x' % _authtoken_time()).encode('ascii') + mac = _authtoken_hmac(secret, hextime) + return hextime + b' ' + base64.b64encode(mac) + +def authtoken_check(secret, token, maxskew): + (hextime, theirmac64) = token.split(b' ') + now = _authtoken_time() + then = int(hextime, 16) + skew = then - now; + if (abs(skew) > maxskew): + raise ValueError('too much clock skew (client %ds ahead)' % skew) + theirmac = base64.b64decode(theirmac64) + ourmac = _authtoken_hmac(secret, hextime) + if not hmac.compare_digest(theirmac, ourmac): + raise ValueError('invalid token (wrong secret?)') + pass + #---------- config processing ---------- def _cfg_process_putatives(): @@ -390,16 +422,17 @@ def _cfg_process_putatives(): server_pat + r' ' + '(?:' + client_pat + '|LIMIT)') for cs in cfg.sections(): - log_debug_config('putatives: section [%s]...' % (cs)) + def dbg(m): + log_debug_config('putatives: section [%s] %s' % (cs, m)) def log_ignore(why): - log_debug_config('putatives: section [%s] X ignore: %s' % (cs, why)) + dbg('X ignore: %s' % (why)) print('warning: ignoring config section [%s] (%s)' % (cs, why), file=sys.stderr) if cs == 'LIMIT' or cs == 'COMMON': # plan A "[LIMIT]" or "[COMMON]" - log_debug_config('putatives: section [%s] A ignore' % (cs)) + dbg('A ignore') continue try: @@ -409,7 +442,7 @@ def _cfg_process_putatives(): if server_re.fullmatch(cs): # plan C "[]" - log_debug_config('putatives: section [%s] C ' % (cs)) + dbg('C ') putative(servers, cs, cs) continue @@ -419,28 +452,30 @@ def _cfg_process_putatives(): if pcs == 'LIMIT': # plan E "[ LIMIT]" - log_debug_config('putatives: section [%s] E LIMIT' % (cs)) + dbg('E LIMIT') continue try: # plan D "[ ]" part 2 - ci = ipaddr(pc) + ci = ipaddr(pcs) except AddressValueError: - # plan F "[]" + # plan F branch 1 "[]" log_ignore('bad-addr') continue else: # no AddressValueError # plan D "[ ]" part 3 - log_debug_config('putatives: section [%s] D ' - % (cs)) + dbg('D ') putative(clients, ci, pcs) putative(servers, pss, pss) continue + else: + # plan F branch 2 "[]" + log_ignore('nomatch '+ repr(serverclient_re)) else: # no AddressValueError # plan B "[" part 2 - log_debug_config('putatives: section [%s] B ' % (cs)) + dbg('B ') putative(clients, ci, cs) continue @@ -518,7 +553,7 @@ def cfg_process_client_limited(cc,ss,sections,key): cc.__dict__[key] = min(val,lim) def cfg_process_client_common(cc,ss,cs,ci): - # returns sections to search in, iff password is defined, otherwise None + # returns sections to search in, iff secret is defined, otherwise None cc.ci = ci sections = ['%s %s' % (ss,cs), @@ -526,11 +561,11 @@ def cfg_process_client_common(cc,ss,cs,ci): ss, 'COMMON'] - try: pwsection = cfg_search_section('password', sections) + try: pwsection = cfg_search_section('secret', sections) except NoOptionError: return None - pw = cfg1get(pwsection, 'password') - cc.password = pw.encode('utf-8') + pw = cfg1get(pwsection, 'secret') + cc.secret = pw.encode('utf-8') cfg_process_client_limited(cc,ss,sections,'target_requests_outstanding') cfg_process_client_limited(cc,ss,sections,'http_timeout') @@ -604,7 +639,7 @@ def common_startup(process_cfg): def read_defconfig(): readconfig('/etc/hippotat/config.d', False) - readconfig('/etc/hippotat/passwords.d', False) + readconfig('/etc/hippotat/secrets.d', False) readconfig('/etc/hippotat/master.cfg', False) def oc_defconfig(od,os, value, op):