Skip to content

Commit

Permalink
fix: AwsCredentials should not call metadata server if security creds…
Browse files Browse the repository at this point in the history
… and region are retrievable through environment vars (#1100)

* fix: AwsCredentials should not call metadata server if security creds and region are retrievable through environment vars

* fix: make constants for environment variables

* fix: more tests

* fix: add missing region in test
  • Loading branch information
lsirac committed Nov 18, 2022
1 parent 5e5fe41 commit 1ff5772
Show file tree
Hide file tree
Showing 2 changed files with 298 additions and 27 deletions.
77 changes: 60 additions & 17 deletions oauth2_http/java/com/google/auth/oauth2/AwsCredentials.java
Expand Up @@ -41,6 +41,7 @@
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonParser;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
Expand All @@ -62,6 +63,13 @@
*/
public class AwsCredentials extends ExternalAccountCredentials {

// Supported environment variables.
static final String AWS_REGION = "AWS_REGION";
static final String AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION";
static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID";
static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY";
static final String AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN";

static final String AWS_IMDSV2_SESSION_TOKEN_HEADER = "x-aws-ec2-metadata-token";
static final String AWS_IMDSV2_SESSION_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds";
static final String AWS_IMDSV2_SESSION_TOKEN_TTL = "300";
Expand Down Expand Up @@ -181,7 +189,10 @@ public AccessToken refreshAccessToken() throws IOException {

@Override
public String retrieveSubjectToken() throws IOException {
Map<String, Object> metadataRequestHeaders = createMetadataRequestHeaders(awsCredentialSource);
Map<String, Object> metadataRequestHeaders = new HashMap<>();
if (shouldUseMetadataServer()) {
metadataRequestHeaders = createMetadataRequestHeaders(awsCredentialSource);
}

// The targeted region is required to generate the signed request. The regional
// endpoint must also be used.
Expand Down Expand Up @@ -266,6 +277,39 @@ private String buildSubjectToken(AwsRequestSignature signature)
return URLEncoder.encode(token.toString(), "UTF-8");
}

private boolean canRetrieveRegionFromEnvironment() {
// The AWS region can be provided through AWS_REGION or AWS_DEFAULT_REGION. Only one is
// required.
List<String> keys = ImmutableList.of(AWS_REGION, AWS_DEFAULT_REGION);
for (String env : keys) {
String value = getEnvironmentProvider().getEnv(env);
if (value != null && value.trim().length() > 0) {
// Region available.
return true;
}
}
return false;
}

private boolean canRetrieveSecurityCredentialsFromEnvironment() {
// Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available.
List<String> keys = ImmutableList.of(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);
for (String env : keys) {
String value = getEnvironmentProvider().getEnv(env);
if (value == null || value.trim().length() == 0) {
// Return false if one of them are missing.
return false;
}
}
return true;
}

@VisibleForTesting
boolean shouldUseMetadataServer() {
return !canRetrieveRegionFromEnvironment() || !canRetrieveSecurityCredentialsFromEnvironment();
}

@VisibleForTesting
Map<String, Object> createMetadataRequestHeaders(AwsCredentialSource awsCredentialSource)
throws IOException {
Map<String, Object> metadataRequestHeaders = new HashMap<>();
Expand Down Expand Up @@ -302,15 +346,14 @@ Map<String, Object> createMetadataRequestHeaders(AwsCredentialSource awsCredenti

@VisibleForTesting
String getAwsRegion(Map<String, Object> metadataRequestHeaders) throws IOException {
// For AWS Lambda, the region is retrieved through the AWS_REGION environment variable.
String region = getEnvironmentProvider().getEnv("AWS_REGION");
if (region != null) {
return region;
}

String defaultRegion = getEnvironmentProvider().getEnv("AWS_DEFAULT_REGION");
if (defaultRegion != null) {
return defaultRegion;
String region;
if (canRetrieveRegionFromEnvironment()) {
// For AWS Lambda, the region is retrieved through the AWS_REGION environment variable.
region = getEnvironmentProvider().getEnv(AWS_REGION);
if (region != null && region.trim().length() > 0) {
return region;
}
return getEnvironmentProvider().getEnv(AWS_DEFAULT_REGION);
}

if (awsCredentialSource.regionUrl == null || awsCredentialSource.regionUrl.isEmpty()) {
Expand All @@ -329,10 +372,10 @@ String getAwsRegion(Map<String, Object> metadataRequestHeaders) throws IOExcepti
AwsSecurityCredentials getAwsSecurityCredentials(Map<String, Object> metadataRequestHeaders)
throws IOException {
// Check environment variables for credentials first.
String accessKeyId = getEnvironmentProvider().getEnv("AWS_ACCESS_KEY_ID");
String secretAccessKey = getEnvironmentProvider().getEnv("AWS_SECRET_ACCESS_KEY");
String token = getEnvironmentProvider().getEnv("AWS_SESSION_TOKEN");
if (accessKeyId != null && secretAccessKey != null) {
if (canRetrieveSecurityCredentialsFromEnvironment()) {
String accessKeyId = getEnvironmentProvider().getEnv(AWS_ACCESS_KEY_ID);
String secretAccessKey = getEnvironmentProvider().getEnv(AWS_SECRET_ACCESS_KEY);
String token = getEnvironmentProvider().getEnv(AWS_SESSION_TOKEN);
return new AwsSecurityCredentials(accessKeyId, secretAccessKey, token);
}

Expand All @@ -355,9 +398,9 @@ AwsSecurityCredentials getAwsSecurityCredentials(Map<String, Object> metadataReq
JsonParser parser = OAuth2Utils.JSON_FACTORY.createJsonParser(awsCredentials);
GenericJson genericJson = parser.parseAndClose(GenericJson.class);

accessKeyId = (String) genericJson.get("AccessKeyId");
secretAccessKey = (String) genericJson.get("SecretAccessKey");
token = (String) genericJson.get("Token");
String accessKeyId = (String) genericJson.get("AccessKeyId");
String secretAccessKey = (String) genericJson.get("SecretAccessKey");
String token = (String) genericJson.get("Token");

// These credentials last for a few hours - we may consider caching these in the
// future.
Expand Down

0 comments on commit 1ff5772

Please sign in to comment.