Skip to content

Commit 725abd0

Browse files
committed
Settings: Migrate ec2 discovery sensitive settings to elasticsearch keystore (#23961)
This change adds secure settings for access/secret keys and proxy username/password to ec2 discovery. It adds the new settings with the prefix `discovery.ec2`, copies other relevant ec2 client settings to the same prefix, and deprecates all other settings (`cloud.aws.*` and `cloud.aws.ec2.*`). Note that this is simpler than the client configs in repository-s3 because discovery is only initialized once for the entire node, so there is no reason to complicate the configuration with the ability to have multiple sets of client settings. relates #22475
1 parent e32c16e commit 725abd0

File tree

5 files changed

+216
-55
lines changed

5 files changed

+216
-55
lines changed

plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Service.java

+68-29
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import com.amazonaws.ClientConfiguration;
2323
import com.amazonaws.Protocol;
2424
import com.amazonaws.services.ec2.AmazonEC2;
25+
import org.elasticsearch.common.settings.SecureSetting;
26+
import org.elasticsearch.common.settings.SecureString;
2527
import org.elasticsearch.common.settings.Setting;
2628
import org.elasticsearch.common.settings.Setting.Property;
2729
import org.elasticsearch.common.settings.Settings;
@@ -42,50 +44,52 @@ interface AwsEc2Service {
4244
/**
4345
* cloud.aws.access_key: AWS Access key. Shared with repository-s3 plugin
4446
*/
45-
Setting<String> KEY_SETTING =
46-
Setting.simpleString("cloud.aws.access_key", Property.NodeScope, Property.Filtered, Property.Shared);
47+
Setting<SecureString> KEY_SETTING = new Setting<>("cloud.aws.access_key", "", SecureString::new,
48+
Property.NodeScope, Property.Filtered, Property.Shared, Property.Deprecated);
4749
/**
4850
* cloud.aws.secret_key: AWS Secret key. Shared with repository-s3 plugin
4951
*/
50-
Setting<String> SECRET_SETTING =
51-
Setting.simpleString("cloud.aws.secret_key", Property.NodeScope, Property.Filtered, Property.Shared);
52+
Setting<SecureString> SECRET_SETTING = new Setting<>("cloud.aws.secret_key", "", SecureString::new,
53+
Property.NodeScope, Property.Filtered, Property.Shared, Property.Deprecated);
5254
/**
5355
* cloud.aws.protocol: Protocol for AWS API: http or https. Defaults to https. Shared with repository-s3 plugin
5456
*/
5557
Setting<Protocol> PROTOCOL_SETTING = new Setting<>("cloud.aws.protocol", "https", s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)),
56-
Property.NodeScope, Property.Shared);
58+
Property.NodeScope, Property.Shared, Property.Deprecated);
5759
/**
5860
* cloud.aws.proxy.host: In case of proxy, define its hostname/IP. Shared with repository-s3 plugin
5961
*/
60-
Setting<String> PROXY_HOST_SETTING = Setting.simpleString("cloud.aws.proxy.host", Property.NodeScope, Property.Shared);
62+
Setting<String> PROXY_HOST_SETTING = Setting.simpleString("cloud.aws.proxy.host",
63+
Property.NodeScope, Property.Shared, Property.Deprecated);
6164
/**
6265
* cloud.aws.proxy.port: In case of proxy, define its port. Defaults to 80. Shared with repository-s3 plugin
6366
*/
64-
Setting<Integer> PROXY_PORT_SETTING = Setting.intSetting("cloud.aws.proxy.port", 80, 0, 1<<16, Property.NodeScope,
65-
Property.Shared);
67+
Setting<Integer> PROXY_PORT_SETTING = Setting.intSetting("cloud.aws.proxy.port", 80, 0, 1<<16,
68+
Property.NodeScope, Property.Shared, Property.Deprecated);
6669
/**
6770
* cloud.aws.proxy.username: In case of proxy with auth, define the username. Shared with repository-s3 plugin
6871
*/
69-
Setting<String> PROXY_USERNAME_SETTING = Setting.simpleString("cloud.aws.proxy.username", Property.NodeScope, Property.Shared);
72+
Setting<SecureString> PROXY_USERNAME_SETTING = new Setting<>("cloud.aws.proxy.username", "", SecureString::new,
73+
Property.NodeScope, Property.Filtered, Property.Shared, Property.Deprecated);
7074
/**
7175
* cloud.aws.proxy.password: In case of proxy with auth, define the password. Shared with repository-s3 plugin
7276
*/
73-
Setting<String> PROXY_PASSWORD_SETTING =
74-
Setting.simpleString("cloud.aws.proxy.password", Property.NodeScope, Property.Filtered, Property.Shared);
77+
Setting<SecureString> PROXY_PASSWORD_SETTING = new Setting<>("cloud.aws.proxy.password", "", SecureString::new,
78+
Property.NodeScope, Property.Filtered, Property.Shared, Property.Deprecated);
7579
/**
7680
* cloud.aws.signer: If you are using an old AWS API version, you can define a Signer. Shared with repository-s3 plugin
7781
*/
78-
Setting<String> SIGNER_SETTING = Setting.simpleString("cloud.aws.signer", Property.NodeScope, Property.Shared);
82+
Setting<String> SIGNER_SETTING = Setting.simpleString("cloud.aws.signer", Property.NodeScope, Property.Shared, Property.Deprecated);
7983
/**
8084
* cloud.aws.region: Region. Shared with repository-s3 plugin
8185
*/
8286
Setting<String> REGION_SETTING =
83-
new Setting<>("cloud.aws.region", "", s -> s.toLowerCase(Locale.ROOT), Property.NodeScope, Property.Shared);
87+
new Setting<>("cloud.aws.region", "", s -> s.toLowerCase(Locale.ROOT), Property.NodeScope, Property.Shared, Property.Deprecated);
8488
/**
8589
* cloud.aws.read_timeout: Socket read timeout. Shared with repository-s3 plugin
8690
*/
8791
Setting<TimeValue> READ_TIMEOUT = Setting.timeSetting("cloud.aws.read_timeout",
88-
TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), Property.NodeScope, Property.Shared);
92+
TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), Property.NodeScope, Property.Shared, Property.Deprecated);
8993

