Beispiel #1
0
def env(
    client_obj: client.VaultClientBase, path: Sequence[str], command: Sequence[str]
) -> NoReturn:
    """
    Launches a command, loading secrets in environment.

    Strings are exported as-is, other types (including booleans, nulls, dicts, lists)
    are exported as yaml (more specifically as json).
    """
    paths = list(path) or [""]

    env_secrets = {}

    for path in paths:
        secrets = client_obj.get_secrets(path)
        env_secrets.update(
            {
                environment.make_env_key(
                    path=path, key=key
                ): environment.make_env_value(value)
                for key, value in secrets.items()
            }
        )

    environ = os.environ.copy()
    environ.update(env_secrets)

    environment.exec_command(command=command, environ=environ)
Beispiel #2
0
def ssh_(
    client_obj: client.VaultClientBase,
    key: str,
    passphrase: Optional[str],
    command: Sequence[str],
) -> NoReturn:
    """
    Launch a command, with a configured ssh-agent running.

    The ssh agent has all the keys specified as `--key` arguments.

    The effect will be that any command launched through this wrapper will "magically"
    (thanks to ssh-agent) be able to use the secret keys given as `--key` for their
    ssh connections.
    """
    ssh.ensure_agent()

    if ":" not in key:
        raise click.UsageError("Format for private_key: `path/to/private_key:key`")
    private_key_name, private_key_key = key.rsplit(":", 1)
    private_key_secret_obj = client_obj.get_secret(private_key_name, private_key_key)
    private_key_secret = ensure_str(secret=private_key_secret_obj, path=key)

    passphrase_secret = None
    if passphrase:
        if ":" not in passphrase:
            raise click.UsageError("Format for passphrase: `path/to/passphrase:key`")
        passphrase_name, passphrase_key = passphrase.rsplit(":", 1)
        passphrase_secret_obj = client_obj.get_secret(passphrase_name, passphrase_key)
        passphrase_secret = ensure_str(secret=passphrase_secret_obj, path=passphrase)

    ssh.add_key(key=private_key_secret, passphrase=passphrase_secret)

    environment.exec_command(command=command)
Beispiel #3
0
def test_exec_command(mocker):
    execvpe = mocker.patch("os.execvpe")

    environment.exec_command(["a", "b"], {"C": "d"})

    execvpe.assert_called_with("a", ("a", "b"), mocker.ANY)
    args, __ = execvpe.call_args
    assert args[2]["C"] == "d"
Beispiel #4
0
def env(
    client_obj: client.VaultClientBase,
    path: Sequence[str],
    omit_single_key: bool,
    command: Sequence[str],
) -> NoReturn:
    """
    Launch a command, loading secrets in environment.

    Strings are exported as-is, other types (including booleans, nulls, dicts, lists)
    are exported JSON-encoded.

    If the path ends with `:key` then only one key of the mapping is used and its name is the name of the key.

    VARIABLE NAMES

    By default the name is build upon the relative path to the parent of the given path (in parameter) and the name of the keys in the value mapping.
    Let's say that we have stored the mapping `{'username': '******', 'password': '******'}` at path `a/b/c`

    Using `--path a/b` will inject the following environment variables: B_C_USERNAME and B_C_PASSWORD
    Using `--path a/b/c` will inject the following environment variables: C_USERNAME and C_PASSWORD
    Using `--path a/b/c:username` will only inject `USERNAME=me` in the environment.

    You can customize the variable names generation by appending `=SOME_PREFIX` to the path.
    In this case the part corresponding to the base path is replace by your prefix.

    Using `--path a/b=FOO` will inject the following environment variables: FOO_C_USERNAME and FOO_C_PASSWORD
    Using `--path a/b/c=FOO` will inject the following environment variables: FOO_USERNAME and FOO_PASSWORD
    Using `--path a/b/c:username=FOO` will inject `FOO=me` in the environment.
    """
    paths = list(path) or [""]

    env_secrets = {}

    for path in paths:
        path_with_key, _, prefix = path.partition("=")
        path, _, filter_key = path_with_key.partition(":")

        if filter_key:
            secret = client_obj.get_secret(path=path, key=filter_key)
            env_updates = environment.get_envvars_for_secret(key=filter_key,
                                                             secret=secret,
                                                             prefix=prefix)
        else:
            secrets = client_obj.get_secrets(path=path, relative=True)
            env_updates = environment.get_envvars_for_secrets(
                path=path,
                prefix=prefix,
                secrets=secrets,
                omit_single_key=omit_single_key,
            )
        env_secrets.update(env_updates)

    environ = os.environ.copy()
    environ.update(env_secrets)

    environment.exec_command(command=command, environ=environ)
