|
|
|
@ -15,7 +15,6 @@ try:
|
|
|
|
|
YAML_SUPPORTED = True
|
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
|
YAML_SUPPORTED = False
|
|
|
|
|
yaml = NotImplemented
|
|
|
|
|
|
|
|
|
|
WIREGUARD_DIR = Path('/etc/wireguard')
|
|
|
|
|
NETNS_DIR = Path('/etc/netns')
|
|
|
|
@ -65,10 +64,6 @@ def cli(args):
|
|
|
|
|
parser = subparsers.add_parser('switch', help='open shell in namespace')
|
|
|
|
|
parser.add_argument('netns', metavar='NETNS', help='network namespace name')
|
|
|
|
|
|
|
|
|
|
parser = subparsers.add_parser('exec', help='run command in namespace')
|
|
|
|
|
parser.add_argument('netns', metavar='NETNS', help='network namespace name')
|
|
|
|
|
parser.add_argument('command', nargs='+', help='command')
|
|
|
|
|
|
|
|
|
|
opts = entrypoint.parse_args(args)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
@ -101,8 +96,6 @@ def cli(args):
|
|
|
|
|
print('\n'.join(item['name'] for item in data))
|
|
|
|
|
elif opts.action == 'switch':
|
|
|
|
|
os.execvp('sudo', ['ip', 'ip', 'netns', 'exec', opts.netns, 'sudo', '-u', getpass.getuser(), '-D', Path.cwd().as_posix(), os.environ['SHELL'], '-i'])
|
|
|
|
|
elif opts.action == 'exec':
|
|
|
|
|
os.execvp('sudo', ['ip', 'ip', 'netns', 'exec', opts.netns, 'sudo', '-u', getpass.getuser(), '-D', Path.cwd().as_posix(), *opts.command])
|
|
|
|
|
else:
|
|
|
|
|
raise RuntimeError('congratulations, you reached unreachable code')
|
|
|
|
|
|
|
|
|
@ -179,17 +172,17 @@ class Interface:
|
|
|
|
|
if self.private_key:
|
|
|
|
|
wg('set', self.name, 'private-key', '/dev/stdin', stdin=self.private_key, netns=self.base_netns)
|
|
|
|
|
|
|
|
|
|
def _assign_namespace(self, namespace: str|None) -> None:
|
|
|
|
|
ip('link', 'set', self.name, 'netns', namespace if namespace else '1', netns=self.base_netns)
|
|
|
|
|
def _assign_namespace(self, namespace: str) -> None:
|
|
|
|
|
ip('link', 'set', self.name, 'netns', namespace, netns=self.base_netns)
|
|
|
|
|
|
|
|
|
|
def _assign_addresses(self, namespace: str|None) -> None:
|
|
|
|
|
def _assign_addresses(self, namespace: str) -> None:
|
|
|
|
|
for address in self.address:
|
|
|
|
|
ip('-6' if ':' in address else '-4', 'address', 'add', address, 'dev', self.name, netns=namespace)
|
|
|
|
|
|
|
|
|
|
def _bring_up(self, namespace: str|None) -> None:
|
|
|
|
|
def _bring_up(self, namespace: str) -> None:
|
|
|
|
|
ip('link', 'set', 'dev', self.name, 'mtu', self.mtu, 'up', netns=namespace)
|
|
|
|
|
|
|
|
|
|
def _create_routes(self, namespace: str|None):
|
|
|
|
|
def _create_routes(self, namespace: str):
|
|
|
|
|
for peer in self.peers:
|
|
|
|
|
networks = peer.routes if peer.routes is not None else peer.allowed_ips
|
|
|
|
|
for network in networks:
|
|
|
|
@ -224,8 +217,8 @@ class ScriptletItem:
|
|
|
|
|
host_namespace = bool(data.pop('host_namespace', None))
|
|
|
|
|
return cls(**data, host_namespace=host_namespace)
|
|
|
|
|
|
|
|
|
|
def run(self, netns: str|None):
|
|
|
|
|
if self.host_namespace or not netns:
|
|
|
|
|
def run(self, netns: str):
|
|
|
|
|
if self.host_namespace:
|
|
|
|
|
host_eval(self.command)
|
|
|
|
|
else:
|
|
|
|
|
ip_netns_eval(self.command, netns=netns)
|
|
|
|
@ -254,14 +247,14 @@ class Scriptlet:
|
|
|
|
|
item = ScriptletItem.from_str(data)
|
|
|
|
|
return cls(items=[item])
|
|
|
|
|
|
|
|
|
|
def run(self, netns: str|None):
|
|
|
|
|
def run(self, netns: str):
|
|
|
|
|
for item in self.items:
|
|
|
|
|
item.run(netns=netns)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class Namespace:
|
|
|
|
|
name: str|None
|
|
|
|
|
name: str
|
|
|
|
|
pre_up: Optional[Scriptlet] = None
|
|
|
|
|
post_up: Optional[Scriptlet] = None
|
|
|
|
|
pre_down: Optional[Scriptlet] = None
|
|
|
|
@ -309,7 +302,7 @@ class Namespace:
|
|
|
|
|
return cls(**data, **scriptlets, interfaces=interfaces) # type: ignore
|
|
|
|
|
|
|
|
|
|
def setup(self) -> Namespace:
|
|
|
|
|
if self.managed and self.name:
|
|
|
|
|
if self.managed:
|
|
|
|
|
self._create()
|
|
|
|
|
self._write_resolvconf()
|
|
|
|
|
if self.pre_up:
|
|
|
|
@ -345,7 +338,6 @@ class Namespace:
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def _resolvconf_path(self) -> Path:
|
|
|
|
|
assert self.name
|
|
|
|
|
return NETNS_DIR/self.name/'resolv.conf'
|
|
|
|
|
|
|
|
|
|
def _write_resolvconf(self) -> None:
|
|
|
|
@ -378,8 +370,8 @@ def ip_netns_exec(*args, netns: str, stdin: str|None = None, check=True, capture
|
|
|
|
|
return ip('netns', 'exec', netns, *args, stdin=stdin, check=check, capture=capture)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ip(*args, stdin: str|None = None, netns: str|None =None, check=True, capture=False) -> str:
|
|
|
|
|
return run('ip', *(['-n', netns] if netns else []), *args, stdin=stdin, check=check, capture=capture)
|
|
|
|
|
def ip(*args, stdin: str|None = None, netns=None, check=True, capture=False) -> str:
|
|
|
|
|
return run('ip', *([] if netns is None else ['-n', netns]), *args, stdin=stdin, check=check, capture=capture)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def host_eval(*args, stdin: str|None = None, check=True, capture=False) -> str:
|
|
|
|
|