Skip to content

Versioned Security Configuration Management#5357

Merged
shikharj05 merged 17 commits intoopensearch-project:mainfrom
divyanshi-0402:securityConfigVersioning
Jun 18, 2025
Merged

Versioned Security Configuration Management#5357
shikharj05 merged 17 commits intoopensearch-project:mainfrom
divyanshi-0402:securityConfigVersioning

Conversation

@divyanshi-0402
Copy link
Copy Markdown
Contributor

Description

Implementing a comprehensive versioning system for the OpenSearch security index. This system will enable rollback and roll-forward capabilities, provide a history of changes, and allow administrators to easily view and select specific configurations from the past.

New System Index: Introduce a dedicated system index named .opendistro_security_config_versions to store versioned security configurations.

Versioning Mechanism:

  • Generate a new version (e.g., v1, v2, v3) for each change in the security index.
  • Store the complete security configuration for each version in the new system index.
  • Have retention policy to purge old versions

Updated Security Config Update Flow: Modify the existing security configuration update process to

  • Create a new version entry in the .opendistro_security_config_versions index
  • Store the full configuration snapshot for each version

Index status with 10 security config versions :

green open .opendistro_security_config_versions 4Hvxs5KOST6gN2YtFNHlZg 1 0 1 0 60.1kb 60.1kb

Index Structure :

{
  "_index": ".opendistro_security_config_versions",
  "_id": "opendistro_security_config_versions",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "versions": [
      {
        "version_id": "v1",
        "timestamp": "2025-05-22T08:46:11.887620466Z",
        "modified_by": "system",
        "security_configs": {
          "rolesmapping": {
            "lastUpdated": "2025-05-22T08:46:11.887620466Z",
            "configData": {
              "all_access": {
                "hosts": [],
                "users": [],
                "reserved": false,
                "hidden": false,
                "backend_roles": ["admin"],
                "and_backend_roles": [],
                "description": "Maps admin to all_access"
              },
              "kibana_server": {
                "hosts": [],
                "users": ["kibanaserver"],
                "reserved": true,
                "hidden": false,
                "backend_roles": [],
                "and_backend_roles": []
              }
              ... 
            }
          },
          ...
        }
      },
      ...
    ]
  }
}

Issues Resolved

Related to #5093

Testing

Manual Testing :

  • No version update in case of no change in security configurations
 Index request for .opendistro_security_config_versions
 Index .opendistro_security_config_versions already exists
 Version index already exists, skipping initialization.
 Node started, try to initialize it. Wait for at least yellow cluster state....
 No changes detected in security configuration.
 No changes detected in security configuration. Skipping version update.

  • Version update on PUT request :
curl -XPUT https://localhost:9200/_plugins/_security/api/internalusers/testuser -u 'admin:Divvv@admin123' —insecure -H 'Content-Type: application/json' -d'
{
 "password": "Tz9$mK3pL#qR7xJ@",
 "opendistro_security_roles": [ ],
 "backend_roles": [ ],
 "attributes": { }
}
‘
{"status":"CREATED","message":"'testuser' created."}%
 Detected changes in security configuration: [{"op":"add","path":"/internalusers/testuser","value":{"attributes":{},"backend_roles":[],"hash":"$2y$12$7rQFz2qQD5rDghg7lQaLQOi1aY1wC91XbZ7HKo.Ru5unNL6dSIlyC","hidden":false,"opendistro_security_roles":[],"reserved":false,"static":false}}]
 Successfully saved version v2 to .opendistro_security_config_versions
  • Version update on PATCH request :
curl -k -X PATCH 'https://localhost:9200/_plugins/_security/api/internalusers/testuser' \
 -u 'admin:Divvv@admin123' \
 -H 'Content-Type: application/json' \
 -d ‘
 [
 {
 "op": "add",
 "path": "/backend_roles",
 "value": ["admin"]
 }
 ]‘


{"status":"OK","message":"'testuser' updated."}%
 Detected changes in security configuration: [{"op":"add","path":"/internalusers/testuser/backend_roles/0","value":"admin"}]
 Successfully saved version v3 to .opendistro_security_config_versions

Check List

  • New functionality includes testing
  • New functionality has been documented
  • New Roles/Permissions have a corresponding security dashboards plugin PR
  • API changes companion pull request created
  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Divyanshi Chouksey added 2 commits May 22, 2025 02:01
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
@codecov
Copy link
Copy Markdown

codecov bot commented May 22, 2025

Codecov Report

Attention: Patch coverage is 77.31092% with 81 lines in your changes missing coverage. Please review.