9094
/**
9195
* Defines specific ec2 settings starting with cloud.aws.ec2.
@@ -95,69 +99,70 @@ interface CLOUD_EC2 {
9599
* cloud.aws.ec2.access_key: AWS Access key specific for EC2 API calls. Defaults to cloud.aws.access_key.
96100
* @see AwsEc2Service#KEY_SETTING
97101
*/
98-
Setting<String> KEY_SETTING = new Setting<>("cloud.aws.ec2.access_key", AwsEc2Service.KEY_SETTING, Function.identity(),
99-
Property.NodeScope, Property.Filtered);
102+
Setting<SecureString> KEY_SETTING = new Setting<>("cloud.aws.ec2.access_key", AwsEc2Service.KEY_SETTING,
103+
SecureString::new, Property.NodeScope, Property.Filtered, Property.Deprecated);
104+
100105
/**
101106
* cloud.aws.ec2.secret_key: AWS Secret key specific for EC2 API calls. Defaults to cloud.aws.secret_key.
102107
* @see AwsEc2Service#SECRET_SETTING
103108
*/
104-
Setting<String> SECRET_SETTING = new Setting<>("cloud.aws.ec2.secret_key", AwsEc2Service.SECRET_SETTING, Function.identity(),
105-
Property.NodeScope, Property.Filtered);
109+
Setting<SecureString> SECRET_SETTING = new Setting<>("cloud.aws.ec2.secret_key", AwsEc2Service.SECRET_SETTING,
110+
SecureString::new, Property.NodeScope, Property.Filtered, Property.Deprecated);
106111
/**
107112
* cloud.aws.ec2.protocol: Protocol for AWS API specific for EC2 API calls: http or https. Defaults to cloud.aws.protocol.
108113
* @see AwsEc2Service#PROTOCOL_SETTING
109114
*/
110115
Setting<Protocol> PROTOCOL_SETTING = new Setting<>("cloud.aws.ec2.protocol", AwsEc2Service.PROTOCOL_SETTING,
111-
s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope);
116+
s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope, Property.Deprecated);
112117
/**
113118
* cloud.aws.ec2.proxy.host: In case of proxy, define its hostname/IP specific for EC2 API calls. Defaults to cloud.aws.proxy.host.
114119
* @see AwsEc2Service#PROXY_HOST_SETTING
115120
*/
116121
Setting<String> PROXY_HOST_SETTING = new Setting<>("cloud.aws.ec2.proxy.host", AwsEc2Service.PROXY_HOST_SETTING,
117-
Function.identity(), Property.NodeScope);
122+
Function.identity(), Property.NodeScope, Property.Deprecated);
118123
/**
119124
* cloud.aws.ec2.proxy.port: In case of proxy, define its port specific for EC2 API calls. Defaults to cloud.aws.proxy.port.
120125
* @see AwsEc2Service#PROXY_PORT_SETTING
121126
*/
122127
Setting<Integer> PROXY_PORT_SETTING = new Setting<>("cloud.aws.ec2.proxy.port", AwsEc2Service.PROXY_PORT_SETTING,
123-
s -> Setting.parseInt(s, 0, 1<<16, "cloud.aws.ec2.proxy.port"), Property.NodeScope);
128+
s -> Setting.parseInt(s, 0, 1<<16, "cloud.aws.ec2.proxy.port"), Property.NodeScope, Property.Deprecated);
124129
/**
125130
* cloud.aws.ec2.proxy.username: In case of proxy with auth, define the username specific for EC2 API calls.
126131
* Defaults to cloud.aws.proxy.username.
127132
* @see AwsEc2Service#PROXY_USERNAME_SETTING
128133
*/
129-
Setting<String> PROXY_USERNAME_SETTING = new Setting<>("cloud.aws.ec2.proxy.username", AwsEc2Service.PROXY_USERNAME_SETTING,
130-
Function.identity(), Property.NodeScope);
134+
Setting<SecureString> PROXY_USERNAME_SETTING = new Setting<>("cloud.aws.ec2.proxy.username", AwsEc2Service.PROXY_USERNAME_SETTING,
135+
SecureString::new, Property.NodeScope, Property.Filtered, Property.Deprecated);
131136
/**
132137
* cloud.aws.ec2.proxy.password: In case of proxy with auth, define the password specific for EC2 API calls.
133138
* Defaults to cloud.aws.proxy.password.
134139
* @see AwsEc2Service#PROXY_PASSWORD_SETTING
135140
*/
136-
Setting<String> PROXY_PASSWORD_SETTING = new Setting<>("cloud.aws.ec2.proxy.password", AwsEc2Service.PROXY_PASSWORD_SETTING,
137-
Function.identity(), Property.NodeScope, Property.Filtered);
141+
Setting<SecureString> PROXY_PASSWORD_SETTING = new Setting<>("cloud.aws.ec2.proxy.password", AwsEc2Service.PROXY_PASSWORD_SETTING,
142+
SecureString::new, Property.NodeScope, Property.Filtered, Property.Deprecated);
138143
/**
139144
* cloud.aws.ec2.signer: If you are using an old AWS API version, you can define a Signer. Specific for EC2 API calls.
140145
* Defaults to cloud.aws.signer.
141146
* @see AwsEc2Service#SIGNER_SETTING
142147
*/
143148
Setting<String> SIGNER_SETTING = new Setting<>("cloud.aws.ec2.signer", AwsEc2Service.SIGNER_SETTING, Function.identity(),
144-
Property.NodeScope);
149+
Property.NodeScope, Property.Deprecated);
145150
/**
146151
* cloud.aws.ec2.region: Region specific for EC2 API calls. Defaults to cloud.aws.region.
147152
* @see AwsEc2Service#REGION_SETTING
148153
*/
149154
Setting<String> REGION_SETTING = new Setting<>("cloud.aws.ec2.region", AwsEc2Service.REGION_SETTING,
150-
s -> s.toLowerCase(Locale.ROOT), Property.NodeScope);
155+
s -> s.toLowerCase(Locale.ROOT), Property.NodeScope, Property.Deprecated);
151156
/**
152157
* cloud.aws.ec2.endpoint: Endpoint. If not set, endpoint will be guessed based on region setting.
153158
*/
154-
Setting<String> ENDPOINT_SETTING = Setting.simpleString("cloud.aws.ec2.endpoint", Property.NodeScope);
159+
Setting<String> ENDPOINT_SETTING = Setting.simpleString("cloud.aws.ec2.endpoint", Property.NodeScope, Property.Deprecated);
155160
/**
156161
* cloud.aws.ec2.read_timeout: Socket read timeout. Defaults to cloud.aws.read_timeout
157162
* @see AwsEc2Service#READ_TIMEOUT
158163
*/
159164
Setting<TimeValue> READ_TIMEOUT =
160-
Setting.timeSetting("cloud.aws.ec2.read_timeout", AwsEc2Service.READ_TIMEOUT, Property.NodeScope);
165+
Setting.timeSetting("cloud.aws.ec2.read_timeout", AwsEc2Service.READ_TIMEOUT, Property.NodeScope, Property.Deprecated);
161166
}
162167

