Extract AWS Key from KeyChain instead of using potential null value

While I was working on #18703, I discovered a bad behavior when people don't provide AWS key/secret as part as their `elasticsearch.yml` but rely on SysProps or env. variables...

In [`InternalAwsS3Service#getClient(...)`](d4366f8493/plugins/repository-s3/src/main/java/org/elasticsearch/cloud/aws/InternalAwsS3Service.java (L76-L141)), we have:

```java
        Tuple<String, String> clientDescriptor = new Tuple<>(endpoint, account);
        AmazonS3Client client = clients.get(clientDescriptor);
```

But if people don't provide credentials, `account` is `null`.

Even if it actually could work, I think that we should use the `AWSCredentialsProvider` we create later on and extract from it the `account` (AWS KEY actually) and then use it as the second value of the tuple.

Closes #19557.
This commit is contained in:
David Pilato 2016-07-23 01:17:23 +02:00
parent fef0ffc49e
commit f8e0557be5
7 changed files with 34 additions and 32 deletions

View File

@ -24,6 +24,7 @@ import com.amazonaws.services.s3.AmazonS3;
import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import java.util.Locale; import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
@ -154,6 +155,6 @@ public interface AwsS3Service extends LifecycleComponent {
Setting<String> ENDPOINT_SETTING = Setting.simpleString("cloud.aws.s3.endpoint", Property.NodeScope); Setting<String> ENDPOINT_SETTING = Setting.simpleString("cloud.aws.s3.endpoint", Property.NodeScope);
} }
AmazonS3 client(String endpoint, Protocol protocol, String region, String account, String key, Integer maxRetries, AmazonS3 client(Settings repositorySettings, String endpoint, Protocol protocol, String region, Integer maxRetries,
boolean useThrottleRetries, Boolean pathStyleAccess); boolean useThrottleRetries, Boolean pathStyleAccess);
} }

View File

