add logging

This commit is contained in:
brent s. 2020-05-10 05:57:35 -04:00
parent 2ac6085846
commit 276041ba7d
Signed by: bts
GPG Key ID: 8C004C2F93481F6B

View File

@ -3,12 +3,21 @@
import argparse
import configparser
import ipaddress
import logging
import logging.handlers
import os
import socket
import sys
##
import requests
import requests.auth
from pyroute2 import IPRoute
try:
# https://www.freedesktop.org/software/systemd/python-systemd/journal.html#journalhandler-class
from systemd import journal
_has_journald = True
except ImportError:
_has_journald = False


# TODO: add checking to see if we're already configured.
@ -19,6 +28,46 @@ from pyroute2 import IPRoute
# https://gist.github.com/pklaus/960672


logfile = '/var/log/tunnelbroker_manager.log'
# Prep the log file.
logfile = os.path.abspath(os.path.expanduser(logfile))
os.makedirs(os.path.dirname(logfile), exist_ok = True, mode = 0o0700)
if not os.path.isfile(logfile):
with open(logfile, 'w') as fh:
fh.write('')
os.chmod(logfile, 0o0600)

# And set up logging.
_cfg_args = {'handlers': [],
'level': logging.DEBUG}
if _has_journald:
# There were some weird changes somewhere along the line.
try:
# But it's *probably* this one.
h = journal.JournalHandler()
except AttributeError:
h = journal.JournaldLogHandler()
# Systemd includes times, so we don't need to.
h.setFormatter(logging.Formatter(style = '{',
fmt = ('{name}:{levelname}:{name}:{filename}:'
'{funcName}:{lineno}: {message}')))
_cfg_args['handlers'].append(h)
h = logging.handlers.RotatingFileHandler(logfile,
encoding = 'utf8',
# Disable rotating for now.
# maxBytes = 50000000000,
# backupCount = 30
)
h.setFormatter(logging.Formatter(style = '{',
fmt = ('{asctime}:'
'{levelname}:{name}:{filename}:'
'{funcName}:{lineno}: {message}')))
_cfg_args['handlers'].append(h)
logging.basicConfig(**_cfg_args)
logger = logging.getLogger('HE Tunnelbroker Manager')
logger.info('Logging initialized.')


class TunnelBroker(object):
ipget_url = 'https://ipv4.clientinfo.square-r00t.net/'
ipget_params = {'raw': '1'}
@ -33,83 +82,153 @@ class TunnelBroker(object):
self.wan = wan_ip
self.force_update = update
self.conf_file = os.path.abspath(os.path.expanduser(conf))
logger.debug('Using config path: {0}'.format(self.conf_file))
self._conf = configparser.ConfigParser(allow_no_value = True,
interpolation = configparser.ExtendedInterpolation())
try:
self._conf.read(self.conf_file)
logger.debug('Read in configuration successfully.')
except Exception as e:
logger.error('Failed reading configuration: {0}'.format(e))
raise e
if len(self._conf.sections()) < 1:
logger.error('Config file has no sections/tunnels defined.')
raise RuntimeError('Config file has no sections/tunnels defined')
self.tun_id = tun_id
if self.tun_id and self.tun_id not in self._conf.sections():
logger.error(('The tun_id {0} is not a valid tunnel ID. '
'Valid tunnels are: {1}').format(self.tun_id, ', '.join(self._conf.sections())))
raise ValueError('tun_id not a valid tunnel ID')
elif not self.tun_id:
self.tun_id = self._conf.sections()[0]
logger.debug('Automatically set tun_id to {0}'.format(self.tun_id))
self.cfg = self._conf[self.tun_id]
self.server = ipaddress.ip_address(self.cfg['server'])
logger.debug('Set server IP to {0}.'.format(str(self.server)))
self.addrs = [ipaddress.ip_network(ip.strip()) for ip in self.cfg['allocations'].split(',')]
logger.debug('Using address allocations: {0}'.format(', '.join([str(ip) for ip in self.addrs])))
for k in ('user', 'update_key'):
setattr(self, k, self.cfg[k])
# Don't log creds, even in debug.
# logger.debug('Set {0} to {1}'.format(k, getattr(self, k)))
self.iface_name = 'he-{0}'.format(self.tun_id)

def _get_my_ip(self):
if self.wan:
logger.debug('WAN IP tunneling enabled; fetching WAN IP.')
req = requests.get(self.ipget_url, params = self.ipget_params)
if not req.ok:
logger.error('Could not fetch self IP. Request returned {0}.'.format(req.status_code))
raise RuntimeError('Could not fetch self IP')
self.my_ip = ipaddress.ip_address(req.json()['ip'])
logger.debug('Set my_ip to {0}.'.format(str(self.my_ip)))
else:
logger.debug('WAN IP tunneling disabled; fetching LAN IP.')
if not self.ipr:
self.ipr = IPRoute()
_defrt = self.ipr.get_default_routes(family = socket.AF_INET)
if len(_defrt) != 1: # This (probably) WILL fail on multipath systems.
logger.error('Could not determine default route. Does this machine have a single default route?')
raise RuntimeError('Could not determine default IPv4 route')
self.my_ip = ipaddress.ip_address(_defrt[0]['attrs']['RTA_PREFSRC'])
logger.debug('Set my_ip to {0}.'.format(str(self.my_ip)))
return(None)

