Allow usage of API with dropped privileges (not only master key)

Created on 16 February 2023, over 1 year ago
Updated 7 February 2024, 9 months ago

Problem/Motivation

https://docs.meilisearch.com/learn/security/master_api_keys.html#communi... says

For most of your day-to-day operations, you should use API keys when communicating with a protected instance.

and

We strongly recommend you only use the master key when managing API keys.

Drupal should therefore not use the master key as the only one.

Proposed resolution

Creating a dedicated API key with the following request seems to be enough for the use cases I have found until now:

curl   -X POST 'http://localhost:7700/keys'   -H 'Content-Type: application/json'   -H 'Authorization: Bearer MASTER_KEY'   --data-binary '{
    "description": "mykey",
    "actions": ["search", "version", "indexes.get", "documents.add", "documents.get", "documents.delete", "settings.update", "tasks.get", "tasks.cancel", "tasks.delete" ],
    "indexes": ["my_first_allowed_index", "my_second_allowed_index"],
    "expiresAt": null
  }' | json_pp

(json_pp prettifies the returned JSON, this is optional)

Creating (and interestingly, deleting, though not explicitly granted; maybe only for those created by the same user/key?) indexes also works if I add "indexes.create", but, as expected, using the newly created index does not work, as this would need permissions to adapt key permissions themselves (which is what we want to avoid). Therefore, it does not really make sense to add the "indexes.create" permission. (The above also proves that indexes are indeed inaccessible without the proper API key.)

What currently works is
* entering the master API key on the search server's config page
* creating all indexes as desired
* manually creating a key with dropped privileges outside of drupal, explicitly granting access to the indexes needed only
* switching to use this new key as "master API key" on the search server's config page

This is a better setup, as the configured API key will never be able to mess with indexes (of other sites of a multisite install, for example) not belonging to this key. Also, if the key gets compromised, the risk is minimized as far as possible.

I could imagine adding a drush command would help (and possibly a webGUI option as well), for a one-off creation of indexes that automatically also creates a key with the necessary permissions (with an API request outlined above). The master key would need to be provided as an input (field) that does not get saved anywhere (also not in the logs).

