Exemplo n.º 1
0
    def from_params(cls, data, ref_params):
        """
        Return new GPPSecret from data and ref_params: target_name
        recipients will be grabbed from the inventory via target_name
        """
        try:
            # XXX only used for testing
            if GPG_TARGET_FINGERPRINTS:
                _fingerprints = [{"fingerprint": v} for _, v in GPG_TARGET_FINGERPRINTS.items()]
                return cls(data, _fingerprints, **ref_params.kwargs)

            target_name = ref_params.kwargs["target_name"]
            if target_name is None:
                raise ValueError("target_name not set")

            target_inv = cached.inv["nodes"].get(target_name, None)
            if target_inv is None:
                raise ValueError("target_inv not set")

            if "secrets" not in target_inv["parameters"]["kapitan"]:
                raise KapitanError(
                    f"parameters.kapitan.secrets not defined in inventory of target {target_name}"
                )

            recipients = target_inv["parameters"]["kapitan"]["secrets"]["gpg"]["recipients"]

            return cls(data, recipients, **ref_params.kwargs)
        except KeyError:
            raise RefError("Could not create GPGSecret: target_name missing")
Exemplo n.º 2
0
def ref_reveal(args, ref_controller):
    "Reveal secrets in file_name"
    revealer = Revealer(ref_controller)
    file_name = args.file
    reffile_name = args.ref_file
    tag_name = args.tag

    if file_name is None and reffile_name is None and tag_name is None:
        fatal_error("--file or --ref-file is required with --reveal")
    try:
        if file_name == "-" or reffile_name == "-":
            out = revealer.reveal_raw_file(None)
            sys.stdout.write(out)
        elif file_name:
            for rev_obj in revealer.reveal_path(file_name):
                sys.stdout.write(rev_obj.content)
        elif reffile_name:
            ref = ref_controller.ref_from_ref_file(reffile_name)
            sys.stdout.write(ref.reveal())
        elif tag_name:
            out = revealer.reveal_raw_string(tag_name)
            sys.stdout.write(out)
    except (RefHashMismatchError, KeyError):
        raise KapitanError(
            "Reveal failed for file {name}".format(name=file_name))
Exemplo n.º 3
0
    def from_params(cls, data, ref_params):
        """
        Return new GPPSecret from data and ref_params: target_name
        recipients will be grabbed from the inventory via target_name
        """
        try:
            # XXX only used for testing
            if GPG_TARGET_FINGERPRINTS:
                _fingerprints = [{
                    'fingerprint': v
                } for _, v in GPG_TARGET_FINGERPRINTS.items()]
                return cls(data, _fingerprints, **ref_params.kwargs)

            target_name = ref_params.kwargs['target_name']
            if target_name is None:
                raise ValueError('target_name not set')

            target_inv = cached.inv['nodes'].get(target_name, None)
            if target_inv is None:
                raise ValueError('target_inv not set')

            if 'secrets' not in target_inv['parameters']['kapitan']:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        target_name))

            recipients = target_inv['parameters']['kapitan']['secrets']['gpg'][
                'recipients']

            return cls(data, recipients, **ref_params.kwargs)
        except KeyError:
            raise RefError("Could not create GPGSecret: target_name missing")