@ -35,10 +35,13 @@ import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.repositories.s3.S3Repository;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.repositories.s3.S3Repository.getValue;
/** /**
* *
*/ */
@ -54,17 +57,20 @@ public class InternalAwsS3Service extends AbstractLifecycleComponent implements
} }
@Override @Override
public synchronized AmazonS3 client(String endpoint, Protocol protocol, String region, String key, String secret, Integer maxRetries, public synchronized AmazonS3 client(Settings repositorySettings, String endpoint, Protocol protocol, String region, Integer maxRetries,
boolean useThrottleRetries, Boolean pathStyleAccess) { boolean useThrottleRetries, Boolean pathStyleAccess) {
String foundEndpoint = findEndpoint(logger, settings, endpoint, region); String foundEndpoint = findEndpoint(logger, settings, endpoint, region);
Tuple<String, String> clientDescriptor = new Tuple<>(foundEndpoint, key);
AWSCredentialsProvider credentials = buildCredentials(logger, settings, repositorySettings);
Tuple<String, String> clientDescriptor = new Tuple<>(foundEndpoint, credentials.getCredentials().getAWSAccessKeyId());
AmazonS3Client client = clients.get(clientDescriptor); AmazonS3Client client = clients.get(clientDescriptor);
if (client != null) { if (client != null) {
return client; return client;
} }
client = new AmazonS3Client( client = new AmazonS3Client(
buildCredentials(logger, key, secret), credentials,
buildConfiguration(logger, settings, protocol, maxRetries, foundEndpoint, useThrottleRetries)); buildConfiguration(logger, settings, protocol, maxRetries, foundEndpoint, useThrottleRetries));
if (pathStyleAccess != null) { if (pathStyleAccess != null) {
@ -116,8 +122,13 @@ public class InternalAwsS3Service extends AbstractLifecycleComponent implements
return clientConfiguration; return clientConfiguration;
} }
public static AWSCredentialsProvider buildCredentials(ESLogger logger, String key, String secret) { public static AWSCredentialsProvider buildCredentials(ESLogger logger, Settings settings, Settings repositorySettings) {
AWSCredentialsProvider credentials; AWSCredentialsProvider credentials;
String key = getValue(repositorySettings, settings,
S3Repository.Repository.KEY_SETTING, S3Repository.Repositories.KEY_SETTING);
String secret = getValue(repositorySettings, settings,
S3Repository.Repository.SECRET_SETTING, S3Repository.Repositories.SECRET_SETTING);
if (key.isEmpty() && secret.isEmpty()) { if (key.isEmpty() && secret.isEmpty()) {
logger.debug("Using either environment variables, system properties or instance profile credentials"); logger.debug("Using either environment variables, system properties or instance profile credentials");
credentials = new DefaultAWSCredentialsProviderChain(); credentials = new DefaultAWSCredentialsProviderChain();

View File

@ -301,11 +301,8 @@ public class S3Repository extends BlobStoreRepository {
bucket, region, endpoint, protocol, chunkSize, serverSideEncryption, bufferSize, maxRetries, useThrottleRetries, cannedACL, bucket, region, endpoint, protocol, chunkSize, serverSideEncryption, bufferSize, maxRetries, useThrottleRetries, cannedACL,
storageClass, pathStyleAccess); storageClass, pathStyleAccess);
String key = getValue(metadata.settings(), settings, Repository.KEY_SETTING, Repositories.KEY_SETTING);
String secret = getValue(metadata.settings(), settings, Repository.SECRET_SETTING, Repositories.SECRET_SETTING);
blobStore = new S3BlobStore(settings, blobStore = new S3BlobStore(settings,
s3Service.client(endpoint, protocol, region, key, secret, maxRetries, useThrottleRetries, pathStyleAccess), s3Service.client(metadata.settings(), endpoint, protocol, region, maxRetries, useThrottleRetries, pathStyleAccess),
bucket, region, serverSideEncryption, bufferSize, maxRetries, cannedACL, storageClass); bucket, region, serverSideEncryption, bufferSize, maxRetries, cannedACL, storageClass);
String basePath = getValue(metadata.settings(), settings, Repository.BASE_PATH_SETTING, Repositories.BASE_PATH_SETTING); String basePath = getValue(metadata.settings(), settings, Repository.BASE_PATH_SETTING, Repositories.BASE_PATH_SETTING);

View File

@ -35,7 +35,7 @@ import static org.hamcrest.Matchers.is;
public class AwsS3ServiceImplTests extends ESTestCase { public class AwsS3ServiceImplTests extends ESTestCase {
public void testAWSCredentialsWithSystemProviders() { public void testAWSCredentialsWithSystemProviders() {
AWSCredentialsProvider credentialsProvider = InternalAwsS3Service.buildCredentials(logger, "", ""); AWSCredentialsProvider credentialsProvider = InternalAwsS3Service.buildCredentials(logger, Settings.EMPTY, Settings.EMPTY);
assertThat(credentialsProvider, instanceOf(DefaultAWSCredentialsProviderChain.class)); assertThat(credentialsProvider, instanceOf(DefaultAWSCredentialsProviderChain.class));
} }
@ -136,12 +136,7 @@ public class AwsS3ServiceImplTests extends ESTestCase {
protected void launchAWSCredentialsWithElasticsearchSettingsTest(Settings singleRepositorySettings, Settings settings, protected void launchAWSCredentialsWithElasticsearchSettingsTest(Settings singleRepositorySettings, Settings settings,
String expectedKey, String expectedSecret) { String expectedKey, String expectedSecret) {
String key = S3Repository.getValue(singleRepositorySettings, settings, AWSCredentials credentials = InternalAwsS3Service.buildCredentials(logger, settings, singleRepositorySettings).getCredentials();
S3Repository.Repository.KEY_SETTING, S3Repository.Repositories.KEY_SETTING);
String secret = S3Repository.getValue(singleRepositorySettings, settings,
S3Repository.Repository.SECRET_SETTING, S3Repository.Repositories.SECRET_SETTING);
AWSCredentials credentials = InternalAwsS3Service.buildCredentials(logger, key, secret).getCredentials();
assertThat(credentials.getAWSAccessKeyId(), is(expectedKey)); assertThat(credentials.getAWSAccessKeyId(), is(expectedKey));
assertThat(credentials.getAWSSecretKey(), is(expectedSecret)); assertThat(credentials.getAWSSecretKey(), is(expectedSecret));
} }

View File

@ -21,10 +21,8 @@ package org.elasticsearch.cloud.aws;
import com.amazonaws.Protocol; import com.amazonaws.Protocol;
import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugin.repository.s3.S3RepositoryPlugin; import org.elasticsearch.plugin.repository.s3.S3RepositoryPlugin;
import org.elasticsearch.plugins.Plugin;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
@ -44,9 +42,9 @@ public class TestAwsS3Service extends InternalAwsS3Service {
@Override @Override
public synchronized AmazonS3 client(String endpoint, Protocol protocol, String region, String account, String key, Integer maxRetries, public synchronized AmazonS3 client(Settings repositorySettings, String endpoint, Protocol protocol, String region, Integer maxRetries,
boolean useThrottleRetries, Boolean pathStyleAccess) { boolean useThrottleRetries, Boolean pathStyleAccess) {
return cachedWrapper(super.client(endpoint, protocol, region, account, key, maxRetries, useThrottleRetries, pathStyleAccess)); return cachedWrapper(super.client(repositorySettings, endpoint, protocol, region, maxRetries, useThrottleRetries, pathStyleAccess));
} }
private AmazonS3 cachedWrapper(AmazonS3 client) { private AmazonS3 cachedWrapper(AmazonS3 client) {

View File

@ -161,12 +161,15 @@ public abstract class AbstractS3SnapshotRestoreTest extends AbstractAwsTestCase
public void testEncryption() { public void testEncryption() {
Client client = client(); Client client = client();
logger.info("--> creating s3 repository with bucket[{}] and path [{}]", internalCluster().getInstance(Settings.class).get("repositories.s3.bucket"), basePath); logger.info("--> creating s3 repository with bucket[{}] and path [{}]", internalCluster().getInstance(Settings.class).get("repositories.s3.bucket"), basePath);
PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo")
.setType("s3").setSettings(Settings.builder() Settings repositorySettings = Settings.builder()
.put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath) .put(S3Repository.Repository.BASE_PATH_SETTING.getKey(), basePath)
.put(S3Repository.Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000)) .put(S3Repository.Repository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(1000, 10000))
.put(S3Repository.Repository.SERVER_SIDE_ENCRYPTION_SETTING.getKey(), true) .put(S3Repository.Repository.SERVER_SIDE_ENCRYPTION_SETTING.getKey(), true)
).get(); .build();
PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo")
.setType("s3").setSettings(repositorySettings).get();
assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true));
createIndex("test-idx-1", "test-idx-2", "test-idx-3"); createIndex("test-idx-1", "test-idx-2", "test-idx-3");
@ -193,11 +196,10 @@ public abstract class AbstractS3SnapshotRestoreTest extends AbstractAwsTestCase
Settings settings = internalCluster().getInstance(Settings.class); Settings settings = internalCluster().getInstance(Settings.class);
Settings bucket = settings.getByPrefix("repositories.s3."); Settings bucket = settings.getByPrefix("repositories.s3.");
AmazonS3 s3Client = internalCluster().getInstance(AwsS3Service.class).client( AmazonS3 s3Client = internalCluster().getInstance(AwsS3Service.class).client(
repositorySettings,
null, null,
S3Repository.Repositories.PROTOCOL_SETTING.get(settings), S3Repository.Repositories.PROTOCOL_SETTING.get(settings),
S3Repository.Repositories.REGION_SETTING.get(settings), S3Repository.Repositories.REGION_SETTING.get(settings),
S3Repository.Repositories.KEY_SETTING.get(settings),
S3Repository.Repositories.SECRET_SETTING.get(settings),
null, randomBoolean(), null); null, randomBoolean(), null);
String bucketName = bucket.get("bucket"); String bucketName = bucket.get("bucket");
@ -466,15 +468,13 @@ public abstract class AbstractS3SnapshotRestoreTest extends AbstractAwsTestCase
String endpoint = bucket.get("endpoint", S3Repository.Repositories.ENDPOINT_SETTING.get(settings)); String endpoint = bucket.get("endpoint", S3Repository.Repositories.ENDPOINT_SETTING.get(settings));
Protocol protocol = S3Repository.Repositories.PROTOCOL_SETTING.get(settings); Protocol protocol = S3Repository.Repositories.PROTOCOL_SETTING.get(settings);
String region = bucket.get("region", S3Repository.Repositories.REGION_SETTING.get(settings)); String region = bucket.get("region", S3Repository.Repositories.REGION_SETTING.get(settings));
String accessKey = bucket.get("access_key", S3Repository.Repositories.KEY_SETTING.get(settings));
String secretKey = bucket.get("secret_key", S3Repository.Repositories.SECRET_SETTING.get(settings));
String bucketName = bucket.get("bucket"); String bucketName = bucket.get("bucket");
// We check that settings has been set in elasticsearch.yml integration test file // We check that settings has been set in elasticsearch.yml integration test file
// as described in README // as described in README
assertThat("Your settings in elasticsearch.yml are incorrects. Check README file.", bucketName, notNullValue()); assertThat("Your settings in elasticsearch.yml are incorrects. Check README file.", bucketName, notNullValue());
AmazonS3 client = internalCluster().getInstance(AwsS3Service.class).client(endpoint, protocol, region, accessKey, secretKey, AmazonS3 client = internalCluster().getInstance(AwsS3Service.class).client(Settings.EMPTY, endpoint, protocol, region, null,
null, randomBoolean(), null); randomBoolean(), null);
try { try {
ObjectListing prevListing = null; ObjectListing prevListing = null;
//From http://docs.amazonwebservices.com/AmazonS3/latest/dev/DeletingMultipleObjectsUsingJava.html //From http://docs.amazonwebservices.com/AmazonS3/latest/dev/DeletingMultipleObjectsUsingJava.html

View File

@ -58,7 +58,7 @@ public class S3RepositoryTests extends ESTestCase {
@Override @Override
protected void doClose() {} protected void doClose() {}
@Override @Override
public AmazonS3 client(String endpoint, Protocol protocol, String region, String account, String key, Integer maxRetries, public AmazonS3 client(Settings repositorySettings, String endpoint, Protocol protocol, String region, Integer maxRetries,
boolean useThrottleRetries, Boolean pathStyleAccess) { boolean useThrottleRetries, Boolean pathStyleAccess) {
return new DummyS3Client(); return new DummyS3Client();
} }