Modifying an existing key is basically possible.
A call to /keys/<uid_or_key> can be used, as per https://docs.meilisearch.com/reference/api/keys.html#update-a-key
However, it seems permissions ("actions") can not be adapted (https://docs.meilisearch.com/reference/errors/error_codes.html#immutable...), and neither can indexes (https://docs.meilisearch.com/reference/errors/error_codes.html#immutable...).
Therefore, a change to the allowed indexes (necessary when creating a new index) means deleting the key and recreating it is necessary.

In the meantime, a short howto or link to this issue could be added on the module's front page here on drupal.org so others can benefit.

โœจ Feature request
Status

Needs work

Version

2.0

Component

Code

Created by

๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @tgoeg
  • ๐Ÿ‡ธ๐Ÿ‡ฎSlovenia bcizej

    I think key management or access levels/permissions is of out of scope of this module but I do agree that master key should not be needed. What we can do here is change the title and add a description of what permissions the key must have to perform operations within Drupal to the configuration form field.

    Perhaps we can add an extra optional field for a key that supports search operations only to be used with searches although I don't see any benefit from the security standpoint (both keys are stored in Drupal) apart from maybe when used in decoupled architecture so searches can be done directly from the frontend.

    As for the example you provided if I understood correctly, the issue is that even if your key has "indexes.create" permission, you cannot access the newly created index because of "indexes": ["my_first_allowed_index", "my_second_allowed_index"] setting?
    Looking at the documentation it does state that "indexes" field does support wildcards and can be granted access to all indexes with ["*"]. If that is too loose it can be narrowed down to indexes starting with specific prefix like ["my_*"].
    That would mean that the user in Drupal with such a key could create indexes starting with "my_" and have access to the newly created index.
    Keep in mind I did not test this and the docs are for the latest stable version which we are not using (yet).

  • ๐Ÿ‡ธ๐Ÿ‡ฎSlovenia bcizej
  • Now that we are working with the latest stable version of Meilisearch. I wanted to test the wildcard option in the index parameter.

    I created a new API key with this body:

    {
    	"name": "Test key",
    	"description": "Search, view documents and version",
    	"actions": [
    		"search",
    		"documents.get",
    		"documents.add",
    		"documents.delete",
    		"indexes.get",
    		"indexes.create",
    		"indexes.update",
    		"indexes.delete",
    		"settings.update",
    		"settings.get",
    		"tasks.get",
    		"stats.get",
    		"version"
    	],
    	"indexes": ["search_api_*"],
    	"expiresAt": null
    }
    

    This API key can do almost everything master_key can, except modify API keys and it can only do its operations on indexes that are prefixed with search_api_, this also means creating a new index (but the prefix limit is still there).

    Implementation idea:

    Knowing this I was thinking of how to implement the functionality to be as simple as possible. First I think this could be a submodule, so if someone wants a basic or doesn't have a multi-tenant setup, doesn't have to use it.

    Now this submodule would modify the first-time configuration of a new server. We'd still keep the master key field, but it would be a one-time usage key, just used for the generation of the API key. Also, there would have to be an index prefix field. Having these two fields we could create a new API key with the actions I put in the upper JSON body and indexes set to ["prefix_*"]. At this point we would store the API key and the master_key would never be used again.

    Then modify the index creation and any interaction with the index, to always prefix the index id. Now the simplest way to implement this would be to override the machine name of the index on index creation, this way we don't have to change any other functionality of the base module.

    This would introduce the requested security and functionality, but I see some complications that would show up.
    The first one is if there is already an existing active server with indexes. For this, I think we should just set the prefix to an empty string and leave the master_key as was set.
    The second is removing of submodule. So if the API key stays there searching, modifying, and deleting existing indexes should not be a problem, but if someone tries to add a new index to the server it will only work if the admin adds the prefix that works with the API key when naming the index.

    Now after that long text. I'd like to hear opinions about this implementation. Especially from @tgoeg since from what I saw in other issues you actively use Meilisearch and this module.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    Seems I did not push save the last time I wrote an answer to this, sorry.

    I didn't know about the wildcard option. But I like the idea very much. Do you know why? My indexes already have a prefix, the domain they are being used on :-)

    So yes, this feels pretty natural to me and I guess it will to anyone having a multisite setup.

    I am unsure about the consequences when deleting an index. If a wildcard API key exists, it will most likely continue to exist. I think I remember that API keys vanish when their index gets deleted (but I may be wrong). Creating an index should also be fine, as - as long as the prefix is correct - there is no additional key needed (which would otherwise be the case; a per-index key needs to exist to be able to use the index, and therefor creating a new index would not make too much sense).

    I currently get an error "Index already exists" if drush cim a config with an existing index. Would this be "fixed" then? I do not think an index should then be deleted, even if that would be possible now.
    But that's a different issue actually, which I think needs a little modification as well.

    There might be applications where protecting an index from getting deleted might make sense. My current (manually-created-key-)setup does just that. Drupal may in fact only really add and remove documents, and search through them, which feels adequate for a production instance. No way to really destroy anything.
    I think people who want just this can however still lock down keys manually. This module should probably not break if people do it, however.

    This way, I think this could be added to the core functionality right away. It does not really seem to have the potential to bring disadvantages to anyone, and it would prevent people from using a master-key based setup. I think that's something that should never be possible. It feels like saving your root password into some configuration in plain text.

    If this shall be a separate submodule then there should be a big fat warning that running without it is not at all recommended.

    I might be a little biased but I think prepopulating the (editable) prefix with the transliterated domain of the page, e.g. my-website.co.at will become my_website_co_at_ as a prefix) could help beginners a lot to get a clean setup.

    Regarding your questions:
    > The first one is if there is already an existing active server with indexes. For this, I think we should just set the prefix to an empty string and leave the master_key as was set.
    Yes and no. It will keep legacy setups running, yes. But I'd like to see an upgrade path for this. It would be great if one could provide a prefix (that's already in use) and a key, basically just like the new algorithm layed out above would do, just that the key would not be created, but an existing one could be entered.
    If the algorithm would just fill in the prefix and the (generated) key upon clicking some "generate" button (that also needs to have the master API key supplied), then those fields could be editable as well (with a warning stating that the key and prefix either need to exist already (for upgrades) or one should better use the generator, if a new key/prefix (and probably index) is preferred.

    > The second is removing of submodule. So if the API key stays there searching, modifying, and deleting existing indexes should not be a problem, but if someone tries to add a new index to the server it will only work if the admin adds the prefix that works with the API key when naming the index.
    Yes. And a note for this would need to be added. Which makes part of this functionality appear in the main module again. I think this should go into the main module (did I already mention that?). But, as I stated above, using a submodule is a possibility as well. The big fat warningโ„ข from above would need an additional info that exactly those problems will arise if anyone decides to remove the submodule. I can't think of any meaningful setup that would need this, though.

    Here are two mockups and the HTML that generates them.
    I am unsure whether this should probably better be put into a separate tab. The double "generate key" buttons are no good solution yet. Probably just add a link to the second tab and do the key generation there.
    Alternatively, it could be fit right into the basic configuration page already as well, should not be too long.


    As you can see my screenshots are partially German. I am from Austria, so I can provide translations as well (later on).

  • So from my understanding, you don't want Drupal to be capable of creating a new API key. You think people should do this by hand outside Drupal? They should make sure that the correct privileges are setup and the correct index prefix is setup.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    No!
    And sorry, I seem to not have been clear.

    I DO think it's a good idea for Drupal to setup keys. Something along the workflow depicted in the mockups.
    It's just that the usage of prefixes (which is a good design decision IMHO) will not always be backwards compatible with existing setups. Only in the case of existing indexes that do NOT conform to a prefixed namespace will an admin have to manually manage keys outside of Drupal.
    And that will be a very small percentage of users, if they exist at all.
    Even if people have existing indexes, I - for one - would advise to drop them, generate a key like in the mockups and create the indexes anew, now conforming to the prefixed setup.

    I think what needs additional attention is the creation of new indexes.
    When a prefix (and accompanying key) is setup, only indexes starting with the prefix can be created. So misconfiguration can technically be ruled out as wrong indexes will not be able to be created anyway.
    However, it might be helpful if it were possible to restrict new index creation to names starting with the prefix.
    If that's too complicated, the error message when trying to create an index with a different prefix should state state the index does not start with the prefix. Not just that index creation failed because of missing permissions.

  • Ah ok, now I understand.

    When it comes to creating new indexes, we have full control over what the index is named. I was thinking that prefixing should just happen in the code, an admin when creating a new index would just type in the name without the prefix. Of course, we'd add a quick check if the admin already included the prefix by hand, just so there is no double prefixing.

  • Ok, I've been wondering if this is a good way to do the configs.

    This is how the server config looks when you first open it and set the prefix.

    When you select "Generate API Key":

    Once you save the server configuration:

    Of course, I'll add more explanation to the field description. Also, the Generate API key checkbox is grayed out if there is a set access key.

  • Status changed to Needs work about 1 year ago
  • @admirlju opened merge request.
  • Still needs a lot of work. Since the currently published code creates a fully permissive API key "actions": ["*"]. I think it would be good to add an extra checkbox when confirming deletion, to also remove the generated API key.

    The prefixing also works now. So if someone sets the prefix, all indexes created from this point will automatically prepend it.

    I'd just like to know if the UI is logical the way I made it. If there are any other things that should be added or changed.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    Yeah! That looks good to me as well! Probably re-use some of my description texts to make it clearer.

    Would I be able to deliberately delete the key and re-activate "Generate API key"?
    There might be situations where I want to have a new key when one is configured already. Maybe when a new/empty Meilisearch server gets used and I want to start anew.
    Or probably do not deactivate the checkbox and only show a red warning when it is selected and a key is already configured?

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    Comments overlapped.
    Yes, a confirmation and deletion of the generated key totally makes sense. I do already have a lot of unused keys from development tries. This would definitely make setups cleaner and mean less manual admin work on the Meilisearch backend.

  • At the moment if you delete the key, it will enable the Generate API key checkbox and you can generate a new key. That said I need to add a way for the key to actually be removed from Meilisearch.

  • I think for the most part I implemented how I think it should work. But still need to add error handling (at the moment a quiet failure happens) and to add some automated tests.

    I made it so prefix can't be changed once set at index creation. Sadly how key management works, every time the prefix gets changed we'd have to generate a new key.
    Now when editing an already created server you can click generate new API key and this will cause the old key to be deleted and the new one set. When deleting a server you now have a master key field as a confirmation to delete the key, might change this to a checkbox, that shows the master key field. We always need to prompt the admin to type in the master key for this task.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    All of this sounds reasonable to me!
    I think a warning should be added that keys will get deleted (haven't looked at the actual code yet, maybe it is like this already). Most of the time this will be desired, but there may be cases where an index+key may be used from multiple Drupal instances or even different frontends.
    Taking care of every hypothetical setup should not be the aim here, however. I'd expect admins of complex setups to be able to handle them manually, for themselves. A warning/note however should protect from unexpected accidents.

    > Sadly how key management works, every time the prefix gets changed we'd have to generate a new key.
    Yes, that's something that irritated me as well already. Should we probably file a feature request for an UPDATE feature of the /keys endpoint to allow both changing the index and/or the key?

  • Yes, I made sure to use a warning banner, so it pops out and makes it harder for admins to miss the message. The banner explains that if an admin wishes to keep the key, they can create one manually and paste it, this will not cause key deletion.

    With the default D9.5 admin theme, it looks like this:

    We could fill a feature request, they have a specific page for that. But I think it will not be their priority, so it might take some time before it's added.

  • Ok, so I've noticed one more thing, that might cause pain to admins. Looking at the Meilisearch documentation, API keys are calculated using the master key and key uid. This means that if an admin changes their master key, Meilisearch will generate a new API key hash so the key that is saved on Drupal will not work anymore.

    I don't know if this should be a warning somewhere.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    OK, RTFM'ing sometimes really helps :-)

    That key derivation first irritated me, but after reading the reasoning behind it, it totally makes sense.
    This also means it's of no use to file a feature request, as API keys simply cannot be changed.

    It also rules out situations where people might probably change API keys on the Meilisearch server but forget to keep it in sync with Drupal, which could be considered an advantage.

    All of this might be too theoretical anyway. I think the way you did it right now makes for 99% of the setups. At that's what this issue was all about at first. To remove the master key from the config and have a convenient way of managing the keys from Drupal itself so one can have a secure setup.
    This seems fulfilled to me.

    Let's wait for other use cases if there are any.

    I could think of one, btw :-)

    This is an actual project that might see the light of day next year probably. I'd want to have one central Drupal instance to feed an index and have multiple web sites to be able to search through subsets of this index (based on given filter criteria).
    It would mean one default setup with the routines you laid out to initially create the index on the central instance. Nothing fancy yet.
    On the other instances, I would manually enter prefixes and keys, as I would very likely want them to not be able to delete the index, add or delete documents, etc. One could probably prepare for such a scenario by adding an additional checkbox "read-only access", which would create the API key with a reduced set of permissions.
    I am still not sure how to get the actual content from the central Drupal site, then, if search_api can handle non-local content for indexed data at all and if this is a good idea. Or if this module needs to be the one that indexed the data itself to be able to use it again (maybe not anymore since the fix in ๐Ÿ“Œ Remove entity id mapping config Fixed ?)
    But this would probably be one application for another kind of key generation.

  • Status changed to Needs review about 1 year ago
  • I'll set the issue to needs review. It would be nice to test in different scenarios.

  • Status changed to Needs work 9 months ago
  • ๐Ÿ‡ธ๐Ÿ‡ฎSlovenia bcizej

    I'm not really sure what to make of this feature as it is now. I think this goes way above what the base module should provide as I explained in #2. If this is moved to a submodule as perhaps a separate backend provider (so user can choose standard connector or advanced connector with keys management, auto generate keys for specific indexes etc.) then I'm ok with it. But in the current form of this PR I do not want to merge it. For this reason I'm setting the status to back to "Needs work".

    What I do agree here is that current connector should not use master key, but API and Search key fields should be configurable and used instead. But this is a breaking change as current installations would need to update configuration manually and a separate issue should be opened on how to best deal with this.

  • ๐Ÿ‡ฆ๐Ÿ‡นAustria tgoeg

    I'm OK with a separate module, though I feel this should be the standard (secure) way of setting things up (and installed by default, then?). Maybe adding a note on the module's front page that using the submodule providing key management is strongly recommended for those not seasoned with manual key/index setups can help.

    You mentioned

    API and Search key

    .
    Haven't thought of search keys at all until now. The concept sounds good - a search client (Drupal frontend) only gets the least privilege, i.e. only permissions for searching. However, as the backend lives on the same installation, it is kind of limited here. When ingesting/indexing content is done on one machine and searching only on another, that absolutely makes sense. But yeah, why not, it doesn't hurt. And in case there's some vulnerability in the search code only, the index's contents are not at risk at once.

    It should not be too hard to solve the backward compatibility I think: Let's take this over to โœจ Change master key to API and search key for improved security Active

    If this issue at hand is available as a submodule, I think I can offer testing. It's just that I am currently stuck on 1.x, as the upgrade to 2.x breaks my facetted searches, even when I add the new facetting submodule. No errors whatsoever, I just don't get any results, but that's a different story. Just so you know this might be delayed quite a bit.

  • ๐Ÿ‡ธ๐Ÿ‡ฎSlovenia bcizej

    If this issue at hand is available as a submodule, I think I can offer testing. It's just that I am currently stuck on 1.x, as the upgrade to 2.x breaks my facetted searches, even when I add the new facetting submodule. No errors whatsoever, I just don't get any results, but that's a different story.

    Feel free to open an issue with as much info as possible, there might be some hidden issues we are not aware of.

Production build 0.71.5 2024