Skip to content

[Privmon] Synchronize Privileged User index based on configured Entity Sources#221610

Merged
CAWilson94 merged 35 commits intoelastic:mainfrom
CAWilson94:ea-12289-priv-users-sync-task
Jun 24, 2025
Merged

[Privmon] Synchronize Privileged User index based on configured Entity Sources#221610
CAWilson94 merged 35 commits intoelastic:mainfrom
CAWilson94:ea-12289-priv-users-sync-task

Conversation

@CAWilson94
Copy link
Contributor

@CAWilson94 CAWilson94 commented May 27, 2025

Summary

This PR introduces support for populating the Privileged Users index from custom data sources (Elasticsearch indices) by querying Privileged User Monitoring Saved Objects from a Kibana task.

Related Issue
Figma Breakdown Working from the "Synchronize Privileged User index based on configured Entity Sources" section, to the right.

✅ Implemented

• Registered the temporary type and mappings
• Created a Descriptor Client that saves it via soClient.create()
• Called Descriptor Client create method from PrivMon initialisation.

  • Testing above default Saved Object creation within PrivMon Initialisation
  • Update the entity_analytics:monitoring:privileges:engine task to:
    • Read all Saved objects of index type
    • query user.name values from given indexPattern
    • Apply any filters or matchers defined in the Saved Object
    • Insert matched user.names into .entity_analytics.monitoring.users- with source type index.
    • Cleanup logic:
    • Target pattern does not exist, log warning and continue task
    • Failures will be caught and do not interrupt task loop
    • Remove previously stored user.names with source type index, that are no longer present.
    • Component Testing

🚧 Wish List

  • Pull out sync functions from privilege monitoring data client into their own sync service class / similar
    • Currently in the data client, they should not be on this layer.
  • Update GET and DELETE methods with dynamic id's for monitoring data client as per this ticket

Follow-up Issues:

How to Test

  • Pull branch into local machine
  • Security experimental flag enable: 'privilegeMonitoringEnabled'
  • Start up ElasticSearch and Kibana

1. Optional - create the default index, this should just skip if you don't make it.

POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}

2. Create test index/ indicies

POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

3. Register Monitoring Entity Source Saved Objects

POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

- OPTIONAL: You can check what is in the monitoring entity_source SO:

GET kbn:/api/entity_analytics/monitoring/entity_source/list

4. Initialise monitoring engine:

POST kbn:/api/entity_analytics/monitoring/engine/init {}

5. Verify Users in Monitoring Index

  • Check the list of synced user, should include:
    • The created users
    • The default user (if you created it)
GET kbn:/api/entity_analytics/monitoring/users/list

e.g. output:

[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

Testing: Removing Stale Users

The engine should soft delete users from the internal index if they no longer appear in the synced sources - e.g. label as monitoring.privileged_users: "not_monitored"
Example:

  • Delete users in index:
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name.keyword": ["Chewbacca", "Han Solo"]
   }
 }
}

  • re-run engine init
POST kbn:/api/entity_analytics/monitoring/engine/init
{}

-Fetch the updated user list:

GET kbn:/api/entity_analytics/monitoring/users/list

You should now only see both Chewbacca and Han Solo are no longer privileged:

// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]

… and updated registering SO imports; creating sourceindex from privmon init
@CAWilson94 CAWilson94 changed the title Index Source Saved Object setup - added index and privmon SO to index… [Privmon] Synchronize Privileged User index based on configured Entity Sources May 27, 2025
@CAWilson94 CAWilson94 self-assigned this May 29, 2025
@CAWilson94 CAWilson94 marked this pull request as ready for review June 18, 2025 13:25
@CAWilson94 CAWilson94 requested review from a team as code owners June 18, 2025 13:25
@CAWilson94 CAWilson94 requested a review from hop-dev June 18, 2025 13:25
…ring sync sources. WIP: To change to using helper bulk wrapper; bulk ops for soft delete too; code splitting
machadoum added a commit that referenced this pull request Jun 24, 2025
Depends on #221610

This PR adds the import index workflow to privileged user monitoring and
API changes required to support it.

### API Enhancements

