änderungen für mac
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from ipaddress import ip_interface
|
||||
|
||||
from .posix import PosixProcessProvider
|
||||
from .provider import FirewallProvider, RouteProvider, SplitDNSProvider
|
||||
from .util import get_executable
|
||||
|
||||
|
||||
class PsProvider(PosixProcessProvider):
|
||||
def __init__(self):
|
||||
self.lsof = get_executable('/usr/sbin/lsof')
|
||||
self.ps = get_executable('/bin/ps')
|
||||
|
||||
def pid2exe(self, pid):
|
||||
info = subprocess.check_output([self.lsof, '-p', str(pid)], universal_newlines=True)
|
||||
for line in info.splitlines():
|
||||
parts = line.split()
|
||||
if parts[3] == 'txt':
|
||||
return parts[8]
|
||||
|
||||
def ppid_of(self, pid=None):
|
||||
if pid is None:
|
||||
return os.getppid()
|
||||
try:
|
||||
return int(subprocess.check_output([self.ps, '-p', str(pid), '-o', 'ppid=']))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
class BSDRouteProvider(RouteProvider):
|
||||
def __init__(self):
|
||||
self.route = get_executable('/sbin/route')
|
||||
self.ifconfig = get_executable('/sbin/ifconfig')
|
||||
|
||||
def _route(self, *args):
|
||||
return subprocess.check_output([self.route, '-n'] + list(map(str, args)), universal_newlines=True)
|
||||
|
||||
def _ifconfig(self, *args):
|
||||
return subprocess.check_output([self.ifconfig] + list(map(str, args)), universal_newlines=True)
|
||||
|
||||
def _family_option(self, destination):
|
||||
return '-inet6' if destination.version == 6 else '-inet'
|
||||
|
||||
def add_route(self, destination, *, via=None, dev=None, src=None, mtu=None):
|
||||
args = ['add', self._family_option(destination)]
|
||||
if mtu is not None:
|
||||
args.extend(('-mtu', str(mtu)))
|
||||
if via is not None:
|
||||
args.extend((destination, via))
|
||||
elif dev is not None:
|
||||
args.extend(('-interface', destination, dev))
|
||||
self._route(*args)
|
||||
|
||||
replace_route = add_route
|
||||
|
||||
def remove_route(self, destination):
|
||||
self._route('delete', self._family_option(destination), destination)
|
||||
|
||||
def get_route(self, destination):
|
||||
# Format of BSD route get output: https://unix.stackexchange.com/questions/53446
|
||||
info = self._route('get', self._family_option(destination), destination)
|
||||
lines = iter(info.splitlines())
|
||||
info_d = {}
|
||||
for line in lines:
|
||||
if ':' not in line:
|
||||
keys = line.split()
|
||||
vals = next(lines).split()
|
||||
info_d.update(zip(keys, vals))
|
||||
break
|
||||
key, val = line.split(':', 1)
|
||||
info_d[key.strip()] = val.strip()
|
||||
if 'gateway' in info_d or 'interface' in info_d:
|
||||
return {
|
||||
'via': info_d.get('gateway', None),
|
||||
'dev': info_d.get('interface', None),
|
||||
'mtu': info_d.get('mtu', None),
|
||||
}
|
||||
|
||||
def flush_cache(self):
|
||||
pass
|
||||
|
||||
_LINK_INFO_RE = re.compile(r'flags=\d<(.*?)>\smtu\s(\d+)$')
|
||||
|
||||
def get_link_info(self, device):
|
||||
info = self._ifconfig(device)
|
||||
match = self._LINK_INFO_RE.search(info)
|
||||
if match:
|
||||
flags = match.group(1).split(',')
|
||||
mtu = int(match.group(2))
|
||||
return {
|
||||
'state': 'UP' if 'UP' in flags else 'DOWN',
|
||||
'mtu': mtu,
|
||||
}
|
||||
return None
|
||||
|
||||
def set_link_info(self, device, state, mtu=None):
|
||||
args = [device]
|
||||
if state is not None:
|
||||
args.append(state)
|
||||
if mtu is not None:
|
||||
args.extend(('mtu', str(mtu)))
|
||||
self._ifconfig(*args)
|
||||
|
||||
def add_address(self, device, address):
|
||||
address = ip_interface(address)
|
||||
if address.version == 6:
|
||||
self._ifconfig(device, 'inet6', address)
|
||||
else:
|
||||
# Repetition of the IP address is the correct syntax for a point-to-point interface
|
||||
# with BSD ifconfig. See example in default vpnc-script:
|
||||
# https://gitlab.com/openconnect/vpnc-scripts/blob/https://gitlab.com/openconnect/vpnc-scripts/blob/921e8760/vpnc-script#L193
|
||||
self._ifconfig(device, 'inet', address.ip, address.ip, 'netmask', '255.255.255.255')
|
||||
|
||||
|
||||
class MacSplitDNSProvider(SplitDNSProvider):
|
||||
def configure_domain_vpn_dns(self, domains, nameservers):
|
||||
if not os.path.exists('/etc/resolver'):
|
||||
os.makedirs('/etc/resolver')
|
||||
for domain in domains:
|
||||
resolver_file_name = "/etc/resolver/{0}".format(domain)
|
||||
with open(resolver_file_name, "w") as resolver_file:
|
||||
for nameserver in nameservers:
|
||||
resolver_file.write("nameserver {}\n".format(nameserver))
|
||||
|
||||
def deconfigure_domain_vpn_dns(self, domains, nameservers):
|
||||
for domain in domains:
|
||||
resolver_file_name = "/etc/resolver/{0}".format(domain)
|
||||
if os.path.exists(resolver_file_name):
|
||||
os.remove(resolver_file_name)
|
||||
if not len(os.listdir('/etc/resolver')):
|
||||
os.removedirs('/etc/resolver')
|
||||
|
||||
|
||||
class PfFirewallProvider(FirewallProvider):
|
||||
def __init__(self):
|
||||
self.pfctl = get_executable('/sbin/pfctl')
|
||||
|
||||
_PF_TOKEN_RE = re.compile(r'Token : (\d+)')
|
||||
_PF_ANCHOR = 'vpn_slice'
|
||||
_PF_CONF_FILE = '/etc/pf.conf'
|
||||
|
||||
def _reload_conf(self):
|
||||
cmd = [self.pfctl, '-f', self._PF_CONF_FILE]
|
||||
p = subprocess.Popen(cmd, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
output, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise subprocess.CalledProcessError(p.returncode, cmd, output=output, stderr=stderr)
|
||||
|
||||
def configure_firewall(self, device):
|
||||
# Enabled Packet Filter - increments a reference counter for processes that need packet filter enabled
|
||||
cl = [self.pfctl, '-E']
|
||||
p = subprocess.Popen(cl, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
output, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise subprocess.CalledProcessError(p.returncode, cl, output=output, stderr=stderr)
|
||||
|
||||
# store token returned to later be able to decrement the reference counter correctly
|
||||
enable_token = None
|
||||
|
||||
for line in stderr.splitlines():
|
||||
match = self._PF_TOKEN_RE.search(line)
|
||||
if match:
|
||||
enable_token = match.group(1)
|
||||
|
||||
if not enable_token:
|
||||
print("WARNING: failed to get pf enable reference token, packet filter might not shutdown correctly")
|
||||
|
||||
anchor = '{}/{}'.format(self._PF_ANCHOR, device)
|
||||
# add anchor to generate rules with
|
||||
with open(self._PF_CONF_FILE, 'a') as file:
|
||||
file.write('anchor "{}" # vpn-slice-{} AUTOCREATED {}\n'.format(anchor, device, enable_token))
|
||||
|
||||
# reload config file
|
||||
self._reload_conf()
|
||||
|
||||
p = subprocess.Popen([self.pfctl, '-a', anchor, '-f', '-'],
|
||||
universal_newlines=True,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE)
|
||||
|
||||
rules = '''pass out on {0} all keep state
|
||||
block drop in on {0} all
|
||||
'''.format(device)
|
||||
|
||||
output, stderr = p.communicate(rules)
|
||||
if p.returncode != 0:
|
||||
raise subprocess.CalledProcessError(p.returncode, cl, output=output, stderr=stderr)
|
||||
|
||||
def deconfigure_firewall(self, device):
|
||||
# disable anchor
|
||||
anchor = '{}/{}'.format(self._PF_ANCHOR, device)
|
||||
subprocess.check_call([self.pfctl, '-a', anchor, '-F', 'all'])
|
||||
|
||||
with open(self._PF_CONF_FILE, 'r') as file:
|
||||
lines = file.readlines()
|
||||
|
||||
enable_tokens = []
|
||||
rule_re = re.compile(r'vpn-slice-{} AUTOCREATED (\d+)'.format(device))
|
||||
with open(self._PF_CONF_FILE, 'w') as file:
|
||||
for line in lines:
|
||||
match = rule_re.search(line)
|
||||
if match:
|
||||
enable_tokens.append(match.group(1))
|
||||
else:
|
||||
file.write(line)
|
||||
|
||||
# decrement pf enable reference counter
|
||||
for token in enable_tokens:
|
||||
cl = [self.pfctl, '-X', token]
|
||||
p = subprocess.Popen(cl, universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
output, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise subprocess.CalledProcessError(p.returncode, cl, output=output, stderr=stderr)
|
||||
|
||||
if not enable_tokens:
|
||||
print("WARNING: failed to get pf enable reference token, packet filter might not have shutdown correctly")
|
||||
|
||||
self._reload_conf()
|
||||
Reference in New Issue
Block a user