def __call__(self, iface, push_entry): pkt = self._encode(push_entry) ns_name = self.context.make_network_namespace_name() ns_fd = self.ns_fs_location / ns_name with nsenter.Namespace(str(ns_fd), 'net'): sendp(pkt, iface=iface.name, verbose=False, promisc=False)
def do_command(commandline): """Run a command in a user session inside the bottle, initializing it if necessary.""" if verbose: print("genie: running command " + ' '.join(commandline)) sdp = find_systemd() if sdp == 1: # we're already inside the bottle ic = subprocess.run(commandline) return ic.returncode pre_systemd_action_checks(sdp) sdp = find_systemd() command = [ "machinectl", "shell", "-q", login + "@.host", "/usr/lib/genie/runinwsl", os.getcwd() ] + commandline with nsenter.Namespace(sdp, 'pid'): sp = subprocess.run(' '.join(command), shell=True) return sp
def do_shutdown(): """Shutdown the genie bottle and clean up.""" sdp = find_systemd() if sdp == 0: sys.exit("genie: no bottle exists") if sdp == 1: sys.exit("genie: cannot shut down bottle from inside bottle") state = get_systemd_state(sdp) if 'starting' in state or 'stopping' in state: sys.exit( f"genie: bottle is currently {state}; please wait until it is in a stable state" ) if verbose: print("genie: running systemctl poweroff within bottle") with nsenter.Namespace(sdp, 'pid'): subprocess.run(["systemctl", "poweroff"]) # Wait for systemd to exit. print("Waiting for systemd to exit...", end="", flush=True) timeout = config_system_timeout() while find_systemd() != 0 and timeout > 0: time.sleep(1) print(".", end="", flush=True) timeout -= 1 print("") if (timeout <= 0): print( "genie: systemd did not exit after {config_system_timeout()} seconds" ) print( "genie: this may be due to a problem with your systemd configuration" ) print("genie: attempting to continue") # Reverse the processes we performed to prepare the bottle as the post-shutdown # cleanup, only in reverse. if os.path.exists('/sys/module/apparmor'): apparmor_unconfigure() binfmts_mount() if config_update_hostname(): hostname_restore()
def get_systemd_state(sdp): """Get the systemd state, whether we are within or without the bottle.""" if sdp == 0: return "offline" with nsenter.Namespace(sdp, 'pid'): sc = subprocess.run(["systemctl", "is-system-running"], capture_output=True, text=True) return sc.stdout.rstrip()
def __call__(self, iface, push_entry): data = "TEST" ether = Ether(src=push_entry.src_mac_address, dst=push_entry.dst_mac_address, type=push_entry.eth_type) ip = IP(src=push_entry.src_ip, dst=push_entry.dst_ip) udp = UDP(sport=push_entry.src_port, dport=push_entry.dst_port) pkt = ether / ip / udp / data ns_name = self.context.make_network_namespace_name() ns_fd = self.ns_fs_location / ns_name with nsenter.Namespace(str(ns_fd), 'net'): sendp(pkt, iface=iface.name, verbose=False, promisc=False)
def host_mnt_exec(cmd): try: with nsenter.ExitStack() as stack: stack.enter_context( nsenter.Namespace('1', 'mnt', proc='/var/lib/kolla/host_proc/')) process_ = subprocess.Popen(cmd) # nosec except Exception as e: print("An error has occurred with a component that Kolla manages." " Please file a bug") print("Error: ", e) return process_
def do_login(): """Start a login prompt inside the bottle, initializing it if necessary.""" if verbose: print("genie: starting login prompt") pre_systemd_action_checks(find_systemd()) sdp = find_systemd() if sdp == 1: # we're already inside the bottle sys.exit("genie: already inside the bottle; cannot proceed") # At this point, we should be outside a bottle, one way or another. # Get the bottle namespace with nsenter.Namespace(sdp, 'pid'): subprocess.run("machinectl login .host", shell=True)
def do_initialize(): """Initialize the genie bottle.""" if verbose: print("genie: starting bottle") # Secure the bottle init lock running = bottle_init_lock() if running: # Wait for other process to have started the bottle # The last step is the pid file being created, so we wait # for that, then return. if verbose: print(f"genie: already initializing, pid={running}, waiting...", end="", flush=True) # Allow 10% startup margin timeout = config_system_timeout() * 1.1 while not os.path.exists('/run/genie.systemd.pid') and timeout > 0: time.sleep(1) print(".", end="", flush=True) timeout -= 1 print("") if timeout <= 0: print("genie: WARNING: timeout waiting for bottle to start") return sdp = find_systemd() if sdp != 0: sys.exit("genie: bottle is already established (systemd running)") # FIRST: As a first step in initing systemd, delete any old runtime pid file # if such exists. if os.path.exists('/run/genie.systemd.pid'): os.remove('/run/genie.systemd.pid') # Set secure path, and stash original environment. set_secure_path() stash_environment() # Check and warn if not multi-user.target. if config_target_warning(): target = get_systemd_target() if target != 'multi-user.target': print( f"genie: WARNING: systemd default target is {target}; targets other than multi-user.target may not work" ) print( "genie: WARNING: if you wish to use a different target, this warning can be disabled in the config file" ) print( "genie: WARNING: if you experience problems, please change the target to multi-user.target" ) # Now that the WSL hostname can be set via .wslconfig, we're going to make changing # it automatically in genie an option, enable/disable in genie.ini. Defaults to on # for backwards compatibility and because not doing so when using bridged networking is # a Bad Idea. if config_update_hostname(): hostname_update() # If configured to, create the resolv.conf symlink for systemd-resolved. if config_resolved_stub(): resolved_configure() # Unmount the binfmts fs before starting systemd, so systemd can mount it # again with all the trimmings. binfmts_umount() # Define systemd startup chain. startupChain = [ "daemonize", get_unshare_path(), "-fp", "--propagation", "shared", "--mount-proc", "--" ] # Check whether AppArmor is available in the kernel. if os.path.exists('/sys/module/apparmor'): # If so, configure AppArmor. nsName = apparmor_configure() # Add AppArmor to the startup chain. if nsName is not None: startupChain = startupChain + [ "aa-exec", "-n", nsName, "-p", "unconfined", "--" ] else: if verbose: print( "genie: AppArmor not available in kernel; attempting to continue without AppArmor namespace" ) # Update startup chain with systemd command. startupChain.append("systemd") # Run systemd in a container if verbose: print("genie: starting systemd with command line: ") print(' '.join(startupChain)) # This requires real UID/GID root as well as effective UID/GID root suid = os.getuid() sgid = os.getgid() os.setuid(0) os.setgid(0) subprocess.run(startupChain) os.setuid(suid) os.setgid(sgid) # Wait for systemd to be up (polling, sigh.) sdp = 0 print("Waiting for systemd...", end="", flush=True) while sdp == 0: time.sleep(0.5) sdp = find_systemd() print(".", end="", flush=True) # Wait for systemd to be in running state. state = 'initializing' timeout = config_system_timeout() while (not 'running' in state) and timeout > 0: time.sleep(1) state = get_systemd_state(sdp) print("!", end="", flush=True) timeout -= 1 print("") if not 'running' in state: print( f"genie: systemd did not enter running state ({state}) after {config_system_timeout()} seconds" ) print( "genie: this may be due to a problem with your systemd configuration" ) print( "genie: information on problematic units is available at https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL" ) print("genie: a list of failed units follows:\n") with nsenter.Namespace(sdp, 'pid'): subprocess.run(["systemctl", "--failed"]) # LAST: Now that systemd exists, write out its (external) pid. # We do not need to store the inside-bottle pid anywhere for obvious reasons. with open('/run/genie.systemd.pid', 'w') as pidfile: print(sdp, file=pidfile) pidfile.close() # Unlock the init lock bottle_init_unlock()
def __enter__(self): ns = nsenter.Namespace(self._fs_location, 'net') self._calls.append(ns) return ns.__enter__()