From 1ce627deeb49a2bfc10e9d7ee39c26173fe4ee75 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 17 Mar 2026 17:20:44 -0400 Subject: [PATCH 1/4] Add new setting property 'Sensitive' for tiering dynamic settings Signed-off-by: Craig Perkins --- .../settings/AbstractScopedSettings.java | 9 +++++++++ .../opensearch/common/settings/Setting.java | 20 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/common/settings/AbstractScopedSettings.java b/server/src/main/java/org/opensearch/common/settings/AbstractScopedSettings.java index d3ed1d839093f..f9d57aabe3c2d 100644 --- a/server/src/main/java/org/opensearch/common/settings/AbstractScopedSettings.java +++ b/server/src/main/java/org/opensearch/common/settings/AbstractScopedSettings.java @@ -775,6 +775,15 @@ public boolean isUnmodifiableOnRestoreSetting(String key) { return setting != null && setting.isUnmodifiableOnRestore(); } + /** + * Returns true if the setting for the given key is sensitive, meaning it requires + * security admin privileges to be updated dynamically. Otherwise false. + */ + public boolean isSensitiveSetting(String key) { + final Setting setting = get(key); + return setting != null && setting.isSensitive(); + } + /** * Returns a settings object that contains all settings that are not * already set in the given source. The diff contains either the default value for each diff --git a/server/src/main/java/org/opensearch/common/settings/Setting.java b/server/src/main/java/org/opensearch/common/settings/Setting.java index f77568bf4f711..dd5a3c7aaa160 100644 --- a/server/src/main/java/org/opensearch/common/settings/Setting.java +++ b/server/src/main/java/org/opensearch/common/settings/Setting.java @@ -177,7 +177,14 @@ public enum Property { * Mark this setting as immutable on snapshot restore * i.e. the setting will not be allowed to be removed or modified during restore */ - UnmodifiableOnRestore + UnmodifiableOnRestore, + + /** + * Mark this dynamic setting as sensitive. Sensitive settings require security admin + * privileges to be updated dynamically. This property can only be applied to settings + * that also have {@link Property#Dynamic}. + */ + Sensitive } private final Key key; @@ -217,6 +224,9 @@ private Setting( } else if (propertiesAsSet.contains(Property.UnmodifiableOnRestore) && propertiesAsSet.contains(Property.Dynamic)) { throw new IllegalArgumentException("UnmodifiableOnRestore setting [" + key + "] cannot be dynamic"); } + if (propertiesAsSet.contains(Property.Sensitive) && propertiesAsSet.contains(Property.Dynamic) == false) { + throw new IllegalArgumentException("sensitive setting [" + key + "] must be dynamic"); + } checkPropertyRequiresIndexScope(propertiesAsSet, Property.NotCopyableOnResize); checkPropertyRequiresIndexScope(propertiesAsSet, Property.InternalIndex); checkPropertyRequiresIndexScope(propertiesAsSet, Property.PrivateIndex); @@ -361,6 +371,14 @@ public final boolean isUnmodifiableOnRestore() { return properties.contains(Property.UnmodifiableOnRestore); } + /** + * Returns true if this setting is sensitive, meaning it requires security admin + * privileges to be updated dynamically. Otherwise false. + */ + public final boolean isSensitive() { + return properties.contains(Property.Sensitive); + } + public final boolean isInternalIndex() { return properties.contains(Property.InternalIndex); } From 79c3855a851c1b32e50bed73d5b0e20863fbe589 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 17 Mar 2026 17:32:53 -0400 Subject: [PATCH 2/4] Add unit test Signed-off-by: Craig Perkins --- CHANGELOG.md | 1 + .../java/org/opensearch/common/settings/SettingTests.java | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c844a12bd5929..6568eefe78bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - WLM group custom search settings - groundwork and timeout ([#20536](https://github.com/opensearch-project/OpenSearch/issues/20536)) - Expose JVM runtime metrics via telemetry framework ([#20844](https://github.com/opensearch-project/OpenSearch/pull/20844)) - Add intra segment support for single-value metric aggregations ([#20503](https://github.com/opensearch-project/OpenSearch/pull/20503)) +- Add new setting property 'Sensitive' for tiering dynamic settings ([#20901](https://github.com/opensearch-project/OpenSearch/pull/20901)) ### Changed - Make telemetry `Tags` immutable ([#20788](https://github.com/opensearch-project/OpenSearch/pull/20788)) diff --git a/server/src/test/java/org/opensearch/common/settings/SettingTests.java b/server/src/test/java/org/opensearch/common/settings/SettingTests.java index a0788b0c83e11..68cea5a4200c7 100644 --- a/server/src/test/java/org/opensearch/common/settings/SettingTests.java +++ b/server/src/test/java/org/opensearch/common/settings/SettingTests.java @@ -1447,6 +1447,14 @@ public void testRejectConflictingDynamicAndUnmodifiableOnRestoreProperties() { assertThat(ex.getMessage(), containsString("UnmodifiableOnRestore setting [foo.bar] cannot be dynamic")); } + public void testRejectSensitiveWithoutDynamic() { + IllegalArgumentException ex = expectThrows( + IllegalArgumentException.class, + () -> Setting.simpleString("foo.bar", Property.Sensitive, Property.NodeScope) + ); + assertThat(ex.getMessage(), containsString("sensitive setting [foo.bar] must be dynamic")); + } + public void testRejectNonIndexScopedUnmodifiableOnRestoreSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, From b0ab01cab41cfdfc783522f23641724cf19ecfa0 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 17 Mar 2026 20:29:58 -0400 Subject: [PATCH 3/4] Require persistent for sensitive settings Signed-off-by: Craig Perkins --- .../cluster/settings/SettingsUpdater.java | 9 +++++++++ .../settings/SettingsUpdaterTests.java | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java index c7030764a5b47..034b87bb07b29 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java @@ -76,6 +76,7 @@ synchronized ClusterState updateSettings( final Settings persistentToApply, final Logger logger ) { + validateNoTransientSensitiveSettings(transientToApply); boolean changed = false; /* @@ -212,4 +213,12 @@ private void logInvalidSetting( ); } + private void validateNoTransientSensitiveSettings(final Settings transientSettings) { + for (String key : transientSettings.keySet()) { + if (clusterSettings.isSensitiveSetting(key)) { + throw new IllegalArgumentException("sensitive setting [" + key + "] must be updated using persistent settings"); + } + } + } + } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/settings/SettingsUpdaterTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/settings/SettingsUpdaterTests.java index 8d65c24f36b63..7052e2a41a27b 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/settings/SettingsUpdaterTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/settings/SettingsUpdaterTests.java @@ -670,4 +670,24 @@ public void testUpdateOfValidationDependentSettings() { assertThat(exception.getMessage(), equalTo("[high]=2 is lower than [low]=5")); } + public void testRejectSensitiveSettingInTransient() { + Setting sensitiveSetting = Setting.simpleString( + "sensitive.setting", + Property.Dynamic, + Property.NodeScope, + Property.Sensitive + ); + final Set> settingsSet = Stream.concat(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS.stream(), Stream.of(sensitiveSetting)) + .collect(Collectors.toSet()); + ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, settingsSet); + SettingsUpdater updater = new SettingsUpdater(clusterSettings); + ClusterState state = ClusterState.builder(new ClusterName("foo")).metadata(Metadata.builder().build()).build(); + + IllegalArgumentException ex = expectThrows( + IllegalArgumentException.class, + () -> updater.updateSettings(state, Settings.builder().put("sensitive.setting", "value").build(), Settings.EMPTY, logger) + ); + assertThat(ex.getMessage(), equalTo("sensitive setting [sensitive.setting] must be updated using persistent settings")); + } + } From 634d09394db9ec26c2f3197cd943fd16b7dba4fb Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 23 Mar 2026 16:20:31 -0400 Subject: [PATCH 4/4] Update comment Signed-off-by: Craig Perkins --- .../main/java/org/opensearch/common/settings/Setting.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/common/settings/Setting.java b/server/src/main/java/org/opensearch/common/settings/Setting.java index dd5a3c7aaa160..aacbefba6838e 100644 --- a/server/src/main/java/org/opensearch/common/settings/Setting.java +++ b/server/src/main/java/org/opensearch/common/settings/Setting.java @@ -180,9 +180,10 @@ public enum Property { UnmodifiableOnRestore, /** - * Mark this dynamic setting as sensitive. Sensitive settings require security admin - * privileges to be updated dynamically. This property can only be applied to settings - * that also have {@link Property#Dynamic}. + * Marks a setting as sensitive. Can only be applied to dynamic settings. + * The Sensitive property has default enforcement but enables plugins to implement + * different policies for these settings. In practice the security plugin will + * require higher privileges for modifying sensitive settings. */ Sensitive }