Beispiel #5
0
def env(
    client_obj: client.VaultClientBase, path: Sequence[str], command: Sequence[str]
) -> NoReturn:
    """
    Launch a command, loading secrets in environment.

    Strings are exported as-is, other types (including booleans, nulls, dicts, lists)
    are exported as yaml (more specifically as json).

    VARIABLE NAMES

    If the path is not a folder the prefix value is used as the name of the
    variable.
    e.g.: for a/b/key, `--path a/b/c=foo` gives `FOO=...`

    If the path is a folder, then the variable names will be generated as:
    PREFIX + _ + relative path
    e.g.: for a/b/key, `--path a/b=my` gives `MY_KEY=...`

    The standard behavior when no prefix is set will use the relative path to
    the parent of the given path as variable name
    e.g.: for a/b/key, --path a/b gives `B_KEY=...`
    e.g.: for a/b/key, --path a/b/key gives `KEY=...`
    """
    paths = list(path) or [""]

    env_secrets = {}

    for path in paths:
        path, _, prefix = path.partition("=")
        secrets = client_obj.get_secrets(path)
        env_secrets.update(
            {
                environment.make_env_key(
                    path=path, prefix=prefix, key=key
                ): environment.make_env_value(value)
                for key, value in secrets.items()
            }
        )

    environ = os.environ.copy()
    environ.update(env_secrets)

    environment.exec_command(command=command, environ=environ)
Beispiel #6
0
def test_exec_command(mocker):
    execvpe = mocker.patch("os.execvpe")

    environment.exec_command(["a", "b"], {"c": "d"})

    execvpe.assert_called_with("a", ("a", "b"), {"c": "d"})
Beispiel #7
0
def env(
    client_obj: client.VaultClientBase,
    envvar: Sequence[str],
    file: Sequence[str],
    omit_single_key: bool,
    force: bool,
    command: Sequence[str],
) -> NoReturn:
    """
    Write secrets to disk, load secrets in environment variable, then  lauch a command.

    Secrets stored as strings are loaded as-is. Secrets of any other type exported as an
    environment variable are JSON-encoded. Secrets of any other type written to a file
    are YAML-encoded.

    LOADING ENVIRONMENT VARIABLES

    If the path ends with `:key` then only one key of the mapping is used and its name is the name of the key.

    By default the name is build upon the relative path to the parent of the given path (in parameter) and the name of the keys in the value mapping.
    Let's say that we have stored the mapping `{'username': '******', 'password': '******'}` at path `a/b/c`

    Using `--envvar a/b` will inject the following environment variables: B_C_USERNAME and B_C_PASSWORD
    Using `--envvar a/b/c` will inject the following environment variables: C_USERNAME and C_PASSWORD
    Using `--envvar a/b/c:username` will only inject `USERNAME=me` in the environment.

    You can customize the variable names generation by appending `=SOME_PREFIX` to the path.
    In this case the part corresponding to the base path is replace by your prefix.

    Using `--envvar a/b=FOO` will inject the following environment variables: FOO_C_USERNAME and FOO_C_PASSWORD
    Using `--envvar a/b/c=FOO` will inject the following environment variables: FOO_USERNAME and FOO_PASSWORD
    Using `--envvar a/b/c:username=FOO` will inject `FOO=me` in the environment.
    """
    envvars = list(envvar) or []
    files = list(file) or []

    env_secrets = {}

    for path in envvars:
        path, key, prefix = get_env_parts(path)
        if not path and key:
            raise click.BadOptionUsage(
                "envvar", "Cannot omit the path if a filter key is provided"
            )

        env_updates = {}
        env_updates = environment.get_envvars(
            vault_client=client_obj,
            path=path,
            prefix=prefix,
            omit_single_key=omit_single_key,
            filter_key=key,
        )

        env_secrets.update(env_updates)

    for file in files:
        path, key, filesystem_path = get_env_parts(file)
        if not (path and filesystem_path):
            raise click.BadOptionUsage(
                "file", "--file expects both a vault path and a filesystem path"
            )
        secret_obj = client_obj.get_secret(path=path, key=key or None)

        if not isinstance(secret_obj, (list, dict)):
            secret = str(secret_obj).strip() + "\n"
        else:
            secret = yaml.safe_dump(
                secret_obj, default_flow_style=False, explicit_start=True
            )
        pathlib.Path(filesystem_path).write_text(secret)

    if bool(client_obj.errors) and not force:
        raise click.ClickException("There was an error while reading the secrets.")
    environment.exec_command(command=command, environment=env_secrets)
Beispiel #8
0
def ensure_agent() -> None:
    if "SSH_AUTH_SOCK" not in os.environ:
        wrapper_command = ["ssh-agent"] + sys.argv
        env_module.exec_command(command=wrapper_command)