Project coverage is 72.42%. Comparing base (441577b) to head (32232e6).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ty/configuration/SecurityConfigVersionHandler.java 76.54% 31 Missing and 7 partials ⚠️
...ty/configuration/SecurityConfigVersionsLoader.java 74.07% 17 Missing and 4 partials ⚠️
...ty/configuration/SecurityConfigDiffCalculator.java 68.08% 10 Missing and 5 partials ⚠️
...y/configuration/SecurityConfigVersionDocument.java 87.50% 4 Missing and 3 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5357      +/-   ##
==========================================
+ Coverage   72.33%   72.42%   +0.08%     
==========================================
  Files         384      388       +4     
  Lines       23820    24177     +357     
  Branches     3669     3706      +37     
==========================================
+ Hits        17231    17509     +278     
- Misses       4785     4850      +65     
- Partials     1804     1818      +14     
Files with missing lines Coverage Δ
.../opensearch/security/OpenSearchSecurityPlugin.java 84.69% <100.00%> (+0.11%) ⬆️
...ch/security/securityconf/DynamicConfigFactory.java 64.47% <100.00%> (+1.20%) ⬆️
...g/opensearch/security/support/ConfigConstants.java 95.23% <ø> (ø)
...y/configuration/SecurityConfigVersionDocument.java 87.50% <87.50%> (ø)
...ty/configuration/SecurityConfigDiffCalculator.java 68.08% <68.08%> (ø)
...ty/configuration/SecurityConfigVersionsLoader.java 74.07% <74.07%> (ø)
...ty/configuration/SecurityConfigVersionHandler.java 76.54% <76.54%> (ø)

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@peternied
Copy link
Copy Markdown
Member

@divyanshi-0402 Thanks for working on this.

Maybe this is a good opportunity to change the configuration process to instead add a new version in a single index (Great opportunity to rename our index!) and when loading the configuration the plugin loads specific versions from the set of all changes. For rollback this would mean just changing pointers rather than having to change the data itself.

@peternied
Copy link
Copy Markdown
Member

Here is a diagram showing how we'd move between immutable versions of the configuration. I'll be curious what you think of this approach.

flowchart LR
  subgraph .security_config
    v1["v1"]
    v2["v2"]
    v3["v3"]
  end

  Load["Active Security Config"]

  %% Initial pointer to v3
  Load --> v3

  %% Rollback pointer to v2 (dashed red)
  Load -. rollback .-> v2

  %% Style the rollback link
  linkStyle 1 stroke:#f66,stroke-width:2px,stroke-dasharray:5,5
Loading

@cwperks
Copy link
Copy Markdown
Member

cwperks commented May 22, 2025

Thank you for the PR @divyanshi-0402 ! I will take a pass shortly and provide some comments.

On a high-level, I think we should store the diffs instead of the full snapshots as there would be a lot of redundant information stored for each snapshot of the security config for small changes. I believe that's how collaborative word editors work where they keep track of the diffs in order to implement undo and redo without having to keep snapshots of the document being edited. For clusters where the security index is small, snapshots may be performant but we should design this regardless of the size of the security index and support cases where the security index could contain MBs of data with more frequent changes.

The audit log does have capability to keep track of diffs to the security index using the COMPLIANCE_INTERNAL_CONFIG_WRITE auditlog category and computes the diffs here.

Copy link
Copy Markdown
Collaborator

@shikharj05 shikharj05 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 the work @divyanshi-0402 ! Some general comments-
JavaDoc on the classes will be helpful; add comments in places there is custom logic to retain, delete, sort, etc. Add more coverage for tests :)
You should also update the README file with this new feature - this will help to understand what the feature does and how it can be used.
Similarly, add Changelog entry as well.

@shikharj05
Copy link
Copy Markdown
Collaborator

On a high-level, I think we should store the diffs instead of the full snapshots as there would be a lot of redundant information stored for each snapshot of the security config for small changes. I believe that's how collaborative word editors work where they keep track of the diffs in order to implement undo and redo without having to keep snapshots of the document being edited. For clusters where the security index is small, snapshots may be performant but we should design this regardless of the size of the security index and support cases where the security index could contain MBs of data with more frequent changes.

Storing the diffs/incremental changes can be helpful to reduce storage overhead. I think for now allowing users to configure versions to retain is also a good start. One problem with just storing diffs is that it would require more logic to restore versions or for users to look at different versions of the security config. For example, for a search request on the index that stores versions, this would mean using the current config and adding/removing configs (replay the changes) to show a meaningful response OR maybe showing the diff is okay for now. I think the current mindset is to support versioning of configs - however, we should allow customers to enable this and dynamically change number of versions to store.

@divyanshi-0402
Copy link
Copy Markdown
Contributor Author

