Skip to content

Commit

Permalink
ECS Task IAM profile credentials ignored in repository-s3 plugin (#31864
Browse files Browse the repository at this point in the history
)

ECS Task IAM profile credentials ignored in repository-s3 plugin (#31864)

Closes #26913
  • Loading branch information
vladimirdolzhenko authored Jul 19, 2018
1 parent f6d7854 commit 7c0fc20
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class ClusterConfiguration {
// there are cases when value depends on task that is not executed yet on configuration stage
Map<String, Object> systemProperties = new HashMap<>()

Map<String, Object> environmentVariables = new HashMap<>()

Map<String, Object> settings = new HashMap<>()

Map<String, String> keystoreSettings = new HashMap<>()
Expand All @@ -164,6 +166,11 @@ class ClusterConfiguration {
systemProperties.put(property, value)
}

@Input
void environment(String variable, Object value) {
environmentVariables.put(variable, value)
}

@Input
void setting(String name, Object value) {
settings.put(name, value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class NodeInfo {

args.addAll("-E", "node.portsfile=true")
env = [:]
env.putAll(config.environmentVariables)
for (Map.Entry<String, String> property : System.properties.entrySet()) {
if (property.key.startsWith('tests.es.')) {
args.add("-E")
Expand Down
4 changes: 2 additions & 2 deletions docs/plugins/repository-s3.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ include::install_remove.asciidoc[]
==== Getting started with AWS

The plugin provides a repository type named `s3` which may be used when creating a repository.
The repository defaults to using
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html[IAM Role]
The repository defaults to using https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html[ECS IAM Role] or
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html[EC2 IAM Role]
credentials for authentication. The only mandatory setting is the bucket name:

[source,js]
Expand Down
50 changes: 45 additions & 5 deletions plugins/repository-s3/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,15 @@ String s3TemporaryBasePath = System.getenv("amazon_s3_base_path_temporary")
String s3EC2Bucket = System.getenv("amazon_s3_bucket_ec2")
String s3EC2BasePath = System.getenv("amazon_s3_base_path_ec2")

String s3ECSBucket = System.getenv("amazon_s3_bucket_ecs")
String s3ECSBasePath = System.getenv("amazon_s3_base_path_ecs")

// If all these variables are missing then we are testing against the internal fixture instead, which has the following
// credentials hard-coded in.

if (!s3PermanentAccessKey && !s3PermanentSecretKey && !s3PermanentBucket && !s3PermanentBasePath
&& !s3EC2Bucket && !s3EC2BasePath) {
&& !s3EC2Bucket && !s3EC2BasePath
&& !s3ECSBucket && !s3ECSBasePath) {
s3PermanentAccessKey = 's3_integration_test_permanent_access_key'
s3PermanentSecretKey = 's3_integration_test_permanent_secret_key'
s3PermanentBucket = 'permanent-bucket-test'
Expand All @@ -105,10 +109,14 @@ if (!s3PermanentAccessKey && !s3PermanentSecretKey && !s3PermanentBucket && !s3P
s3EC2Bucket = 'ec2-bucket-test'
s3EC2BasePath = 'integration_test'

s3ECSBucket = 'ecs-bucket-test'
s3ECSBasePath = 'integration_test'

useFixture = true

} else if (!s3PermanentAccessKey || !s3PermanentSecretKey || !s3PermanentBucket || !s3PermanentBasePath
|| !s3EC2Bucket || !s3EC2BasePath) {
|| !s3EC2Bucket || !s3EC2BasePath
|| !s3ECSBucket || !s3ECSBasePath) {
throw new IllegalArgumentException("not all options specified to run against external S3 service")
}

Expand Down Expand Up @@ -284,7 +292,8 @@ if (useFixture && minioDistribution) {
// Minio only supports a single access key, see https://github.com/minio/minio/pull/5968
integTestMinioRunner.systemProperty 'tests.rest.blacklist', [
'repository_s3/30_repository_temporary_credentials/*',
'repository_s3/40_repository_ec2_credentials/*'
'repository_s3/40_repository_ec2_credentials/*',
'repository_s3/50_repository_ecs_credentials/*'
].join(",")

project.check.dependsOn(integTestMinio)
Expand All @@ -302,7 +311,8 @@ task s3FixtureProperties {
"s3Fixture.temporary_bucket_name" : s3TemporaryBucket,
"s3Fixture.temporary_key" : s3TemporaryAccessKey,
"s3Fixture.temporary_session_token": s3TemporarySessionToken,
"s3Fixture.ec2_bucket_name" : s3EC2Bucket
"s3Fixture.ec2_bucket_name" : s3EC2Bucket,
"s3Fixture.ecs_bucket_name" : s3ECSBucket
]

doLast {
Expand All @@ -327,7 +337,9 @@ Map<String, Object> expansions = [
'temporary_bucket': s3TemporaryBucket,
'temporary_base_path': s3TemporaryBasePath,
'ec2_bucket': s3EC2Bucket,
'ec2_base_path': s3EC2BasePath
'ec2_base_path': s3EC2BasePath,
'ecs_bucket': s3ECSBucket,
'ecs_base_path': s3ECSBasePath
]

processTestResources {
Expand Down Expand Up @@ -364,6 +376,34 @@ integTestCluster {
}
}

integTestRunner.systemProperty 'tests.rest.blacklist', 'repository_s3/50_repository_ecs_credentials/*'

///
RestIntegTestTask integTestECS = project.tasks.create('integTestECS', RestIntegTestTask.class) {
description = "Runs tests using the ECS repository."
}

// The following closure must execute before the afterEvaluate block in the constructor of the following integrationTest tasks:
project.afterEvaluate {
ClusterConfiguration cluster = project.extensions.getByName('integTestECSCluster') as ClusterConfiguration
cluster.dependsOn(project.s3Fixture)

cluster.setting 's3.client.integration_test_ecs.endpoint', "http://${-> s3Fixture.addressAndPort}"

Task integTestECSTask = project.tasks.getByName('integTestECS')
integTestECSTask.clusterConfig.plugin(project.path)
integTestECSTask.clusterConfig.environment 'AWS_CONTAINER_CREDENTIALS_FULL_URI',
"http://${-> s3Fixture.addressAndPort}/ecs_credentials_endpoint"
integTestECSRunner.systemProperty 'tests.rest.blacklist', [
'repository_s3/10_basic/*',
'repository_s3/20_repository_permanent_credentials/*',
'repository_s3/30_repository_temporary_credentials/*',
'repository_s3/40_repository_ec2_credentials/*'
].join(",")
}
project.check.dependsOn(integTestECS)
///

thirdPartyAudit.excludes = [
// classes are missing
'javax.servlet.ServletContextEvent',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper;
import com.amazonaws.http.IdleConnectionReaper;
import com.amazonaws.internal.StaticCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
Expand Down Expand Up @@ -156,10 +156,11 @@ protected synchronized void releaseCachedClients() {
}

static class PrivilegedInstanceProfileCredentialsProvider implements AWSCredentialsProvider {
private final InstanceProfileCredentialsProvider credentials;
private final AWSCredentialsProvider credentials;

private PrivilegedInstanceProfileCredentialsProvider() {
this.credentials = new InstanceProfileCredentialsProvider();
// InstanceProfileCredentialsProvider as last item of chain
this.credentials = new EC2ContainerCredentialsProviderWrapper();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ private AmazonS3Fixture(final String workingDir, Properties properties) {
final Bucket ec2Bucket = new Bucket("s3Fixture.ec2",
randomAsciiAlphanumOfLength(random, 10), randomAsciiAlphanumOfLength(random, 10));

this.handlers = defaultHandlers(buckets, ec2Bucket);
final Bucket ecsBucket = new Bucket("s3Fixture.ecs",
randomAsciiAlphanumOfLength(random, 10), randomAsciiAlphanumOfLength(random, 10));

this.handlers = defaultHandlers(buckets, ec2Bucket, ecsBucket);
}

private static String nonAuthPath(Request request) {
Expand Down Expand Up @@ -174,7 +177,7 @@ public static void main(final String[] args) throws Exception {
}

/** Builds the default request handlers **/
private PathTrie<RequestHandler> defaultHandlers(final Map<String, Bucket> buckets, final Bucket ec2Bucket) {
private PathTrie<RequestHandler> defaultHandlers(final Map<String, Bucket> buckets, final Bucket ec2Bucket, final Bucket ecsBucket) {
final PathTrie<RequestHandler> handlers = new PathTrie<>(RestUtils.REST_DECODER);

// HEAD Object
Expand Down Expand Up @@ -400,11 +403,18 @@ private PathTrie<RequestHandler> defaultHandlers(final Map<String, Bucket> bucke
handlers.insert(nonAuthPath(HttpGet.METHOD_NAME, "/latest/meta-data/iam/security-credentials/{profileName}"), (request) -> {
final String profileName = request.getParam("profileName");
if (EC2_PROFILE.equals(profileName) == false) {
return new Response(RestStatus.NOT_FOUND.getStatus(), new HashMap<>(), "unknown credentials".getBytes(UTF_8));
return new Response(RestStatus.NOT_FOUND.getStatus(), new HashMap<>(), "unknown profile".getBytes(UTF_8));
}
return credentialResponseFunction.apply(profileName, ec2Bucket.key, ec2Bucket.token);
});

// GET
//
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
handlers.insert(nonAuthPath(HttpGet.METHOD_NAME, "/ecs_credentials_endpoint"),
(request) -> credentialResponseFunction.apply("CPV_ECS", ecsBucket.key, ecsBucket.token));


return handlers;
}

Expand Down
Loading

0 comments on commit 7c0fc20

Please sign in to comment.