def start(self):
if self.force_update:
logger.debug('IP update forced; updating.')
self._get_my_ip()
self.update()
if not self.ipr:
self.ipr = IPRoute()
try:
self.ipr.link('add',
ifname = 'he-{0}'.format(self.tun_id),
ifname = self.iface_name,
kind = 'sit',
sit_local = str(self.my_ip),
sit_remote = str(self.server),
sit_ttl = 255)
self.iface_idx = self.ipr.link_lookup(ifname = 'he-{0}'.format(self.tun_id))[0]
logger.debug('Added link {0} successfully.'.format(self.iface_name))
except Exception as e:
logger.error('Could not create link for link {0} '
'(maybe it already exists?): {1}'.format(self.iface_name, e))
raise e
try:
self.iface_idx = self.ipr.link_lookup(ifname = self.iface_name)[0]
logger.debug('Found link {0} at index {1}.'.format(self.iface_name, self.iface_idx))
except Exception as e:
logger.error('Could not set iface_idx for iface name {0}: {1}'.format(self.iface_name, e))
raise e
try:
self.ipr.link('set', index = self.iface_idx, state = 'up', mtu = 1480)
logger.debug('Set link {0} status to UP.'.format(self.iface_name))
except Exception as e:
logger.error(('Could not bring up link for iface name {0} at index {1}: '
'{2}').format(self.iface_name, self.iface_idx, e))
raise e
for a in self.addrs:
try:
self.ipr.addr('add',
index = self.iface_idx,
address = str(a),
address = str(a.network_address),
mask = a.prefixlen,
family = socket.AF_INET6)
logger.debug('Added address {0} to link {1}.'.format(str(a), self.iface_name))
except Exception as e:
logger.error(('Could not add address {0} on link {1}: '
'{2}').format(str(a), self.iface_name, e))
raise e
try:
self.ipr.route('add', dst = '::', mask = 0, oif = self.iface_idx, family = socket.AF_INET6)
logger.debug('Added default route for link {0}.'.format(self.iface_name))
except Exception as e:
logger.error(('Could not add default IPv6 route on link {0}: {1}').format(self.iface_name, e))
raise e
self.ipr.close()
return(None)

def stop(self):
if not self.ipr:
self.ipr = IPRoute()
self.iface_idx = self.ipr.link_lookup(ifname = 'he-{0}'.format(self.tun_id))[0]
try:
self.iface_idx = self.ipr.link_lookup(ifname = self.iface_name)[0]
logger.debug('Found link {0} at index {1}.'.format(self.iface_name, self.iface_idx))
except Exception as e:
logger.error('Could not set iface_idx for link {0}: {1}'.format(self.iface_name, e))
raise e
try:
self.ipr.link('set', index = self.iface_idx, state = 'down')
except Exception as e:
logger.error('Could not bring down link {0}: {1} (continuing anyways)'.format(self.iface_name, e))
try:
self.ipr.route('del', dst = '::', mask = 0, oif = self.iface_idx, family = socket.AF_INET6)
self.ipr.link('set', index = self.iface_idx, state = 'down')
except Exception as e:
logger.error(('Could not remove default IPv6 route on link {0}: '
'{1} (continuing anyways)').format(self.iface_name, e))1
try:
self.ipr.link('del', index = self.iface_idx)
logger.debug('Deleted link {0}.'.format(self.iface_name))
except Exception as e:
logger.error('Could not delete link {0}: {1}'.format(self.iface_name, e))
raise e
self.ipr.close()
return(None)

def update(self, oneshot = False):
self._get_my_ip()
auth_handler = requests.auth.HTTPBasicAuth(self.user, self.update_key)
logger.debug('Set auth handler.')
logger.debug('Requesting IP update at provider.')
req = requests.get(self.api_base,
params = {'hostname': self.tun_id,
'myip': self.my_ip},
auth = auth_handler)
if not req.ok:
logger.error('Could not update IP at provider. Request returned {0}.'.format(req.status_code))
raise RuntimeError('Could not update client IP in tunnel')
status = req.content.decode('utf-8').split()[0].strip()
if status.lower() not in ('good', 'nochg'):
logger.error('Returned following failure message: {0}'.format(req.content.decode('utf-8')))
raise RuntimeError('Client IP update returned failure')
if self.ipr and oneshot:
self.ipr.close()
@ -150,6 +269,7 @@ def parseArgs():

def main():
args = parseArgs().parse_args()
logger.debug('Invoked with args: {0}'.format(vars(args)))
tb = TunnelBroker(**vars(args))
if args.oper == 'start':
tb.start()