Skip to content

Commit 3c88e66

Browse files
committed
Add secure setting for watcher email password (#31620)
Other watcher actions already account for secure settings in their sensitive settings, whereas the email sending action did not. This adds the ability to optionally set a secure_password for email accounts.
1 parent 2ccd9c9 commit 3c88e66

File tree

3 files changed

+72
-8
lines changed

3 files changed

+72
-8
lines changed

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/Account.java

+35-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
import org.apache.logging.log4j.Logger;
99
import org.elasticsearch.SpecialPermission;
10+
import org.elasticsearch.common.settings.SecureSetting;
11+
import org.elasticsearch.common.settings.SecureString;
12+
import org.elasticsearch.common.settings.Setting;
1013
import org.elasticsearch.common.settings.Settings;
1114
import org.elasticsearch.common.settings.SettingsException;
1215
import org.elasticsearch.common.unit.TimeValue;
@@ -24,10 +27,13 @@
2427
import java.security.PrivilegedActionException;
2528
import java.security.PrivilegedExceptionAction;
2629
import java.util.Properties;
30+
import java.util.Set;
2731

2832
public class Account {
2933

3034
static final String SMTP_PROTOCOL = "smtp";
35+
private static final String SMTP_PASSWORD = "password";
36+
private static final Setting<SecureString> SECURE_PASSWORD_SETTING = SecureSetting.secureString("secure_" + SMTP_PASSWORD, null);
3137

3238
static {
3339
SecurityManager sm = System.getSecurityManager();
@@ -101,7 +107,7 @@ public Email send(Email email, Authentication auth, Profile profile) throws Mess
101107
if (auth != null && auth.password() != null) {
102108
password = new String(auth.password().text(cryptoService));
103109
} else if (config.smtp.password != null) {
104-
password = new String(config.smtp.password);
110+
password = new String(config.smtp.password.getChars());
105111
}
106112

107113
if (profile == null) {
@@ -199,18 +205,40 @@ static class Smtp {
199205
final String host;
200206
final int port;
201207
final String user;
202-
final char[] password;
208+
final SecureString password;
203209
final Properties properties;
204210

205211
Smtp(Settings settings) {
206212
host = settings.get("host", settings.get("localaddress", settings.get("local_address")));
213+
207214
port = settings.getAsInt("port", settings.getAsInt("localport", settings.getAsInt("local_port", 25)));
208215
user = settings.get("user", settings.get("from", null));
209-
String passStr = settings.get("password", null);
210-
password = passStr != null ? passStr.toCharArray() : null;
216+
password = getSecureSetting(SMTP_PASSWORD, settings, SECURE_PASSWORD_SETTING);
217+
//password = passStr != null ? passStr.toCharArray() : null;
211218
properties = loadSmtpProperties(settings);
212219
}
213220

221+
/**
222+
* Finds a setting, and then a secure setting if the setting is null, or returns null if one does not exist. This differs
223+
* from other getSetting calls in that it allows for null whereas the other methods throw an exception.
224+
*
225+
* Note: if your setting was not previously secure, than the string reference that is in the setting object is still
226+
* insecure. This is only constructing a new SecureString with the char[] of the insecure setting.
227+
*/
228+
private static SecureString getSecureSetting(String settingName, Settings settings, Setting<SecureString> secureSetting) {
229+
String value = settings.get(settingName);
230+
if (value == null) {
231+
SecureString secureString = secureSetting.get(settings);
232+
if (secureString != null && secureString.length() > 0) {
233+
return secureString;
234+
} else {
235+
return null;
236+
}
237+
} else {
238+
return new SecureString(value.toCharArray());
239+
}
240+
}
241+
214242
/**
215243
* loads the standard Java Mail properties as settings from the given account settings.
216244
* The standard settings are not that readable, therefore we enabled the user to configure
@@ -231,7 +259,9 @@ static Properties loadSmtpProperties(Settings settings) {
231259

232260
settings = builder.build();
233261
Properties props = new Properties();
234-
for (String key : settings.keySet()) {
262+
// Secure strings can not be retreived out of a settings object and should be handled differently
263+
Set<String> insecureSettings = settings.filter(s -> s.startsWith("secure_") == false).keySet();
264+
for (String key : insecureSettings) {
235265
props.setProperty(SMTP_SETTINGS_PREFIX + key, settings.get(key));
236266
}
237267
return props;

x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/EmailService.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import org.elasticsearch.common.Nullable;
99
import org.elasticsearch.common.settings.ClusterSettings;
10+
import org.elasticsearch.common.settings.SecureSetting;
11+
import org.elasticsearch.common.settings.SecureString;
1012
import org.elasticsearch.common.settings.Setting;
1113
import org.elasticsearch.common.settings.Setting.Property;
1214
import org.elasticsearch.common.settings.Settings;
@@ -63,6 +65,10 @@ public class EmailService extends NotificationService<Account> {
6365
Setting.affixKeySetting("xpack.notification.email.account.", "smtp.password",
6466
(key) -> Setting.simpleString(key, Property.Dynamic, Property.NodeScope, Property.Filtered));
6567

68+
private static final Setting.AffixSetting<SecureString> SETTING_SECURE_PASSWORD =
69+
Setting.affixKeySetting("xpack.notification.email.account.", "smtp.secure_password",
70+
(key) -> SecureSetting.secureString(key, null));
71+
6672
private static final Setting.AffixSetting<TimeValue> SETTING_SMTP_TIMEOUT =
6773
Setting.affixKeySetting("xpack.notification.email.account.", "smtp.timeout",
6874
(key) -> Setting.timeSetting(key, TimeValue.timeValueMinutes(2), Property.Dynamic, Property.NodeScope));
@@ -111,6 +117,7 @@ public EmailService(Settings settings, @Nullable CryptoService cryptoService, Cl
111117
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_PORT, (s, o) -> {}, (s, o) -> {});
112118
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_USER, (s, o) -> {}, (s, o) -> {});
113119
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_PASSWORD, (s, o) -> {}, (s, o) -> {});
120+
clusterSettings.addAffixUpdateConsumer(SETTING_SECURE_PASSWORD, (s, o) -> {}, (s, o) -> {});
114121
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_TIMEOUT, (s, o) -> {}, (s, o) -> {});
115122
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_CONNECTION_TIMEOUT, (s, o) -> {}, (s, o) -> {});
116123
clusterSettings.addAffixUpdateConsumer(SETTING_SMTP_WRITE_TIMEOUT, (s, o) -> {}, (s, o) -> {});
@@ -172,7 +179,8 @@ public static List<Setting<?>> getSettings() {
172179
return Arrays.asList(SETTING_DEFAULT_ACCOUNT, SETTING_PROFILE, SETTING_EMAIL_DEFAULTS, SETTING_SMTP_AUTH, SETTING_SMTP_HOST,
173180
SETTING_SMTP_PASSWORD, SETTING_SMTP_PORT, SETTING_SMTP_STARTTLS_ENABLE, SETTING_SMTP_USER, SETTING_SMTP_STARTTLS_REQUIRED,
174181
SETTING_SMTP_TIMEOUT, SETTING_SMTP_CONNECTION_TIMEOUT, SETTING_SMTP_WRITE_TIMEOUT, SETTING_SMTP_LOCAL_ADDRESS,
175-
SETTING_SMTP_LOCAL_PORT, SETTING_SMTP_SEND_PARTIAL, SETTING_SMTP_WAIT_ON_QUIT, SETTING_SMTP_SSL_TRUST_ADDRESS);
182+
SETTING_SMTP_LOCAL_PORT, SETTING_SMTP_SEND_PARTIAL, SETTING_SMTP_WAIT_ON_QUIT, SETTING_SMTP_SSL_TRUST_ADDRESS,
183+
SETTING_SECURE_PASSWORD);
176184
}
177185

178186
}

x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountTests.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package org.elasticsearch.xpack.watcher.notification.email;
77

8+
import org.elasticsearch.common.settings.MockSecureSettings;
89
import org.elasticsearch.common.settings.Settings;
910
import org.elasticsearch.common.unit.TimeValue;
1011
import org.elasticsearch.test.ESTestCase;
@@ -16,7 +17,6 @@
1617
import javax.mail.Address;
1718
import javax.mail.Message;
1819
import javax.mail.internet.InternetAddress;
19-
2020
import java.util.Properties;
2121
import java.util.concurrent.CountDownLatch;
2222
import java.util.concurrent.TimeUnit;
@@ -149,7 +149,7 @@ public void testConfig() throws Exception {
149149
assertThat(config.smtp.host, is(host));
150150
assertThat(config.smtp.user, is(user));
151151
if (password != null) {
152-
assertThat(config.smtp.password, is(password.toCharArray()));
152+
assertThat(config.smtp.password.getChars(), is(password.toCharArray()));
153153
} else {
154154
assertThat(config.smtp.password, nullValue());
155155
}
@@ -292,4 +292,30 @@ public void testAccountTimeoutsConfiguredAsNumberAreRejected() {
292292
.build()), null, logger);
293293
});
294294
}
295+
296+
public void testEnsurePasswordSetAsSecureSetting() {
297+
String password = "password";
298+
MockSecureSettings secureSettings = new MockSecureSettings();
299+
secureSettings.setString("smtp.secure_password", password);
300+
301+
Settings settings = Settings.builder()
302+
.put("smtp.host", "localhost")
303+
.put("smtp.port", server.port())
304+
.put("smtp.connection_timeout", TimeValue.timeValueMinutes(4))
305+
.setSecureSettings(secureSettings)
306+
.build();
307+
308+
Account.Config config = new Account.Config("default", settings);
309+
assertThat(config.smtp.password.getChars(), equalTo(password.toCharArray()));
310+
311+
settings = Settings.builder()
312+
.put("smtp.host", "localhost")
313+
.put("smtp.port", server.port())
314+
.put("smtp.connection_timeout", TimeValue.timeValueMinutes(4))
315+
.put("smtp.password", password)
316+
.build();
317+
318+
config = new Account.Config("default", settings);
319+
assertThat(config.smtp.password.getChars(), equalTo(password.toCharArray()));
320+
}
295321
}

0 commit comments

Comments
 (0)