Cmk.base.plugins.bakery.bakery_api.v1.password_store does not match Password() valuespec

Hi @moritz ,

cmk.base.plugins.bakery.bakery_api.v1 has a password_store module, re-imported from cmk.utils.

This has a method extract which does not seem to be compatible with the new valuespec Password as it only gets an ID of type PasswordId.

But Password now generates a Tuple like ('cmk_postprocessed', 'explicit_password', ('uuiddc646119-6a96-432c-8d9c-7af3e479a9dc', 'password')).

What should we use in a bakery plugin when the ruleset has a Password?

1 Like

Yeah, that’s ugly. But you can use the generated id (uuiddc64...) to extract it from the password store. It does not matter anymore, whether the user chose “explicit” or “stored” – it will be in the store anyway. You can (and should) use password_store.lookup_for_bakery for now.

For a v2 of the bakery API I intend to do something similar (and yet the contrary) of what we do in the server_side_calls API: The bakery plugins should get a dedicated object, that does contain the actual secret. For bakery plugins we will always need access to it – no need to make the plugin developers look it up themselves. I intend to replace the ugly tuple you described by something like this:

class Secret(NamedTuple):
    # it seems that NamedTuple is the most reasonable way to create a pydantic compatible class
    # without adding a dependency on pydantic
    """Represents a configured secret to the bakery plugin

    This class aims to reduce the chances of accidentally exposing the secret in crash reports and log messages.
    However, a bakery plug-in produces configuration files that are deployed to the target system.
    Therefor the plugin needs access to the actual secret.
    As a result, we cannot guarantee that bakery plugins will not expose the secret in any way:

    Example:

        # this is passed by the backend to the bakery plugin
        >>> s = Secret("s3cr3t")

        >>> print(f"This is the secret as string: {s}")
        This is the secret as string: 4e738ca5563c06cfd0018299933d58db1dd8bf97f6973dc99bf6cdc64b5550bd

        >>> print(f"This is the secrets `repr`: {s!r}")
        This is the secrets `repr`: Secret('4e738ca5563c06cfd0018299933d58db1dd8bf97f6973dc99bf6cdc64b5550bd')

        >>> print(f"But we can see the actual value: {s.revealed!r}")
        But we can see the actual value: 's3cr3t'

    """

    revealed: str

    def _hash(self) -> str:
        return hashlib.sha256(self.revealed.encode("utf-8")).hexdigest()

    def __str__(self) -> str:
        return self._hash()

    def __repr__(self) -> str:
        """Mask the actual value of the secret

        This deliberately breaks the semantics of the `repr` function.
        """
        # The backend uses the `repr` function (pprint) to create a hash of the secret,
        # so make sure to not return something constant here.
        return f"Secret('{self._hash()}')"

What do you think?

1 Like

There is no method of this name in 2.3.0p34.

I have created two helper methods:

def _lookup_for_bakery(pw_id: str) -> str:
    return password_store.lookup(password_store.password_store_path(), pw_id)

def _get_password(v):
    if isinstance(v, tuple):
        if v[0] == "cmk_postprocessed":
            if v[1] == "explicit_password":
                return v[2][1]
            if v[1] == "stored_password":
                return _lookup_for_bakery(v[2][0])
    return None

IMHO this forcing of the new API v2 on extension developers was not a good idea when obviously not much of the internal code has been migrated.

:frowning: Oh. Too bad I didn’t backport this into 2.3. Would it help you if I did, or is too late anyway?

The question here is: Do you expect the bakery to use the pending configuration, or the activated one. We decided to use the activated one in lookup_for_bakery in 2.4 – so if you want to be consitent with that you should use password_store.core_password_store_path(password_store.LATEST_CONFIG) (not tested).

Why is there a difference?

For example: If a user creates a new rule (or, maybe even more surprising) changes an entry in the password store, but they did not yet activate the changes – would expect the change to be reflected in a newly baked agent package?
I don’t think this question has an obvious answer, but bakery plugins should behave consistently here.

In the past any changes to agent rules immediately changed the bakery output. No need for activation.

Yes. On the other hand, the general idea is that fiddling with the configuration will not break the running monitoring as long as changes are not activated.
But I understand that you are in favor of using the pending config?

Yes, as this has been the behaviour since the start of Checkmk.

1 Like

Hi Robert and Moritz,
I was also trying a while to use the “old” password_store.extract on the new ruleset API v1 Password object/class Password, what did not work.
Actually I also wanted to use a newly written Extension for 2.3 and 2.4, what from a dev perspective only makes sense to use the new ruleset API.

So how do I get this working in an extension with the new APIs and bakelet code I want to use on 2.3 and 2.4 ? We are slowly migrating Extensions and want to start using them in 2.3 also before we make the big switch to 2.4. How is that possible with this inconsitent password handling ?

PS: Is there any way to use –debug flag with cmk -A ? I can use it on the command line, but tried to use
try:
from cmk.ccc import debug
except ImportError:
from cmk.utils import debug
and then later

if debug.enabled():
what also seems not to work here as in the check plugins…

Update: I found out, that for some reason the debugging now works as decribed in my thread above, at least for 2.4…and I found examples in Roberts Github Repo, that made the password extraction work in 2.4 as well…

I also tested Roberts code in 2.3.0p42 and it also works, what is fine for me so far…thanks for investigating this…