163168
/**
@@ -172,6 +177,40 @@ class HostType {
172177
public static final String TAG_PREFIX = "tag:";
173178
}
174179

180+
/** The access key (ie login id) for connecting to ec2. */
181+
Setting<SecureString> ACCESS_KEY_SETTING = SecureSetting.secureString("discovery.ec2.access_key", CLOUD_EC2.KEY_SETTING, false);
182+
183+
/** The secret key (ie password) for connecting to ec2. */
184+
Setting<SecureString> SECRET_KEY_SETTING = SecureSetting.secureString("discovery.ec2.secret_key", CLOUD_EC2.SECRET_SETTING, false);
185+
186+
/** An override for the ec2 endpoint to connect to. */
187+
Setting<String> ENDPOINT_SETTING = new Setting<>("discovery.ec2.endpoint", CLOUD_EC2.ENDPOINT_SETTING,
188+
s -> s.toLowerCase(Locale.ROOT), Setting.Property.NodeScope);
189+
190+
/** The protocol to use to connect to to ec2. */
191+
Setting<Protocol> PROTOCOL_SETTING = new Setting<>("discovery.ec2.protocol", CLOUD_EC2.PROTOCOL_SETTING,
192+
s -> Protocol.valueOf(s.toUpperCase(Locale.ROOT)), Setting.Property.NodeScope);
193+
194+
/** The host name of a proxy to connect to ec2 through. */
195+
Setting<String> PROXY_HOST_SETTING = new Setting<>("discovery.ec2.proxy.host", CLOUD_EC2.PROXY_HOST_SETTING,
196+
Function.identity(), Setting.Property.NodeScope);
197+
198+
/** The port of a proxy to connect to ec2 through. */
199+
Setting<Integer> PROXY_PORT_SETTING = Setting.intSetting("discovery.ec2.proxy.port", CLOUD_EC2.PROXY_PORT_SETTING,
200+
0, Setting.Property.NodeScope);
201+
202+
/** The username of a proxy to connect to s3 through. */
203+
Setting<SecureString> PROXY_USERNAME_SETTING = SecureSetting.secureString("discovery.ec2.proxy.username",
204+
CLOUD_EC2.PROXY_USERNAME_SETTING, false);
205+
206+
/** The password of a proxy to connect to s3 through. */
207+
Setting<SecureString> PROXY_PASSWORD_SETTING = SecureSetting.secureString("discovery.ec2.proxy.password",
208+
CLOUD_EC2.PROXY_PASSWORD_SETTING, false);
209+
210+
/** The socket timeout for connecting to s3. */
211+
Setting<TimeValue> READ_TIMEOUT_SETTING = Setting.timeSetting("discovery.ec2.read_timeout",
212+
CLOUD_EC2.READ_TIMEOUT, Setting.Property.NodeScope);
213+
175214
/**
176215
* discovery.ec2.host_type: The type of host type to use to communicate with other instances.
177216
* Can be one of private_ip, public_ip, private_dns, public_dns or tag:XXXX where

plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImpl.java

+27-24
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.elasticsearch.common.Randomness;
3939
import org.elasticsearch.common.Strings;
4040
import org.elasticsearch.common.component.AbstractComponent;
41+
import org.elasticsearch.common.settings.SecureString;
4142
import org.elasticsearch.common.settings.Settings;
4243

4344
class AwsEc2ServiceImpl extends AbstractComponent implements AwsEc2Service, Closeable {
@@ -68,14 +69,15 @@ public synchronized AmazonEC2 client() {
6869
protected static AWSCredentialsProvider buildCredentials(Logger logger, Settings settings) {
6970
AWSCredentialsProvider credentials;
7071

71-
String key = CLOUD_EC2.KEY_SETTING.get(settings);
72-
String secret = CLOUD_EC2.SECRET_SETTING.get(settings);
73-
if (key.isEmpty() && secret.isEmpty()) {
74-
logger.debug("Using either environment variables, system properties or instance profile credentials");
75-
credentials = new DefaultAWSCredentialsProviderChain();
76-
} else {
77-
logger.debug("Using basic key/secret credentials");
78-
credentials = new StaticCredentialsProvider(new BasicAWSCredentials(key, secret));
72+
try (SecureString key = DISCOVERY_EC2.ACCESS_KEY_SETTING.get(settings);
73+
SecureString secret = DISCOVERY_EC2.SECRET_KEY_SETTING.get(settings)) {
74+
if (key.length() == 0 && secret.length() == 0) {
75+
logger.debug("Using either environment variables, system properties or instance profile credentials");
76+
credentials = new DefaultAWSCredentialsProviderChain();
77+
} else {
78+
logger.debug("Using basic key/secret credentials");
79+
credentials = new StaticCredentialsProvider(new BasicAWSCredentials(key.toString(), secret.toString()));
80+
}
7981
}
8082

8183
return credentials;
@@ -86,19 +88,20 @@ protected static ClientConfiguration buildConfiguration(Logger logger, Settings
8688
// the response metadata cache is only there for diagnostics purposes,
8789
// but can force objects from every response to the old generation.
8890
clientConfiguration.setResponseMetadataCacheSize(0);
89-
clientConfiguration.setProtocol(CLOUD_EC2.PROTOCOL_SETTING.get(settings));
90-
91-
if (PROXY_HOST_SETTING.exists(settings) || CLOUD_EC2.PROXY_HOST_SETTING.exists(settings)) {
92-
String proxyHost = CLOUD_EC2.PROXY_HOST_SETTING.get(settings);
93-
Integer proxyPort = CLOUD_EC2.PROXY_PORT_SETTING.get(settings);
94-
String proxyUsername = CLOUD_EC2.PROXY_USERNAME_SETTING.get(settings);
95-
String proxyPassword = CLOUD_EC2.PROXY_PASSWORD_SETTING.get(settings);
96-
97-
clientConfiguration
98-
.withProxyHost(proxyHost)
99-
.withProxyPort(proxyPort)
100-
.withProxyUsername(proxyUsername)
101-
.withProxyPassword(proxyPassword);
91+
clientConfiguration.setProtocol(DISCOVERY_EC2.PROTOCOL_SETTING.get(settings));
92+
93+
if (PROXY_HOST_SETTING.exists(settings) || DISCOVERY_EC2.PROXY_HOST_SETTING.exists(settings)) {
94+
String proxyHost = DISCOVERY_EC2.PROXY_HOST_SETTING.get(settings);
95+
Integer proxyPort = DISCOVERY_EC2.PROXY_PORT_SETTING.get(settings);
96+
try (SecureString proxyUsername = DISCOVERY_EC2.PROXY_USERNAME_SETTING.get(settings);
97+
SecureString proxyPassword = DISCOVERY_EC2.PROXY_PASSWORD_SETTING.get(settings)) {
98+
99+
clientConfiguration
100+
.withProxyHost(proxyHost)
101+
.withProxyPort(proxyPort)
102+
.withProxyUsername(proxyUsername.toString())
103+
.withProxyPassword(proxyPassword.toString());
104+
}
102105
}
103106

104107
// #155: we might have 3rd party users using older EC2 API version
@@ -125,15 +128,15 @@ public long delayBeforeNextRetry(AmazonWebServiceRequest originalRequest,
125128
10,
126129
false);
127130
clientConfiguration.setRetryPolicy(retryPolicy);
128-
clientConfiguration.setSocketTimeout((int) CLOUD_EC2.READ_TIMEOUT.get(settings).millis());
131+
clientConfiguration.setSocketTimeout((int) DISCOVERY_EC2.READ_TIMEOUT_SETTING.get(settings).millis());
129132

130133
return clientConfiguration;
131134
}
132135

133136
protected static String findEndpoint(Logger logger, Settings settings) {
134137
String endpoint = null;
135-
if (CLOUD_EC2.ENDPOINT_SETTING.exists(settings)) {
136-
endpoint = CLOUD_EC2.ENDPOINT_SETTING.get(settings);
138+
if (DISCOVERY_EC2.ENDPOINT_SETTING.exists(settings) || CLOUD_EC2.ENDPOINT_SETTING.exists(settings)) {
139+
endpoint = DISCOVERY_EC2.ENDPOINT_SETTING.get(settings);
137140
logger.debug("using explicit ec2 endpoint [{}]", endpoint);
138141
} else if (REGION_SETTING.exists(settings) || CLOUD_EC2.REGION_SETTING.exists(settings)) {
139142
final String region = CLOUD_EC2.REGION_SETTING.get(settings);

0 commit comments

Comments
 (0)