Exemplo n.º 4
0
def compile_targets(inventory_path, search_path, output_path, parallel, targets, **kwargs):
    """
    Searches and loads target files, and runs compile_target_file() on a
    multiprocessing pool with parallel number of processes.
    kwargs are passed to compile_target()
    """
    # temp_path will hold compiled items
    temp_path = tempfile.mkdtemp(suffix='.kapitan')

    target_objs = load_target_inventory(inventory_path, targets)

    pool = multiprocessing.Pool(parallel)
    # append "compiled" to output_path so we can safely overwrite it
    compile_path = os.path.join(output_path, "compiled")
    worker = partial(compile_target, search_path=search_path, compile_path=temp_path, **kwargs)

    try:
        if target_objs == []:
            logger.error("Error: no targets found")
            raise KapitanError("Error: no targets found")
        # compile_target() returns None on success
        # so p is only not None when raising an exception
        [p.get() for p in pool.imap_unordered(worker, target_objs) if p]

        if not os.path.exists(compile_path):
            os.makedirs(compile_path)

        # if '-t' is set on compile, only override selected targets
        if targets:
            for target in targets:
                compile_path_target = os.path.join(compile_path, target)
                temp_path_target = os.path.join(temp_path, target)

                if not os.path.exists(compile_path_target):
                    os.makedirs(compile_path_target)

                shutil.rmtree(compile_path_target)
                shutil.copytree(temp_path_target, compile_path_target)
                logger.debug("Copied %s into %s", temp_path_target, compile_path_target)
        # otherwise override all targets
        else:
            shutil.rmtree(compile_path)
            shutil.copytree(temp_path, compile_path)
            logger.debug("Copied %s into %s", temp_path, compile_path)

    except Exception as e:
        # if compile worker fails, terminate immediately
        pool.terminate()
        pool.join()
        logger.debug("Compile pool terminated")
        # only print traceback for errors we don't know about
        if not isinstance(e, KapitanError):
            logger.error("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
            traceback.print_exc()
        sys.exit(1)
    finally:
        shutil.rmtree(temp_path)
        logger.debug("Removed %s", temp_path)
Exemplo n.º 5
0
def lint_unused_classes(inventory_path):
    """ Checks your inventory for unused classes by:
        - iterating the inventory_path/classes/ dir and extracting all class names from the file paths
        - converting those file paths to class references (e.g. component/mysql -> component.mysql)
        - does a text search over the entire inventory_path/ to find usages of those classes
    Args:
        inventory_path (string): path to your inventory folder
    Yields:
        checks_sum (int): the number of unused classes found
    """
    classes_dir = os.path.join(inventory_path, "classes/")
    if not os.path.isdir(classes_dir):
        raise KapitanError(
            "{} is not a valid directory or does not exist".format(
                classes_dir))

    logger.debug("Find unused classes from {}".format(classes_dir))
    class_paths = set()
    for path in list_all_paths(classes_dir):
        if os.path.isfile(path) and (path.endswith('.yml')
                                     or path.endswith('.yaml')):
            path = path[len(classes_dir):]
            path = path.replace(".yml", "").replace(".yaml",
                                                    "").replace("/", ".")
            class_paths.add(path)

    logger.debug("Collected # of paths: {}".format(len(class_paths)))
    logger.debug("Checking if all classes are declared in " + classes_dir)

    for path in list_all_paths(inventory_path):
        if os.path.isfile(path):
            with open(path, "r") as compiled_file:
                file_contents = compiled_file.read()
                for class_path in list(class_paths):
                    exists = class_path in file_contents
                    """
                    Classes files may reside in subdirectories, which act as namespaces.
                    For instance, a class ssh.server will result in the class definition to be read from ssh/server.yml.
                    Specifying just ssh will cause the class data to be read from ssh/init.yml or ssh.yml.
                    Note, however, that only one of those two may be present.
                    Thus we also check for ".init" being used in the class here to cover that case.
                    https://reclass.pantsfullofunix.net/operations.html
                    """
                    if class_path.endswith(".init"):
                        exists = (class_path[:-5] in file_contents) or (exists)

                    if exists:
                        class_paths.discard(class_path)

    checks_sum = len(class_paths)
    if checks_sum > 0:
        logger.info(
            "No usage found for the following {} classes:\n{}\n".format(
                len(class_paths), pformat(class_paths)))

    return checks_sum
Exemplo n.º 6
0
def secret_write(args, ref_controller):
    "Write secret to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error('--file is required with --write')
    if file_name == '-':
        data = ''
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            try:
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['gpg']['recipients']
            except KeyError:
                # TODO: Keeping gpg recipients backwards-compatible until we make a breaking release
                logger.warning(
                    "WARNING: parameters.kapitan.secrets.recipients is deprecated, "
                    +
                    "please move them to parameters.kapitan.secrets.gpg.recipients"
                )
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['recipients']
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = '?{{gpg:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            key = inv['nodes'][args.target_name]['parameters']['kapitan'][
                'secrets']['gkms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify in parameters.kapitan.secrets.gkms.key and use --target"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{gkms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj
    else:
        fatal_error("Invalid token: {}".format(token_name))
Exemplo n.º 7
0
def secret_update(args, ref_controller):
    "Update secret gpg recipients/gkms/awskms key"
    # TODO --update *might* mean something else for other types
    token_name = args.update
    if token_name.startswith("gpg:"):
        # args.recipients is a list, convert to recipients dict
        recipients = [dict([("name", name), ]) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError("parameters.kapitan.secrets not defined in {}".format(args.target_name))

            recipients = kap_inv_params['secrets']['gpg']['recipients']
        if not recipients:
            raise KapitanError("No GPG recipients specified. Use --recipients or specify them in " +
                               "parameters.kapitan.secrets.gpg.recipients and use --target")
        type_name, token_path = token_name.split(":")
        tag = '?{{gpg:{}}}'.format(token_path)
        secret_obj = ref_controller[tag]
        secret_obj.update_recipients(recipients)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError("parameters.kapitan.secrets not defined in {}".format(args.target_name))

            key = kap_inv_params['secrets']['gkms']['key']
        if not key:
            raise KapitanError("No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.gkms.key and use --target")
        type_name, token_path = token_name.split(":")
        tag = '?{{gkms:{}}}'.format(token_path)
        secret_obj = ref_controller[tag]
        secret_obj.update_key(key)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("awskms:"):
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError("parameters.kapitan.secrets not defined in {}".format(args.target_name))

            key = kap_inv_params['secrets']['awskms']['key']
        if not key:
            raise KapitanError("No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.awskms.key and use --target")
        type_name, token_path = token_name.split(":")
        tag = '?{{awskms:{}}}'.format(token_path)
        secret_obj = ref_controller[tag]
        secret_obj.update_key(key)
        ref_controller[tag] = secret_obj

    else:
        fatal_error("Invalid token: {name}. Try using gpg/gkms/awskms:{name}".format(name=token_name))
Exemplo n.º 8
0
def ref_reveal(args, ref_controller):
    "Reveal secrets in file_name"
    revealer = Revealer(ref_controller)
    file_name = args.file
    if file_name is None:
        fatal_error('--file is required with --reveal')
    try:
        if file_name == '-':
            out = revealer.reveal_raw_file(None)
            sys.stdout.write(out)
        elif file_name:
            for rev_obj in revealer.reveal_path(file_name):
                sys.stdout.write(rev_obj.content)
    except (RefHashMismatchError, KeyError):
        raise KapitanError("Reveal failed for file {name}".format(name=file_name))
Exemplo n.º 9
0
def compile_targets(target_path, search_path, output_path, parallel, **kwargs):
    """
    Searches and loads target files in target_path and runs compile_target_file() on a
    multiprocessing pool with parallel number of processes.
    kwargs are passed to compile_target_file()
    """
    # temp_path will hold compiled items
    temp_path = tempfile.mkdtemp(suffix='.kapitan')
    pool = multiprocessing.Pool(parallel)
    # append "compiled" to output_path so we can safely overwrite it
    compile_path = os.path.join(output_path, "compiled")
    worker = partial(compile_target_file,
                     search_path=search_path,
                     compile_path=temp_path,
                     **kwargs)
    target_files = search_target_files(target_path)
    try:
        if target_files == []:
            logger.error("Error: no target files found")
            raise KapitanError("Error: no target files found")
        pool.map(worker, target_files)
        if os.path.exists(compile_path):
            shutil.rmtree(compile_path)
        # on success, copy temp_path into compile_path
        shutil.copytree(temp_path, compile_path)
        logger.debug("Copied %s into %s", temp_path, compile_path)
    except Exception as e:
        # if compile worker fails, terminate immediately
        pool.terminate()
        pool.join()
        logger.debug("Compile pool terminated")
        # only print traceback for errors we don't know about
        if not isinstance(e, KapitanError):
            logger.error("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
            traceback.print_exc()
        sys.exit(1)
    finally:
        shutil.rmtree(temp_path)
        logger.debug("Removed %s", temp_path)
Exemplo n.º 10
0
    def from_params(cls, data, ref_params):
        """
        Return new GPPSecret from data and ref_params: target_name
        recipients will be grabbed from the inventory via target_name
        """
        try:
            # XXX only used for testing
            if GPG_TARGET_FINGERPRINTS:
                _fingerprints = [{
                    'fingerprint': v
                } for _, v in GPG_TARGET_FINGERPRINTS.items()]
                return cls(data, _fingerprints, **ref_params.kwargs)

            target_name = ref_params.kwargs['target_name']
            target_inv = cached.inv['nodes'].get(target_name, None)
            if target_name is None:
                raise ValueError('target_name not set')
            if target_inv is None:
                raise ValueError('target_inv not set')

            if 'secrets' not in target_inv['parameters']['kapitan']:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        target_name))

            try:
                recipients = target_inv['parameters']['kapitan']['secrets'][
                    'gpg']['recipients']
            except KeyError:
                # TODO: Keeping gpg recipients backwards-compatible until we make a breaking release
                logger.warning(
                    "WARNING: parameters.kapitan.secrets.recipients is deprecated, "
                    + "please use parameters.kapitan.secrets.gpg.recipients")
                recipients = target_inv['parameters']['kapitan']['secrets'][
                    'recipients']
            return cls(data, recipients, **ref_params.kwargs)
        except KeyError:
            raise RefError("Could not create GPGSecret: target_name missing")
Exemplo n.º 11
0
def secret_update_validate(args, ref_controller):
    "Validate and/or update target secrets"
    # update gpg recipients/gkms/awskms key for all secrets in secrets_path
    # use --refs-path to set scanning path
    inv = inventory_reclass(args.inventory_path)
    targets = set(inv["nodes"].keys())
    secrets_path = os.path.abspath(args.refs_path)
    target_token_paths = search_target_token_paths(secrets_path, targets)
    ret_code = 0

    for target_name, token_paths in target_token_paths.items():
        kap_inv_params = inv["nodes"][target_name]["parameters"]["kapitan"]
        if "secrets" not in kap_inv_params:
            raise KapitanError(
                "parameters.kapitan.secrets not defined in {}".format(
                    target_name))

        try:
            recipients = kap_inv_params["secrets"]["gpg"]["recipients"]
        except KeyError:
            recipients = None
        try:
            gkey = kap_inv_params["secrets"]["gkms"]["key"]
        except KeyError:
            gkey = None
        try:
            awskey = kap_inv_params["secrets"]["awskms"]["key"]
        except KeyError:
            awskey = None
        try:
            vaultkv = kap_inv_params["secrets"]["vaultkv"]["auth"]
        except KeyError:
            vaultkv = None

        for token_path in token_paths:
            if token_path.startswith("?{gpg:"):
                if not recipients:
                    logger.debug(
                        "secret_update_validate: target: %s has no inventory gpg recipients, skipping %s",
                        target_name,
                        token_path,
                    )
                    continue
                secret_obj = ref_controller[token_path]
                target_fingerprints = set(lookup_fingerprints(recipients))
                secret_fingerprints = set(
                    lookup_fingerprints(secret_obj.recipients))
                if target_fingerprints != secret_fingerprints:
                    if args.validate_targets:
                        logger.info("%s recipient mismatch", token_path)
                        to_remove = secret_fingerprints.difference(
                            target_fingerprints)
                        to_add = target_fingerprints.difference(
                            secret_fingerprints)
                        if to_remove:
                            logger.info("%s needs removal", to_remove)
                        if to_add:
                            logger.info("%s needs addition", to_add)
                        ret_code = 1
                    else:
                        new_recipients = [
                            dict([
                                ("fingerprint", f),
                            ]) for f in target_fingerprints
                        ]
                        secret_obj.update_recipients(new_recipients)
                        ref_controller[token_path] = secret_obj

            elif token_path.startswith("?{gkms:"):
                if not gkey:
                    logger.debug(
                        "secret_update_validate: target: %s has no inventory gkms key, skipping %s",
                        target_name,
                        token_path,
                    )
                    continue
                secret_obj = ref_controller[token_path]
                if gkey != secret_obj.key:
                    if args.validate_targets:
                        logger.info("%s key mismatch", token_path)
                        ret_code = 1
                    else:
                        secret_obj.update_key(gkey)
                        ref_controller[token_path] = secret_obj

            elif token_path.startswith("?{awskms:"):
                if not awskey:
                    logger.debug(
                        "secret_update_validate: target: %s has no inventory awskms key, skipping %s",
                        target_name,
                        token_path,
                    )
                    continue
                secret_obj = ref_controller[token_path]
                if awskey != secret_obj.key:
                    if args.validate_targets:
                        logger.info("%s key mismatch", token_path)
                        ret_code = 1
                    else:
                        secret_obj.update_key(awskey)
                        ref_controller[token_path] = secret_obj

            else:
                logger.info("Invalid secret %s, could not get type, skipping",
                            token_path)

    sys.exit(ret_code)
Exemplo n.º 12
0
def ref_write(args, ref_controller):
    "Write ref to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error("--file is required with --write")
    if file_name == "-":
        data = ""
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            recipients = kap_inv_params["secrets"]["gpg"]["recipients"]
        if not recipients:
            raise KapitanError(
                "No GPG recipients specified. Use --recipients or specify them in "
                +
                "parameters.kapitan.secrets.gpg.recipients and use --target-name"
            )
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = "?{{gpg:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            key = kap_inv_params["secrets"]["gkms"]["key"]
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.gkms.key and use --target-name"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = "?{{gkms:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("awskms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            key = kap_inv_params["secrets"]["awskms"]["key"]
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.awskms.key and use --target-name"
            )
        secret_obj = AWSKMSSecret(data, key, encode_base64=args.base64)
        tag = "?{{awskms:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("base64:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = "original"
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = "base64"
        ref_obj = Base64Ref(_data, encoding=encoding)
        tag = "?{{base64:{}}}".format(token_path)
        ref_controller[tag] = ref_obj

    elif token_name.startswith("vaultkv:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        vault_params = {}
        encoding = "original"
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            vault_params = kap_inv_params["secrets"]["vaultkv"]
        if args.vault_auth:
            vault_params["auth"] = args.vault_auth
        if vault_params.get("auth") is None:
            raise KapitanError(
                "No Authentication type parameter specified. Specify it"
                " in parameters.kapitan.secrets.vaultkv.auth and use --target-name or use --vault-auth"
            )

        secret_obj = VaultSecret(_data, vault_params)
        tag = "?{{vaultkv:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("plain:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = "original"
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = "base64"
        ref_obj = PlainRef(_data, encoding=encoding)
        tag = "?{{plain:{}}}".format(token_path)
        ref_controller[tag] = ref_obj

    else:
        fatal_error(
            "Invalid token: {name}. Try using gpg/gkms/awskms/vaultkv/base64/plain:{name}"
            .format(name=token_name))
Exemplo n.º 13
0
def secret_write(args, ref_controller):
    "Write secret to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error('--file is required with --write')
    if file_name == '-':
        data = ''
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            recipients = kap_inv_params['secrets']['gpg']['recipients']
        if not recipients:
            raise KapitanError(
                "No GPG recipients specified. Use --recipients or specify them in "
                + "parameters.kapitan.secrets.gpg.recipients and use --target")
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = '?{{gpg:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            key = kap_inv_params['secrets']['gkms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.gkms.key and use --target"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{gkms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("awskms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            key = kap_inv_params['secrets']['awskms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.awskms.key and use --target"
            )
        secret_obj = AWSKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{awskms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("ref:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = 'original'
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = 'base64'
        ref_obj = Ref(_data, encoding=encoding)
        tag = '?{{ref:{}}}'.format(token_path)
        ref_controller[tag] = ref_obj

    else:
        fatal_error(
            "Invalid token: {name}. Try using gpg/gkms/awskms/ref:{name}".
            format(name=token_name))
Exemplo n.º 14
0
def secret_update_validate(args, ref_controller):
    "Validate and/or update target secrets"
    # update gpg recipients/gkms key for all secrets in secrets_path
    # use --secrets-path to set scanning path
    inv = inventory_reclass(args.inventory_path)
    targets = set(inv['nodes'].keys())
    secrets_path = os.path.abspath(args.secrets_path)
    target_token_paths = search_target_token_paths(secrets_path, targets)
    ret_code = 0

    for target_name, token_paths in target_token_paths.items():
        kap_inv_params = inv['nodes'][target_name]['parameters']['kapitan']
        if 'secrets' not in kap_inv_params:
            raise KapitanError(
                "parameters.kapitan.secrets not defined in {}".format(
                    target_name))

        try:
            try:
                recipients = kap_inv_params['secrets']['gpg']['recipients']
            except KeyError:
                # TODO: Keeping gpg recipients backwards-compatible until we make a breaking release
                logger.warning(
                    "WARNING: parameters.kapitan.secrets.recipients is deprecated, "
                    + "please use parameters.kapitan.secrets.gpg.recipients")
                recipients = kap_inv_params['secrets']['recipients']
        except KeyError:
            recipients = None

        try:
            key = kap_inv_params['secrets']['gkms']['key']
        except KeyError:
            key = None

        for token_path in token_paths:
            if token_path.startswith("?{gpg:"):
                if not recipients:
                    logger.debug(
                        "secret_update_validate: target: %s has no inventory gpg recipients, skipping %s",
                        target_name, token_path)
                    continue
                secret_obj = ref_controller[token_path]
                target_fingerprints = set(lookup_fingerprints(recipients))
                secret_fingerprints = set(
                    lookup_fingerprints(secret_obj.recipients))
                if target_fingerprints != secret_fingerprints:
                    if args.validate_targets:
                        logger.info("%s recipient mismatch", token_path)
                        to_remove = secret_fingerprints.difference(
                            target_fingerprints)
                        to_add = target_fingerprints.difference(
                            secret_fingerprints)
                        if to_remove:
                            logger.info("%s needs removal", to_remove)
                        if to_add:
                            logger.info("%s needs addition", to_add)
                        ret_code = 1
                    else:
                        new_recipients = [
                            dict([
                                ("fingerprint", f),
                            ]) for f in target_fingerprints
                        ]
                        secret_obj.update_recipients(new_recipients)
                        ref_controller[token_path] = secret_obj

            elif token_path.startswith("?{gkms:"):
                if not key:
                    logger.debug(
                        "secret_update_validate: target: %s has no inventory gkms key, skipping %s",
                        target_name, token_path)
                    continue
                secret_obj = ref_controller[token_path]
                if key != secret_obj.key:
                    if args.validate_targets:
                        logger.info("%s key mismatch", token_path)
                        ret_code = 1
                    else:
                        secret_obj.update_key(key)
                        ref_controller[token_path] = secret_obj

            else:
                logger.info("Invalid secret %s, could not get type, skipping",
                            token_path)
                ret_code = 1

    sys.exit(ret_code)