Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacOS implementation of domain specific use of vpn dns #72

Merged
merged 2 commits into from
Jan 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion vpn_slice/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ def get_default_providers():
prep = CheckTunDevProvider,
)
elif platform.startswith('darwin'):
from .mac import PsProvider, BSDRouteProvider
from .mac import PsProvider, BSDRouteProvider, MacSplitDNSProvider
from .posix import PosixHostsFileProvider
from .dnspython import DNSPythonProvider
return dict(
process = PsProvider,
route = BSDRouteProvider,
dns = DNSPythonProvider or DigProvider,
hosts = PosixHostsFileProvider,
domain_vpn_dns = MacSplitDNSProvider,
)
elif platform.startswith('freebsd'):
from .mac import BSDRouteProvider
Expand Down Expand Up @@ -134,6 +135,13 @@ def do_disconnect(env, args):
except sp.CalledProcessError:
print("WARNING: failed to deconfigure firewall for VPN interface (%s)" % env.tundev, file=stderr)

if args.vpn_domains is not None:
try:
providers.domain_vpn_dns.deconfigure_domain_vpn_dns(args.vpn_domains, env.dns)
except:
print("WARNING: failed to deconfigure domains vpn dns", file=stderr)


def do_connect(env, args):
global providers
if args.banner and env.banner:
Expand Down Expand Up @@ -219,6 +227,14 @@ def do_connect(env, args):
if args.verbose:
print("Restored routes for %d excluded subnets." % len(exc_subnets), exc_subnets, file=stderr)

# Use vpn dns for provided domains
if args.vpn_domains is not None:
if 'domain_vpn_dns' not in providers:
print("WARNING: no split dns provider available; can't split dns", file=stderr)
else:
providers.domain_vpn_dns.configure_domain_vpn_dns(args.vpn_domains, env.dns)


def do_post_connect(env, args):
global providers
# lookup named hosts for which we need routes and/or host_map entries
Expand Down Expand Up @@ -429,6 +445,7 @@ def parse_args_and_env(args=None, environ=os.environ):
g.add_argument('-D','--dump', action='store_true', help='Dump environment variables passed by caller')
g.add_argument('--no-fork', action='store_false', dest='fork', help="Don't fork and continue in background on connect")
g.add_argument('--ppid', type=int, help='PID of calling process (normally autodetected, when using openconnect or vpnc)')
g.add_argument('--domains-vpn-dns', dest='vpn_domains', default=None, help="comma seperated domains to query with vpn dns")
args = p.parse_args(args)
env = parse_env(environ)
return p, args, env
Expand Down Expand Up @@ -470,6 +487,9 @@ def finalize_args_and_env(args, env):
if args.route_splits:
args.subnets.extend(env.splitinc)
args.exc_subnets.extend(env.splitexc)
if args.vpn_domains is not None:
args.vpn_domains = str.split(args.vpn_domains, ',')


def main(args=None, environ=os.environ):
global providers
Expand Down
21 changes: 20 additions & 1 deletion vpn_slice/mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ipaddress import ip_network, ip_interface

from .posix import PosixProcessProvider
from .provider import RouteProvider
from .provider import RouteProvider, SplitDNSProvider
from .util import get_executable


Expand Down Expand Up @@ -112,3 +112,22 @@ def add_address(self, device, address):
# 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')
15 changes: 15 additions & 0 deletions vpn_slice/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,18 @@ def prepare_tunnel(self):
Base class behavior is to do nothing.

"""

class SplitDNSProvider:
def configure_domain_vpn_dns(self, domains, nameservers):
"""Configure domain vpn dns.

Base class behavior is to do nothing.

"""

def deconfigure_domain_vpn_dns(self, domains, nameservers):
"""Remove domain vpn dns.

Base class behavior is to do nothing.

"""