@divyanshi-0402 Thanks for working on this.

Maybe this is a good opportunity to change the configuration process to instead add a new version in a single index (Great opportunity to rename our index!) and when loading the configuration the plugin loads specific versions from the set of all changes. For rollback this would mean just changing pointers rather than having to change the data itself.

@peternied Thanks for you suggestions!
While consolidating into a single index is an interesting idea, updating .opendistro_security directly would require widespread changes across the entire codebase and carries a higher risk of introducing instability. From an architectural perspective, keeping version history in a separate index helps isolate configuration versions, and reduces the risk of corrupting active security configurations.

Divyanshi Chouksey and others added 2 commits May 28, 2025 04:08
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
Signed-off-by: Divyanshi Chouksey <77962346+divyanshi-0402@users.noreply.github.com>
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
@nibix
Copy link
Copy Markdown
Collaborator

nibix commented Jun 13, 2025

One question: In the PR description, you describe your manual testing steps. Could you give also the specs of the cluster on which you did the manual testing? Especially, I would be interested in the number of nodes in the cluster.

@shikharj05
Copy link
Copy Markdown
Collaborator

shikharj05 commented Jun 13, 2025

couple of high-level questions- (let me know if you have already addressed these elsewhere)

  1. How does this handle conflict between new write while trying to store previous version?
  2. Is security configuration reloaded in-memory during rollback? Does that also trigger storing a new version, if yes, this could lead to a infinite loop? (new update -> save version -> triggers reload/updates security index ->?? treats this as a new update and starts saving it again) If no, how is it avoided?
  3. This is more for the next PR, View API and Rollback API for Versioned Security Configurations #5366 - can the permission to view/rollback be delegated?
  4. Is the new index protected at same level as security index? for e.g. users without access to security index/security roles, should not be able to execute a search on the version history index.

@shikharj05 shikharj05 self-requested a review June 14, 2025 12:36
divyanshi-0402 and others added 2 commits June 16, 2025 13:04
Signed-off-by: Divyanshi Chouksey <77962346+divyanshi-0402@users.noreply.github.com>
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
@divyanshi-0402
Copy link
Copy Markdown
Contributor Author

One question: In the PR description, you describe your manual testing steps. Could you give also the specs of the cluster on which you did the manual testing? Especially, I would be interested in the number of nodes in the cluster.

We have tested in a single node cluster, will be doing testing with multiple nodes as well.

@divyanshi-0402
Copy link
Copy Markdown
Contributor Author

couple of high-level questions- (let me know if you have already addressed these elsewhere)

  1. How does this handle conflict between new write while trying to store previous version?
  2. Is security configuration reloaded in-memory during rollback? Does that also trigger storing a new version, if yes, this could lead to a infinite loop? (new update -> save version -> triggers reload/updates security index ->?? treats this as a new update and starts saving it again) If no, how is it avoided?
  3. This is more for the next PR, View API and Rollback API for Versioned Security Configurations #5366 - can the permission to view/rollback be delegated?
  4. Is the new index protected at same level as security index? for e.g. users without access to security index/security roles, should not be able to execute a search on the version history index.
  1. In case of version conflict we handle it through exception.
  2. After the opendistro_security index is updated, we update the version index with the new version, there's no infinite loop.
  3. Ack, will be adding the permissions
  4. Yes the permission evaluation is done.

Copy link
Copy Markdown
Member

@cwperks cwperks left a comment

Choose a reason for hiding this comment

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

@divyanshi-0402 thank you for replying to and addressing the feedback in a timely manner.

I think any outstanding comments can be addressed in a follow-up PR.

@nibix
Copy link
Copy Markdown
Collaborator

nibix commented Jun 16, 2025

We have tested in a single node cluster, will be doing testing with multiple nodes as well.

To be honest, IMHO, a multi node test is absolutely essential, especially if there are no integration tests.

@shikharj05
Copy link
Copy Markdown
Collaborator

@divyanshi-0402 thank you for replying to and addressing the feedback in a timely manner.

I think any outstanding comments can be addressed in a follow-up PR.

+1, Thanks @divyanshi-0402! Can you or @nagarajg17 create a GH issue to keep track of pending comments/changes/follow-ups etc?

@nagarajg17
Copy link
Copy Markdown
Contributor

@shikharj05, @cwperks, @nibix Tracking the follow up items here #5407
cc - @divyanshi-0402

@divyanshi-0402
Copy link
Copy Markdown
Contributor Author

Test details for multi-node testing:

Created two nodes : opensearch-node1 and opensearch-node1

node 1 :

