Example #1
0
    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)
Example #2
0
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
Example #3
0
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()
Example #4
0
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()
Example #5
0
    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_
Example #7
0
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)
Example #8
0
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()
Example #9
0
 def __enter__(self):
     ns = nsenter.Namespace(self._fs_location, 'net')
     self._calls.append(ns)
     return ns.__enter__()