import collections
import time
+import hmac
+import hashlib
+import base64
import codecs
import traceback
vroutes = ''
ifname_client = hippo%%d
ifname_server = shippo%%d
+max_clock_skew = 300
#[server] or [<client>] overrides
ipif = userv root ipif %(local)s,%(peer)s,%(mtu)s,slip,%(ifname)s %(rnets)s
# [<client-ip4-or-ipv6-address>]
-# password = <password> # used by both, must match
+# secret = <secret> # used by both, must match
[LIMIT]
max_batch_down = 262144
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():
server_pat = r'[-.0-9A-Za-z]+'
client_pat = r'[.:0-9a-f]+'
server_re = regexp.compile(server_pat)
- serverclient_re = regexp.compile(server_pat + r' ' + client_pat)
+ serverclient_re = regexp.compile(
+ server_pat + r' ' + '(?:' + client_pat + '|LIMIT)')
for cs in cfg.sections():
+ def dbg(m):
+ log_debug_config('putatives: section [%s] %s' % (cs, m))
+
+ def log_ignore(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]"
+ dbg('A ignore')
continue
try:
if server_re.fullmatch(cs):
# plan C "[<servername>]"
+ dbg('C <server>')
putative(servers, cs, cs)
continue
if pcs == 'LIMIT':
# plan E "[<servername> LIMIT]"
+ dbg('E <server> LIMIT')
continue
try:
# plan D "[<servername> <client>]" part 2
- ci = ipaddr(pc)
+ ci = ipaddr(pcs)
except AddressValueError:
- # plan F "[<some thing we do not understand>]"
- # well, we ignore this
- print('warning: ignoring config section %s' % cs, file=sys.stderr)
+ # plan F branch 1 "[<some thing we do not understand>]"
+ log_ignore('bad-addr')
continue
else: # no AddressValueError
- # plan D "[<servername> <client]" part 3
+ # plan D "[<servername> <client>]" part 3
+ dbg('D <server> <client>')
putative(clients, ci, pcs)
putative(servers, pss, pss)
continue
+ else:
+ # plan F branch 2 "[<some thing we do not understand>]"
+ log_ignore('nomatch '+ repr(serverclient_re))
else: # no AddressValueError
# plan B "[<client>" part 2
+ dbg('B <client>')
putative(clients, ci, cs)
continue
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),
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')
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):