./bin/opensearch -E cluster.name=my-cluster -E node.name=node1 -E path.data=./data1 -E http.port=9200 -E transport.port=9300

[node1] Successfully saved version v1 to .opensearch_security_config_versions

[INFO ][o.o.s.c.ConfigurationRepository] [node1] Node 'node1' initialized

node 2 :

./bin/opensearch -E cluster.name=my-cluster -E node.name=node2 -E path.data=./data2 -E http.port=9201 -E transport.port=9301

[INFO ][o.o.s.c.ConfigurationRepository] [node2] Node 'node2' initialized

Updated Security Configurations from node 1 :

curl -XPUT https://localhost:9200/_plugins/_security/api/internalusers/testuser -u 'admin:admin' --insecure -H 'Content-Type: application/json' -d'
{
  "password": "Tz9$mK3pL#qR7xJ@",
  "opendistro_security_roles": [ ],
  "backend_roles": [ ],
  "attributes": { }
}
'
{"status":"CREATED","message":"'testuser' created."}

[INFO ][o.o.s.c.SecurityConfigDiffCalculator] [node1] Detected changes in security configuration: [{"op":"add","path":"/internalusers/testuser","value":{"attributes":{},"backend_roles":[],"hash":"$2y$12$QP.H/ErmdcRh3Azc.55/TO.WdtSAWuKaq.On2ldX/uS8j6LSEkyg6","hidden":false,"opendistro_security_roles":[],"reserved":false,"static":false}}]
[INFO ][o.o.s.c.SecurityConfigVersionHandler] [node1] Successfully saved version v2 to .opensearch_security_config_versions

Fetched users from node 2 :

curl -XGET "https://localhost:9201/_plugins/_security/api/internalusers/?pretty" -u 'admin:admin' --insecure

{
  "logstash" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [
      "logstash"
    ],
    "attributes" : { },
    "description" : "Demo logstash user, using external role mapping",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "snapshotrestore" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [
      "snapshotrestore"
    ],
    "attributes" : { },
    "description" : "Demo snapshotrestore user, using external role mapping",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "admin" : {
    "hash" : "",
    "reserved" : true,
    "hidden" : false,
    "backend_roles" : [
      "admin"
    ],
    "attributes" : { },
    "description" : "Demo admin user",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "testuser" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [ ],
    "attributes" : { },
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "kibanaserver" : {
    "hash" : "",
    "reserved" : true,
    "hidden" : false,
    "backend_roles" : [ ],
    "attributes" : { },
    "description" : "Demo OpenSearch Dashboards user",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "kibanaro" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [
      "kibanauser",
      "readall"
    ],
    "attributes" : {
      "attribute1" : "value1",
      "attribute2" : "value2",
      "attribute3" : "value3"
    },
    "description" : "Demo OpenSearch Dashboards read only user, using external role mapping",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "readall" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [
      "readall"
    ],
    "attributes" : { },
    "description" : "Demo readall user, using external role mapping",
    "opendistro_security_roles" : [ ],
    "static" : false
  },
  "anomalyadmin" : {
    "hash" : "",
    "reserved" : false,
    "hidden" : false,
    "backend_roles" : [ ],
    "attributes" : { },
    "description" : "Demo anomaly admin user, using internal role",
    "opendistro_security_roles" : [
      "anomaly_full_access"
    ],
    "static" : false
  }
}

@shikharj05 shikharj05 merged commit e80302b into opensearch-project:main Jun 18, 2025
120 of 121 checks passed
aidenlindsay pushed a commit to aidenlindsay/security that referenced this pull request Jun 23, 2025
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
Signed-off-by: Divyanshi Chouksey <77962346+divyanshi-0402@users.noreply.github.com>
Co-authored-by: Divyanshi Chouksey <divvv@amazon.com>
Co-authored-by: Jialiang Liang <ryanleeang@gmail.com>
Co-authored-by: Darshit Chanpura <dchanp@amazon.com>
Signed-off-by: Aiden Lindsay <aiden.o.lindsay@gmail.com>
aidenlindsay pushed a commit to aidenlindsay/security that referenced this pull request Jun 24, 2025
Signed-off-by: Divyanshi Chouksey <divvv@amazon.com>
Signed-off-by: Divyanshi Chouksey <77962346+divyanshi-0402@users.noreply.github.com>
Co-authored-by: Divyanshi Chouksey <divvv@amazon.com>
Co-authored-by: Jialiang Liang <ryanleeang@gmail.com>
Co-authored-by: Darshit Chanpura <dchanp@amazon.com>
Signed-off-by: Aiden Lindsay <aiden.o.lindsay@gmail.com>
@nagarajg17 nagarajg17 mentioned this pull request Jul 7, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants