Skip to content

Commit 87280ef

Browse files
committed
Added insecureString options to allow to easier migration
Signed-off-by: Chris White <chriswhite199@gmail.com>
1 parent cb26035 commit 87280ef

3 files changed

Lines changed: 152 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6464
- Changed http code on create index API with bad input raising NotXContentException from 500 to 400 ([#4773](https://github.com/opensearch-project/OpenSearch/pull/4773))
6565
- Change http code for DecommissioningFailedException from 500 to 400 ([#5283](https://github.com/opensearch-project/OpenSearch/pull/5283))
6666
- Pre conditions check before updating weighted routing metadata ([#4955](https://github.com/opensearch-project/OpenSearch/pull/4955))
67+
- Allow insecure string settings to warn-log usage and advise to migration of a newer secure variant ([#5496](https://github.com/opensearch-project/OpenSearch/pull/5496))
6768

6869
### Deprecated
6970

server/src/main/java/org/opensearch/common/settings/SecureSetting.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
import java.util.EnumSet;
4141
import java.util.Set;
4242

43+
import org.apache.logging.log4j.LogManager;
44+
import org.apache.logging.log4j.Logger;
45+
4346
/**
4447
* A secure setting.
4548
*
@@ -161,7 +164,17 @@ public static Setting<SecureString> secureString(String name, Setting<SecureStri
161164
* @see #secureString(String, Setting, Property...)
162165
*/
163166
public static Setting<SecureString> insecureString(String name) {
164-
return new InsecureStringSetting(name);
167+
return insecureString(name, null, false);
168+
}
169+
170+
/**
171+
* A setting which contains a sensitive string, but usage is logged when found outside secure settings, regardless
172+
* of the opensearch.allow_insecure_settings value. Typically. this is used when migrating old legacy settings
173+
* to secure variants while preserving existing functionality.
174+
* @see #insecureString(String)
175+
*/
176+
public static Setting<SecureString> insecureString(String name, String secureName, boolean allowWithWarning) {
177+
return new InsecureStringSetting(name, secureName, allowWithWarning);
165178
}
166179

167180
/**
@@ -206,22 +219,40 @@ SecureString getFallback(Settings settings) {
206219
* @opensearch.internal
207220
*/
208221
private static class InsecureStringSetting extends Setting<SecureString> {
222+
private static final Logger LOG = LogManager.getLogger(InsecureStringSetting.class);
209223
private final String name;
224+
private final String secureName;
225+
private final boolean allowWithWarning;
226+
227+
private boolean warningLogged;
210228

211-
private InsecureStringSetting(String name) {
229+
private InsecureStringSetting(String name, String secureName, boolean allowWithWarning) {
212230
super(name, "", SecureString::new, Property.Deprecated, Property.Filtered, Property.NodeScope);
213231
this.name = name;
232+
this.secureName = secureName;
233+
this.allowWithWarning = allowWithWarning;
214234
}
215235

216236
@Override
217237
public SecureString get(Settings settings) {
218-
if (ALLOW_INSECURE_SETTINGS == false && exists(settings)) {
219-
throw new IllegalArgumentException(
220-
"Setting [" + name + "] is insecure, " + "but property [allow_insecure_settings] is not set"
221-
);
238+
if (exists(settings)) {
239+
logUsage();
240+
241+
if (ALLOW_INSECURE_SETTINGS == false && this.allowWithWarning == false) {
242+
throw new IllegalArgumentException(
243+
"Setting [" + name + "] is insecure, " + "but property [allow_insecure_settings] is not set"
244+
);
245+
}
222246
}
223247
return super.get(settings);
224248
}
249+
250+
private synchronized void logUsage() {
251+
if (!this.warningLogged) {
252+
LOG.warn("Setting [{}] is insecure, but a secure variant [{}] is advised to be used instead", this.name, this.secureName);
253+
this.warningLogged = true;
254+
}
255+
}
225256
}
226257

227258
/**
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.common.settings;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
import org.apache.logging.log4j.LogManager;
15+
import org.apache.logging.log4j.core.LogEvent;
16+
import org.apache.logging.log4j.core.appender.AbstractAppender;
17+
import org.apache.logging.log4j.core.config.Property;
18+
import org.junit.After;
19+
import org.junit.Assert;
20+
import org.junit.Before;
21+
22+
import org.opensearch.common.logging.Loggers;
23+
import org.opensearch.test.OpenSearchTestCase;
24+
25+
public class InsecureSettingTests extends OpenSearchTestCase {
26+
private List<String> rootLogMsgs = new ArrayList<>();
27+
private AbstractAppender rootAppender;
28+
29+
protected void assertSettingWarning() {
30+
assertWarnings(
31+
"[setting.name] setting was deprecated in OpenSearch and will be removed in a future release! See the breaking changes documentation for the next major version."
32+
);
33+
34+
Assert.assertTrue(
35+
this.rootLogMsgs.stream()
36+
.anyMatch(
37+
msg -> msg.equals(
38+
"Setting [setting.name] is insecure, but a secure variant [setting.name_secure] is advised to be used instead"
39+
)
40+
)
41+
);
42+
}
43+
44+
@Before
45+
public void addInsecureSettingsAppender() {
46+
this.rootLogMsgs.clear();
47+
rootAppender = new AbstractAppender("root", null, null, true, Property.EMPTY_ARRAY) {
48+
@Override
49+
public void append(LogEvent event) {
50+
String message = event.getMessage().getFormattedMessage();
51+
InsecureSettingTests.this.rootLogMsgs.add(message);
52+
}
53+
};
54+
Loggers.addAppender(LogManager.getRootLogger(), rootAppender);
55+
rootAppender.start();
56+
}
57+
58+
@After
59+
public void removeInsecureSettingsAppender() {
60+
Loggers.removeAppender(LogManager.getRootLogger(), rootAppender);
61+
}
62+
63+
public void testShouldRaiseExceptionByDefault() {
64+
final var setting = SecureSetting.insecureString("setting.name");
65+
final var settings = Settings.builder().put(setting.getKey(), "value").build();
66+
67+
final var exception = Assert.assertThrows(IllegalArgumentException.class, () -> setting.get(settings));
68+
Assert.assertEquals(
69+
"Setting [setting.name] is insecure, but property [allow_insecure_settings] is not set",
70+
exception.getMessage()
71+
);
72+
}
73+
74+
public void testShouldLogWarn() {
75+
final var setting = SecureSetting.insecureString("setting.name", "setting.name_secure", true);
76+
final var settings = Settings.builder().put(setting.getKey(), "value").build();
77+
78+
Assert.assertEquals("value", setting.get(settings).toString());
79+
assertSettingWarning();
80+
}
81+
82+
public void testShouldLogWarnOnce() {
83+
final var setting = SecureSetting.insecureString("setting.name", "setting.name_secure", true);
84+
final var settings = Settings.builder().put(setting.getKey(), "value").build();
85+
86+
Assert.assertEquals("value", setting.get(settings).toString());
87+
Assert.assertEquals("value", setting.get(settings).toString());
88+
assertSettingWarning();
89+
90+
// check that warning was only logged once
91+
Assert.assertEquals(1, this.rootLogMsgs.stream().filter(msg -> msg.contains("but a secure variant")).count());
92+
93+
}
94+
95+
public void testShouldRaiseExceptionIfConfigured() {
96+
final var setting = SecureSetting.insecureString("setting.name", "setting.name_secure", false);
97+
final var settings = Settings.builder().put(setting.getKey(), "value").build();
98+
99+
final var exception = Assert.assertThrows(IllegalArgumentException.class, () -> setting.get(settings));
100+
Assert.assertEquals(
101+
"Setting [setting.name] is insecure, but property [allow_insecure_settings] is not set",
102+
exception.getMessage()
103+
);
104+
}
105+
106+
public void testShouldFallbackToInsecure() {
107+
final var insecureSetting = SecureSetting.insecureString("setting.name", "setting.name_secure", true);
108+
final var secureSetting = SecureSetting.secureString("setting.name_secure", insecureSetting);
109+
final var settings = Settings.builder().put(insecureSetting.getKey(), "value").build();
110+
111+
Assert.assertEquals("value", secureSetting.get(settings).toString());
112+
assertSettingWarning();
113+
}
114+
}

0 commit comments

Comments
 (0)