* **New API for privilege monitoring index creation**: Added a new API
endpoint (`PUT /api/entity_analytics/monitoring/privileges/indices`) to
create indices for privilege monitoring with support for `standard` and
`lookup` modes. This includes the implementation of request and response
schemas (`create_indidex.gen.ts`, `create_indidex.schema.yaml`).
[[1]](diffhunk://#diff-68329bb90dea945f343e1637990d5d05bc159e0aa2511ef1e45d37ed1a6cda51R1-R41)
[[2]](diffhunk://#diff-e979499654a27b3c1930d63c5b1002113c1c3f53f84ce27a4d75a5c492717a96R1-R42)
* **Updated privilege monitoring health response**: Modified the health
response schema to include a `status` field and an optional `error`
object for detailed error handling
(`privilege_monitoring/health.gen.ts`,
`privilege_monitoring/health.schema.yaml`).
[[1]](diffhunk://#diff-00f39a3e65a336eaddf7d3203d1370d910f5ecd2062b6cc21d9c06922c12884eR19-R28)
[[2]](diffhunk://#diff-83afa72b7a1fc48f3cc063e9fb855190d3525228bc0488fb8b871e112b90e961L22-R33)

### Frontend Integration

* **Introduce the create index modal that opens when the create index
button is clicked.
* **Onboarding modal improvements**: Updated the `AddDataSourcePanel`
component to handle index creation more robustly by passing callbacks to
the modal (`add_data_source.tsx`).
* **Error handling in UI**: Enhanced the `PrivilegedUserMonitoring`
component to display error callouts when privilege monitoring data fails
to load (`privileged_user_monitoring/index.tsx`).
[[1]](diffhunk://#diff-273ad32c97dcf15c6c6054fd7c5516d587132674578d25986b235cd174c75789R22-R26)
[[2]](diffhunk://#diff-273ad32c97dcf15c6c6054fd7c5516d587132674578d25986b235cd174c75789R38-R51)

### How to test it?
* Go to the priv mon page with an empty cluster
* Click on the data source by the index button
* Search for available indices, it should return indices with
`user.name.keyword` fields
* Click 'create index' and create a new index 
* Choose the created index and click 'Add privileged users'
* You should be redirected to the dashboard (The API is currently not
working)




### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
}
}

public async getAllUsernamesFromIndex({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAllUsernamesFromIndex does much more than its name suggests. A ' get `function should only retrieve data. This one adds data to an index.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest splitting it into 2 functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment onto this for now - will split into two after merge

Copy link
Contributor

@tiansivive tiansivive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢
Thanks for all the work! This was quite the epic

Comment on lines +18 to +19
export const defaultMonitoringUsersIndex = 'entity_analytics.privileged_monitoring'; // TODO: update in accordance to spec. Why does this not work when you do this. Investigate.
// should be entity-analytics.privileged-users
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???

Have you gone back and checked? looks like a really puzzling issue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty for nudge, lost it somewhere. BUT classically I don't know why it didn't work and I don't know why it does work now - probably some typo somewhere down the line. All good now 🎉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, previous index makes more sense. Struggling to find reference for this in the spec, will bring up with @hop-dev when back, its easy to change after which is ok.

Copy link
Member

@machadoum machadoum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks ok, we can do the improvement in a follow-up PR due to the time constraintsThe code seems acceptable; improvements can be made in a follow-up PR due to time constraints.

I haven't tested it locally.

Thank you for handling this complex feature! 👏 👏 👏

@CAWilson94 CAWilson94 enabled auto-merge (squash) June 24, 2025 13:30
@CAWilson94
Copy link
Contributor Author

The code looks ok, we can do the improvement in a follow-up PR due to the time constraintsThe code seems acceptable; improvements can be made in a follow-up PR due to time constraints.

I haven't tested it locally.

Thank you for handling this complex feature! 👏 👏 👏

Added a ticket here: https://github.com/elastic/security-team/issues/12887 thanks @machadoum and @tiansivive for having a look and giving feedback 🚀

@CAWilson94 CAWilson94 merged commit 26bb220 into elastic:main Jun 24, 2025
10 checks passed
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Unknown metric groups

ESLint disabled line counts

id before after diff
securitySolution 625 627 +2

Total ESLint disabled count

id before after diff
securitySolution 723 725 +2

History

cc @CAWilson94

akowalska622 pushed a commit to akowalska622/kibana that referenced this pull request Jun 25, 2025
Depends on elastic#221610

This PR adds the import index workflow to privileged user monitoring and
API changes required to support it.

### API Enhancements

* **New API for privilege monitoring index creation**: Added a new API
endpoint (`PUT /api/entity_analytics/monitoring/privileges/indices`) to
create indices for privilege monitoring with support for `standard` and
`lookup` modes. This includes the implementation of request and response
schemas (`create_indidex.gen.ts`, `create_indidex.schema.yaml`).
[[1]](diffhunk://#diff-68329bb90dea945f343e1637990d5d05bc159e0aa2511ef1e45d37ed1a6cda51R1-R41)
[[2]](diffhunk://#diff-e979499654a27b3c1930d63c5b1002113c1c3f53f84ce27a4d75a5c492717a96R1-R42)
* **Updated privilege monitoring health response**: Modified the health
response schema to include a `status` field and an optional `error`
object for detailed error handling
(`privilege_monitoring/health.gen.ts`,
`privilege_monitoring/health.schema.yaml`).
[[1]](diffhunk://#diff-00f39a3e65a336eaddf7d3203d1370d910f5ecd2062b6cc21d9c06922c12884eR19-R28)
[[2]](diffhunk://#diff-83afa72b7a1fc48f3cc063e9fb855190d3525228bc0488fb8b871e112b90e961L22-R33)

### Frontend Integration

* **Introduce the create index modal that opens when the create index
button is clicked.
* **Onboarding modal improvements**: Updated the `AddDataSourcePanel`
component to handle index creation more robustly by passing callbacks to
the modal (`add_data_source.tsx`).
* **Error handling in UI**: Enhanced the `PrivilegedUserMonitoring`
component to display error callouts when privilege monitoring data fails
to load (`privileged_user_monitoring/index.tsx`).
[[1]](diffhunk://#diff-273ad32c97dcf15c6c6054fd7c5516d587132674578d25986b235cd174c75789R22-R26)
[[2]](diffhunk://#diff-273ad32c97dcf15c6c6054fd7c5516d587132674578d25986b235cd174c75789R38-R51)

### How to test it?
* Go to the priv mon page with an empty cluster
* Click on the data source by the index button
* Search for available indices, it should return indices with
`user.name.keyword` fields
* Click 'create index' and create a new index 
* Choose the created index and click 'Add privileged users'
* You should be redirected to the dashboard (The API is currently not
working)




### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
akowalska622 pushed a commit to akowalska622/kibana that referenced this pull request Jun 25, 2025
…y Sources (elastic#221610)

# Summary
This PR introduces support for populating the Privileged Users index
from custom data sources (Elasticsearch indices) by querying Privileged
User Monitoring Saved Objects from a Kibana task.

[Related Issue](elastic/security-team#12289)
[Figma
Breakdown](https://www.figma.com/board/yBr1pBDGu4JqNxb5ZrULtk/MonEntitySourceSyncTask?node-id=0-1&p=f&t=q52ABMD5MLX0uGj1-0)
Working from the _"Synchronize Privileged User index based on configured
Entity Sources"_ section, to the right.
### ✅ Implemented
• Registered the temporary type and mappings
• Created a Descriptor Client that saves it via soClient.create()
• Called Descriptor Client create method from PrivMon initialisation. 
* Testing above default Saved Object creation within PrivMon
Initialisation
* Update the entity_analytics:monitoring:privileges:engine task to: 
     *  Read all Saved objects of index type
     * query user.name values from given indexPattern
     * Apply any filters or matchers defined in the Saved Object
* Insert matched user.names into
.entity_analytics.monitoring.users-<space> with source type index.
     * Cleanup logic: 
     * Target pattern does not exist, log warning and continue task
     * Failures will be caught and do not interrupt task loop
* Remove previously stored user.names with source type index, that are
no longer present.
     * Component Testing

### 🚧 Wish List 
* Pull out sync functions from privilege monitoring data client into
their own sync service class / similar
* * Currently in the data client, they should not be on this layer. 
* Update GET and DELETE methods with dynamic id's for monitoring data
client as per [this
ticket](elastic/security-team#12851)


# How to Test
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name.keyword": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
CAWilson94 added a commit that referenced this pull request Jul 4, 2025
)

## Summary 
This PR adds in API key management and runs plain index sync on task
execution. Thanks to @jaredburgettelastic for the below changes 🚀 :
### Key Changes 
Tasks now call plainIndexSync to sync index users at specified intervals
— this is the main functionality change. The following updates support
it:
- Added support for scoped API key usage via getClientfromApiKey in the
getApiKeyManager
- Using createInternalRepository for API key generation, to allow for
internal kibana user to create saved object client.
- API keys now store a snapshot of the user’s privileges by passing {}
to role_descriptors.
- Updated the find method in MonitoringEntitySourceDescriptorClient to
use a namespace-scoped saved objects client.

## ⚠️ Important Note 
You must fully restart Elasticsearch and Kibana after pulling these
changes to regenerate the API key with the updated privileges.

While taking a privilege snapshot is known to be problematic (e.g.,
privileges can get out of sync if updated later), it’s a known issue and
consistent with the current behavior in the Entity Store. @tiansivive
raised 🚀

# How to Test:
Steps Taken from
[#221610](#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

## Recurring Sync 
Current interval is 10m, change this interval to 30s / shorter interval
for testing -
`
x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`
under 'INTERVAL'.

Should be able to add some more indicies/ delete some users from current
indicies and see results from the same users list above, on index sync
as well as this log "[Privilege Monitoring] Running privilege monitoring
task" running every x chosen INTERVAL occurrence.

---------

Co-authored-by: jaredburgettelastic <jared.burgett@elastic.co>
Co-authored-by: Tiago Vila Verde <tiago.vilaverde@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jeramy Soucy <jeramy.soucy@elastic.co>
adcoelho pushed a commit to adcoelho/kibana that referenced this pull request Jul 7, 2025
…tic#225551)

## Summary 
This PR adds in API key management and runs plain index sync on task
execution. Thanks to @jaredburgettelastic for the below changes 🚀 :
### Key Changes 
Tasks now call plainIndexSync to sync index users at specified intervals
— this is the main functionality change. The following updates support
it:
- Added support for scoped API key usage via getClientfromApiKey in the
getApiKeyManager
- Using createInternalRepository for API key generation, to allow for
internal kibana user to create saved object client.
- API keys now store a snapshot of the user’s privileges by passing {}
to role_descriptors.
- Updated the find method in MonitoringEntitySourceDescriptorClient to
use a namespace-scoped saved objects client.

## ⚠️ Important Note 
You must fully restart Elasticsearch and Kibana after pulling these
changes to regenerate the API key with the updated privileges.

While taking a privilege snapshot is known to be problematic (e.g.,
privileges can get out of sync if updated later), it’s a known issue and
consistent with the current behavior in the Entity Store. @tiansivive
raised 🚀

# How to Test:
Steps Taken from
[elastic#221610](elastic#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

## Recurring Sync 
Current interval is 10m, change this interval to 30s / shorter interval
for testing -
`
x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`
under 'INTERVAL'.

Should be able to add some more indicies/ delete some users from current
indicies and see results from the same users list above, on index sync
as well as this log "[Privilege Monitoring] Running privilege monitoring
task" running every x chosen INTERVAL occurrence.

---------

Co-authored-by: jaredburgettelastic <jared.burgett@elastic.co>
Co-authored-by: Tiago Vila Verde <tiago.vilaverde@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jeramy Soucy <jeramy.soucy@elastic.co>
kibanamachine added a commit to kibanamachine/kibana that referenced this pull request Jul 7, 2025
…tic#225551)

## Summary
This PR adds in API key management and runs plain index sync on task
execution. Thanks to @jaredburgettelastic for the below changes 🚀 :
### Key Changes
Tasks now call plainIndexSync to sync index users at specified intervals
— this is the main functionality change. The following updates support
it:
- Added support for scoped API key usage via getClientfromApiKey in the
getApiKeyManager
- Using createInternalRepository for API key generation, to allow for
internal kibana user to create saved object client.
- API keys now store a snapshot of the user’s privileges by passing {}
to role_descriptors.
- Updated the find method in MonitoringEntitySourceDescriptorClient to
use a namespace-scoped saved objects client.

## ⚠️ Important Note
You must fully restart Elasticsearch and Kibana after pulling these
changes to regenerate the API key with the updated privileges.

While taking a privilege snapshot is known to be problematic (e.g.,
privileges can get out of sync if updated later), it’s a known issue and
consistent with the current behavior in the Entity Store. @tiansivive
raised 🚀

# How to Test:
Steps Taken from
[elastic#221610](elastic#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

```
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

```
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

```
GET kbn:/api/entity_analytics/monitoring/entity_source/list
```
**4. Initialise monitoring engine:**
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
```

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include:
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca",
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
// ... more here

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index:
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list:

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

## Recurring Sync
Current interval is 10m, change this interval to 30s / shorter interval
for testing -
`
x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`
under 'INTERVAL'.

Should be able to add some more indicies/ delete some users from current
indicies and see results from the same users list above, on index sync
as well as this log "[Privilege Monitoring] Running privilege monitoring
task" running every x chosen INTERVAL occurrence.

---------

Co-authored-by: jaredburgettelastic <jared.burgett@elastic.co>
Co-authored-by: Tiago Vila Verde <tiago.vilaverde@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jeramy Soucy <jeramy.soucy@elastic.co>
(cherry picked from commit 441d2a5)
kibanamachine added a commit that referenced this pull request Jul 7, 2025
…#225551) (#226834)

# Backport

This will backport the following commits from `main` to `9.1`:
- [Add API Key Management and Enable Index Sync via Scheduled Task
(#225551)](#225551)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Charlotte Alexandra
Wilson","email":"CAWilson94@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-07-04T14:10:24Z","message":"Add
API Key Management and Enable Index Sync via Scheduled Task
(#225551)\n\n## Summary \nThis PR adds in API key management and runs
plain index sync on task\nexecution. Thanks to @jaredburgettelastic for
the below changes 🚀 :\n### Key Changes \nTasks now call plainIndexSync
to sync index users at specified intervals\n— this is the main
functionality change. The following updates support\nit:\n- Added
support for scoped API key usage via getClientfromApiKey in
the\ngetApiKeyManager\n- Using createInternalRepository for API key
generation, to allow for\ninternal kibana user to create saved object
client.\n- API keys now store a snapshot of the user’s privileges by
passing {}\nto role_descriptors.\n- Updated the find method in
MonitoringEntitySourceDescriptorClient to\nuse a namespace-scoped saved
objects client.\n\n## ⚠️ Important Note \nYou must fully restart
Elasticsearch and Kibana after pulling these\nchanges to regenerate the
API key with the updated privileges.\n\nWhile taking a privilege
snapshot is known to be problematic (e.g.,\nprivileges can get out of
sync if updated later), it’s a known issue and\nconsistent with the
current behavior in the Entity Store. @tiansivive\nraised 🚀\n\n# How to
Test:\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n }\n]\n```\n\n##
Recurring Sync \nCurrent interval is 10m, change this interval to 30s /
shorter interval\nfor testing
-\n`\nx-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`\nunder
'INTERVAL'.\n\nShould be able to add some more indicies/ delete some
users from current\nindicies and see results from the same users list
above, on index sync\nas well as this log \"[Privilege Monitoring]
Running privilege monitoring\ntask\" running every x chosen INTERVAL
occurrence.\n\n---------\n\nCo-authored-by: jaredburgettelastic
<jared.burgett@elastic.co>\nCo-authored-by: Tiago Vila Verde
<tiago.vilaverde@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by:
Jeramy Soucy
<jeramy.soucy@elastic.co>","sha":"441d2a51a4c249706794e2bffa5ed8f64a14b6b9","branchLabelMapping":{"^v9.2.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:
SecuritySolution","Theme: entity_analytics","Team:Entity
Analytics","backport:version","v9.1.0","v9.2.0"],"title":"Add API Key
Management and Enable Index Sync via Scheduled
Task","number":225551,"url":"https://github.com/elastic/kibana/pull/225551","mergeCommit":{"message":"Add
API Key Management and Enable Index Sync via Scheduled Task
(#225551)\n\n## Summary \nThis PR adds in API key management and runs
plain index sync on task\nexecution. Thanks to @jaredburgettelastic for
the below changes 🚀 :\n### Key Changes \nTasks now call plainIndexSync
to sync index users at specified intervals\n— this is the main
functionality change. The following updates support\nit:\n- Added
support for scoped API key usage via getClientfromApiKey in
the\ngetApiKeyManager\n- Using createInternalRepository for API key
generation, to allow for\ninternal kibana user to create saved object
client.\n- API keys now store a snapshot of the user’s privileges by
passing {}\nto role_descriptors.\n- Updated the find method in
MonitoringEntitySourceDescriptorClient to\nuse a namespace-scoped saved
objects client.\n\n## ⚠️ Important Note \nYou must fully restart
Elasticsearch and Kibana after pulling these\nchanges to regenerate the
API key with the updated privileges.\n\nWhile taking a privilege
snapshot is known to be problematic (e.g.,\nprivileges can get out of
sync if updated later), it’s a known issue and\nconsistent with the
current behavior in the Entity Store. @tiansivive\nraised 🚀\n\n# How to
Test:\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n }\n]\n```\n\n##
Recurring Sync \nCurrent interval is 10m, change this interval to 30s /
shorter interval\nfor testing
-\n`\nx-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`\nunder
'INTERVAL'.\n\nShould be able to add some more indicies/ delete some
users from current\nindicies and see results from the same users list
above, on index sync\nas well as this log \"[Privilege Monitoring]
Running privilege monitoring\ntask\" running every x chosen INTERVAL
occurrence.\n\n---------\n\nCo-authored-by: jaredburgettelastic
<jared.burgett@elastic.co>\nCo-authored-by: Tiago Vila Verde
<tiago.vilaverde@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by:
Jeramy Soucy
<jeramy.soucy@elastic.co>","sha":"441d2a51a4c249706794e2bffa5ed8f64a14b6b9"}},"sourceBranch":"main","suggestedTargetBranches":["9.1"],"targetPullRequestStates":[{"branch":"9.1","label":"v9.1.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.2.0","branchLabelMappingKey":"^v9.2.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/225551","number":225551,"mergeCommit":{"message":"Add
API Key Management and Enable Index Sync via Scheduled Task
(#225551)\n\n## Summary \nThis PR adds in API key management and runs
plain index sync on task\nexecution. Thanks to @jaredburgettelastic for
the below changes 🚀 :\n### Key Changes \nTasks now call plainIndexSync
to sync index users at specified intervals\n— this is the main
functionality change. The following updates support\nit:\n- Added
support for scoped API key usage via getClientfromApiKey in
the\ngetApiKeyManager\n- Using createInternalRepository for API key
generation, to allow for\ninternal kibana user to create saved object
client.\n- API keys now store a snapshot of the user’s privileges by
passing {}\nto role_descriptors.\n- Updated the find method in
MonitoringEntitySourceDescriptorClient to\nuse a namespace-scoped saved
objects client.\n\n## ⚠️ Important Note \nYou must fully restart
Elasticsearch and Kibana after pulling these\nchanges to regenerate the
API key with the updated privileges.\n\nWhile taking a privilege
snapshot is known to be problematic (e.g.,\nprivileges can get out of
sync if updated later), it’s a known issue and\nconsistent with the
current behavior in the Entity Store. @tiansivive\nraised 🚀\n\n# How to
Test:\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n }\n]\n```\n\n##
Recurring Sync \nCurrent interval is 10m, change this interval to 30s /
shorter interval\nfor testing
-\n`\nx-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`\nunder
'INTERVAL'.\n\nShould be able to add some more indicies/ delete some
users from current\nindicies and see results from the same users list
above, on index sync\nas well as this log \"[Privilege Monitoring]
Running privilege monitoring\ntask\" running every x chosen INTERVAL
occurrence.\n\n---------\n\nCo-authored-by: jaredburgettelastic
<jared.burgett@elastic.co>\nCo-authored-by: Tiago Vila Verde
<tiago.vilaverde@elastic.co>\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by:
Jeramy Soucy
<jeramy.soucy@elastic.co>","sha":"441d2a51a4c249706794e2bffa5ed8f64a14b6b9"}}]}]
BACKPORT-->

Co-authored-by: Charlotte Alexandra Wilson <CAWilson94@users.noreply.github.com>
Co-authored-by: jaredburgettelastic <jared.burgett@elastic.co>
Co-authored-by: Tiago Vila Verde <tiago.vilaverde@elastic.co>
Co-authored-by: Jeramy Soucy <jeramy.soucy@elastic.co>
hop-dev added a commit that referenced this pull request Jul 11, 2025
…aved objects (#227420)

## Summary

revert to using the default UUID generation for monitoring data source
saved objects and remove any reference to temp-id. Previously we were
generating the ID using the integration name or index name and the
namespace, and also overriding this with temp-id.

- remove temp-id refs
- add ID param to the get and update routes URL
- Remove the behaviour where the create API will update if the source
already exists

## Testing

Steps Taken from
[#221610](#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
kibanamachine added a commit to kibanamachine/kibana that referenced this pull request Jul 11, 2025
…aved objects (elastic#227420)

## Summary

revert to using the default UUID generation for monitoring data source
saved objects and remove any reference to temp-id. Previously we were
generating the ID using the integration name or index name and the
namespace, and also overriding this with temp-id.

- remove temp-id refs
- add ID param to the get and update routes URL
- Remove the behaviour where the create API will update if the source
already exists

## Testing

Steps Taken from
[elastic#221610](elastic#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

```
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

```
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

```
GET kbn:/api/entity_analytics/monitoring/entity_source/list
```
**4. Initialise monitoring engine:**
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
```

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include:
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca",
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
// ... more here

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index:
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list:

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit e26d309)
kibanamachine added a commit that referenced this pull request Jul 11, 2025
…urce saved objects (#227420) (#227702)

# Backport

This will backport the following commits from `main` to `9.1`:
- [[Entity Analytics][Privmon] Use default UUIDs for monitoring source
saved objects (#227420)](#227420)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Mark
Hopkin","email":"mark.hopkin@elastic.co"},"sourceCommit":{"committedDate":"2025-07-11T17:04:53Z","message":"[Entity
Analytics][Privmon] Use default UUIDs for monitoring source saved
objects (#227420)\n\n## Summary\n\nrevert to using the default UUID
generation for monitoring data source\nsaved objects and remove any
reference to temp-id. Previously we were\ngenerating the ID using the
integration name or index name and the\nnamespace, and also overriding
this with temp-id.\n\n- remove temp-id refs\n- add ID param to the get
and update routes URL\n- Remove the behaviour where the create API will
update if the source\nalready exists\n\n## Testing\n\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n
}\n]\n```\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e26d309535c1c5c1ebe1b6111e4438e08b065a7f","branchLabelMapping":{"^v9.2.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Entity
Analytics","backport:version","v9.1.0","v9.2.0"],"title":"[Entity
Analytics][Privmon] Use default UUIDs for monitoring source saved
objects","number":227420,"url":"https://github.com/elastic/kibana/pull/227420","mergeCommit":{"message":"[Entity
Analytics][Privmon] Use default UUIDs for monitoring source saved
objects (#227420)\n\n## Summary\n\nrevert to using the default UUID
generation for monitoring data source\nsaved objects and remove any
reference to temp-id. Previously we were\ngenerating the ID using the
integration name or index name and the\nnamespace, and also overriding
this with temp-id.\n\n- remove temp-id refs\n- add ID param to the get
and update routes URL\n- Remove the behaviour where the create API will
update if the source\nalready exists\n\n## Testing\n\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n
}\n]\n```\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e26d309535c1c5c1ebe1b6111e4438e08b065a7f"}},"sourceBranch":"main","suggestedTargetBranches":["9.1"],"targetPullRequestStates":[{"branch":"9.1","label":"v9.1.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.2.0","branchLabelMappingKey":"^v9.2.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/227420","number":227420,"mergeCommit":{"message":"[Entity
Analytics][Privmon] Use default UUIDs for monitoring source saved
objects (#227420)\n\n## Summary\n\nrevert to using the default UUID
generation for monitoring data source\nsaved objects and remove any
reference to temp-id. Previously we were\ngenerating the ID using the
integration name or index name and the\nnamespace, and also overriding
this with temp-id.\n\n- remove temp-id refs\n- add ID param to the get
and update routes URL\n- Remove the behaviour where the create API will
update if the source\nalready exists\n\n## Testing\n\nSteps Taken
from\n[#221610](https://github.com/elastic/kibana/pull/221610)\n- Pull
branch into local machine\n- Security experimental flag enable:
'privilegeMonitoringEnabled'\n- Start up ElasticSearch and
Kibana\n\n**1. Optional - create the default index, this should just
skip if you\ndon't make it.**\n\n```\nPOST
entity_analytics.privileged_monitoring/_doc\n{\n \"user\": {\n \"name\":
\"default name\"\n }\n}\n```\n\n**2. Create test index/
indicies**\n\n```\nPUT /tatooine-\n{\n \"mappings\": {\n \"properties\":
{\n \"user\": {\n \"properties\": {\n \"name\": {\n \"type\":
\"keyword\",\n \"fields\": {\n \"text\": {\n \"type\": \"text\"\n }\n
}\n }\n }\n }\n }\n }\n}\n\n```\n```\nPOST tatooine-/_bulk\n{ \"index\":
{} }\n{ \"user\": { \"name\": \"Luke Skywalker\" } }\n{ \"index\": {}
}\n{ \"user\": { \"name\": \"Leia Organa\" } }\n{ \"index\": {} }\n{
\"user\": { \"name\": \"Han Solo\" } }\n{ \"index\": {} }\n{ \"user\": {
\"name\": \"Chewbacca\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Obi-Wan Kenobi\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\":
\"Yoda\" } }\n{ \"index\": {} }\n{ \"user\": { \"name\": \"R2-D2\" }
}\n{ \"index\": {} }\n{ \"user\": { \"name\": \"C-3PO\" } }\n{
\"index\": {} }\n{ \"user\": { \"name\": \"Darth Vader\" } }\n\n```
\n**3. Register Monitoring Entity Source Saved Objects**\n```\nPOST
kbn:/api/entity_analytics/monitoring/entity_source\n{\n \"type\":
\"index\",\n \"name\": \"StarWars\",\n \"managed\": true,\n
\"indexPattern\": \"tatooine-\",\n \"enabled\": true,\n \"matchers\":
[\n {\n \"fields\": [\"user.role\"],\n \"values\": [\"admin\"]\n }\n
],\n \"filter\": {}\n}\n\n``` \n**- OPTIONAL: You can check what is in
the monitoring entity_source\nSO:**\n\n``` \nGET
kbn:/api/entity_analytics/monitoring/entity_source/list\n``` \n**4.
Initialise monitoring engine:** \n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init {}\n``` \n\n**5. Verify
Users in Monitoring Index**\n- Check the list of synced user, should
include: \n- - The created users\n- - The default user (if you created
it)\n\n```\nGET kbn:/api/entity_analytics/monitoring/users/list\n```
\n\n**e.g. output:**\n\n```\n[\n {\n \"id\": \"FkMJoZcB7muj1aiwb_eQ\",\n
\"user\": {\n \"name\": \"C-3PO\",\n \"is_privileged\": true\n },\n
\"labels\": {\n \"sources\": [\n \"index\"\n ],\n \"source_indices\":
[\n \"tatooine-\"\n ],\n }\n },\n {\n \"id\":
\"F0MJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\": \"Chewbacca\", \n
\"is_privileged\": true\n },\n \"labels\": {\n \"sources\": [\n
\"index\"\n ],\n \"source_indices\": [\n \"tatooine-\"\n ], \n }\n
},\n// ... more here \n\n```\n\n## Testing: Removing Stale Users\nThe
engine should soft delete users from the internal index if they
no\nlonger appear in the synced sources - e.g. label
as\nmonitoring.privileged_users: \"not_monitored\"\n**Example:**\n\n-
Delete users in index: \n```\nPOST tatooine-/_delete_by_query\n{\n
\"query\": {\n \"terms\": {\n \"user.name\": [\"Chewbacca\", \"Han
Solo\"]\n }\n }\n}\n\n```\n- re-run engine init\n\n```\nPOST
kbn:/api/entity_analytics/monitoring/engine/init\n{}\n```\n-Fetch the
updated user list: \n\n```\nGET
kbn:/api/entity_analytics/monitoring/users/list\n```\nYou should now
only see both Chewbacca and Han Solo are no longer\nprivileged:\n```\n//
..\n{\n \"id\": \"GUMJoZcB7muj1aiwb_eQ\",\n \"user\": {\n \"name\":
\"Han Solo\"\n \"is_privileged\": false \n },\n \"labels\": {\n
\"sources\": [],\n \"source_indices\": [],\n }\n
}\n]\n```\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"e26d309535c1c5c1ebe1b6111e4438e08b065a7f"}}]}]
BACKPORT-->

Co-authored-by: Mark Hopkin <mark.hopkin@elastic.co>
kertal pushed a commit to kertal/kibana that referenced this pull request Jul 25, 2025
…tic#225551)

## Summary 
This PR adds in API key management and runs plain index sync on task
execution. Thanks to @jaredburgettelastic for the below changes 🚀 :
### Key Changes 
Tasks now call plainIndexSync to sync index users at specified intervals
— this is the main functionality change. The following updates support
it:
- Added support for scoped API key usage via getClientfromApiKey in the
getApiKeyManager
- Using createInternalRepository for API key generation, to allow for
internal kibana user to create saved object client.
- API keys now store a snapshot of the user’s privileges by passing {}
to role_descriptors.
- Updated the find method in MonitoringEntitySourceDescriptorClient to
use a namespace-scoped saved objects client.

## ⚠️ Important Note 
You must fully restart Elasticsearch and Kibana after pulling these
changes to regenerate the API key with the updated privileges.

While taking a privilege snapshot is known to be problematic (e.g.,
privileges can get out of sync if updated later), it’s a known issue and
consistent with the current behavior in the Entity Store. @tiansivive
raised 🚀

# How to Test:
Steps Taken from
[elastic#221610](elastic#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

## Recurring Sync 
Current interval is 10m, change this interval to 30s / shorter interval
for testing -
`
x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/constants.ts`
under 'INTERVAL'.

Should be able to add some more indicies/ delete some users from current
indicies and see results from the same users list above, on index sync
as well as this log "[Privilege Monitoring] Running privilege monitoring
task" running every x chosen INTERVAL occurrence.

---------

Co-authored-by: jaredburgettelastic <jared.burgett@elastic.co>
Co-authored-by: Tiago Vila Verde <tiago.vilaverde@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jeramy Soucy <jeramy.soucy@elastic.co>
kertal pushed a commit to kertal/kibana that referenced this pull request Jul 25, 2025
…aved objects (elastic#227420)

## Summary

revert to using the default UUID generation for monitoring data source
saved objects and remove any reference to temp-id. Previously we were
generating the ID using the integration name or index name and the
namespace, and also overriding this with temp-id.

- remove temp-id refs
- add ID param to the get and update routes URL
- Remove the behaviour where the create API will update if the source
already exists

## Testing

Steps Taken from
[elastic#221610](elastic#221610)
- Pull branch into local machine
- Security experimental flag enable: 'privilegeMonitoringEnabled'
- Start up ElasticSearch and Kibana

**1. Optional - create the default index, this should just skip if you
don't make it.**

```
POST entity_analytics.privileged_monitoring/_doc
{
  "user": {
    "name": "default name"
  }
}
```

**2. Create test index/ indicies**

```
PUT /tatooine-
{
  "mappings": {
    "properties": {
      "user": {
        "properties": {
          "name": {
            "type": "keyword",
            "fields": {
              "text": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

```
```
POST tatooine-/_bulk
{ "index": {} }
{ "user": { "name": "Luke Skywalker" } }
{ "index": {} }
{ "user": { "name": "Leia Organa" } }
{ "index": {} }
{ "user": { "name": "Han Solo" } }
{ "index": {} }
{ "user": { "name": "Chewbacca" } }
{ "index": {} }
{ "user": { "name": "Obi-Wan Kenobi" } }
{ "index": {} }
{ "user": { "name": "Yoda" } }
{ "index": {} }
{ "user": { "name": "R2-D2" } }
{ "index": {} }
{ "user": { "name": "C-3PO" } }
{ "index": {} }
{ "user": { "name": "Darth Vader" } }

``` 
**3. Register Monitoring Entity Source Saved Objects**
```
POST kbn:/api/entity_analytics/monitoring/entity_source
{
  "type": "index",
  "name": "StarWars",
  "managed": true,
  "indexPattern": "tatooine-",
  "enabled": true,
 "matchers": [
    {
      "fields": ["user.role"],
      "values": ["admin"]
    }
  ],
  "filter": {}
}

``` 
**- OPTIONAL: You can check what is in the monitoring entity_source
SO:**

``` 
GET kbn:/api/entity_analytics/monitoring/entity_source/list
``` 
**4. Initialise monitoring engine:** 
```
POST kbn:/api/entity_analytics/monitoring/engine/init {}
``` 

**5. Verify Users in Monitoring Index**
- Check the list of synced user, should include: 
- - The created users
- - The default user (if you created it)

```
GET kbn:/api/entity_analytics/monitoring/users/list
```   

**e.g. output:**

```
[
 {
   "id": "FkMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "C-3PO",
     "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],
   }
 },
 {
   "id": "F0MJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Chewbacca", 
      "is_privileged": true
   },
   "labels": {
     "sources": [
       "index"
     ],
     "source_indices": [
       "tatooine-"
     ],     
   }
 },
// ... more here 

```

## Testing: Removing Stale Users
The engine should soft delete users from the internal index if they no
longer appear in the synced sources - e.g. label as
monitoring.privileged_users: "not_monitored"
**Example:**

- Delete users in index: 
```
POST tatooine-/_delete_by_query
{
 "query": {
   "terms": {
     "user.name": ["Chewbacca", "Han Solo"]
   }
 }
}

```
- re-run engine init

```
POST kbn:/api/entity_analytics/monitoring/engine/init
{}
```
-Fetch the updated user list: 

```
GET kbn:/api/entity_analytics/monitoring/users/list
```
You should now only see both Chewbacca and Han Solo are no longer
privileged:
```
// ..
{
   "id": "GUMJoZcB7muj1aiwb_eQ",
   "user": {
     "name": "Han Solo"
     "is_privileged": false 
   },
   "labels": {
     "sources": [],
     "source_indices": [],
   }
 }
]
```

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting Feature:Entity Analytics Security Solution Entity Analytics features release_note:skip Skip the PR/issue when compiling release notes Team:Entity Analytics Security Entity Analytics Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. Theme: entity_analytics v9.1.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants