Polishing.
Move RegionProvider to AwsIamAuthenticationOptions. Update documentation. Original pull request: gh-693. See gh-253
This commit is contained in:
@@ -19,27 +19,26 @@ import java.io.ByteArrayInputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.List;
|
||||
|
||||
import software.amazon.awssdk.http.SdkHttpFullRequest;
|
||||
import software.amazon.awssdk.auth.signer.Aws4Signer;
|
||||
import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
||||
import software.amazon.awssdk.http.SdkHttpMethod;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
||||
import software.amazon.awssdk.auth.signer.Aws4Signer;
|
||||
import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
|
||||
import software.amazon.awssdk.http.SdkHttpFullRequest;
|
||||
import software.amazon.awssdk.http.SdkHttpMethod;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
@@ -53,17 +52,19 @@ import org.springframework.web.client.RestOperations;
|
||||
* AWS IAM authentication creates a {@link Aws4Signer signed} HTTP request that is
|
||||
* executed by Vault to get the identity of the signer using AWS STS
|
||||
* {@literal GetCallerIdentity}. A signature requires
|
||||
* {@link com.amazonaws.auth.AwsCredentials} to calculate the signature.
|
||||
* {@link software.amazon.awssdk.auth.credentials.AwsCredentials} to calculate the
|
||||
* signature.
|
||||
* <p>
|
||||
* This authentication requires AWS' Java SDK to sign request parameters and calculate the
|
||||
* signature key. Using an appropriate {@link com.amazonaws.auth.AwsCredentialsProvider}
|
||||
* allows authentication within AWS-EC2 instances with an assigned profile, within ECS and
|
||||
* Lambda instances.
|
||||
* signature key. Using an appropriate
|
||||
* {@link software.amazon.awssdk.auth.credentials.AwsCredentialsProvider} allows
|
||||
* authentication within AWS-EC2 instances with an assigned profile, within ECS and Lambda
|
||||
* instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.1
|
||||
* @see AwsIamAuthenticationOptions
|
||||
* @see com.amazonaws.auth.AwsCredentialsProvider
|
||||
* @see software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
|
||||
* @see RestOperations
|
||||
* @see <a href="https://www.vaultproject.io/docs/auth/aws.html">Auth Backend: aws
|
||||
* (IAM)</a>
|
||||
@@ -115,14 +116,15 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
|
||||
Assert.notNull(options, "AwsIamAuthenticationOptions must not be null");
|
||||
|
||||
AwsCredentials credentials = options.getCredentialsProvider().resolveCredentials();
|
||||
Region region = options.getRegionProvider().getRegion();
|
||||
|
||||
return createAuthenticationSteps(options, credentials);
|
||||
return createAuthenticationSteps(options, credentials, region);
|
||||
}
|
||||
|
||||
protected static AuthenticationSteps createAuthenticationSteps(AwsIamAuthenticationOptions options,
|
||||
AwsCredentials credentials) {
|
||||
AwsCredentials credentials, Region region) {
|
||||
|
||||
return AuthenticationSteps.fromSupplier(() -> createRequestBody(options, credentials)) //
|
||||
return AuthenticationSteps.fromSupplier(() -> createRequestBody(options, credentials, region)) //
|
||||
.login(AuthenticationUtil.getLoginPath(options.getPath()));
|
||||
}
|
||||
|
||||
@@ -133,7 +135,8 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
|
||||
|
||||
@Override
|
||||
public AuthenticationSteps getAuthenticationSteps() {
|
||||
return createAuthenticationSteps(this.options, this.options.getCredentialsProvider().resolveCredentials());
|
||||
return createAuthenticationSteps(this.options, this.options.getCredentialsProvider().resolveCredentials(),
|
||||
this.options.getRegionProvider().getRegion());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -174,17 +177,20 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
|
||||
* @return the map containing body key-value pairs.
|
||||
*/
|
||||
protected static Map<String, String> createRequestBody(AwsIamAuthenticationOptions options) {
|
||||
return createRequestBody(options, options.getCredentialsProvider().resolveCredentials());
|
||||
return createRequestBody(options, options.getCredentialsProvider().resolveCredentials(),
|
||||
options.getRegionProvider().getRegion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the request body to perform a Vault login using the AWS-IAM authentication
|
||||
* method.
|
||||
* @param options must not be {@literal null}.
|
||||
* @param credentials must not be {@literal null}.
|
||||
* @param region must not be {@literal null}.
|
||||
* @return the map containing body key-value pairs.
|
||||
*/
|
||||
private static Map<String, String> createRequestBody(AwsIamAuthenticationOptions options,
|
||||
AwsCredentials credentials) {
|
||||
AwsCredentials credentials, Region region) {
|
||||
|
||||
Map<String, String> login = new HashMap<>();
|
||||
|
||||
@@ -192,17 +198,18 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
|
||||
login.put("iam_request_url", Base64Utils.encodeToString(options.getEndpointUri().toString().getBytes()));
|
||||
login.put("iam_request_body", REQUEST_BODY_BASE64_ENCODED);
|
||||
|
||||
String headerJson = getSignedHeaders(options, credentials);
|
||||
String headerJson = getSignedHeaders(options, credentials, region);
|
||||
|
||||
login.put("iam_request_headers", Base64Utils.encodeToString(headerJson.getBytes()));
|
||||
|
||||
if (!StringUtils.isEmpty(options.getRole())) {
|
||||
if (!ObjectUtils.isEmpty(options.getRole())) {
|
||||
login.put("role", options.getRole());
|
||||
}
|
||||
return login;
|
||||
}
|
||||
|
||||
private static String getSignedHeaders(AwsIamAuthenticationOptions options, AwsCredentials credentials) {
|
||||
private static String getSignedHeaders(AwsIamAuthenticationOptions options, AwsCredentials credentials,
|
||||
Region region) {
|
||||
|
||||
Map<String, List<String>> headers = createIamRequestHeaders(options);
|
||||
|
||||
@@ -211,7 +218,6 @@ public class AwsIamAuthentication implements ClientAuthentication, Authenticatio
|
||||
.method(SdkHttpMethod.POST).uri(options.getEndpointUri());
|
||||
SdkHttpFullRequest request = builder.build();
|
||||
|
||||
Region region = DefaultAwsRegionProviderChain.builder().build().getRegion();
|
||||
Aws4Signer signer = Aws4Signer.create();
|
||||
Aws4SignerParams signerParams = Aws4SignerParams.builder().awsCredentials(credentials).signingName("sts")
|
||||
.signingRegion(region).build();
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.net.URI;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.providers.AwsRegionProvider;
|
||||
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -51,6 +53,11 @@ public class AwsIamAuthenticationOptions {
|
||||
*/
|
||||
private final AwsCredentialsProvider credentialsProvider;
|
||||
|
||||
/**
|
||||
* Region provider.
|
||||
*/
|
||||
private final AwsRegionProvider regionProvider;
|
||||
|
||||
/**
|
||||
* Name of the role against which the login is being attempted. If role is not
|
||||
* specified, the friendly name (i.e., role name or username) of the IAM principal
|
||||
@@ -71,11 +78,12 @@ public class AwsIamAuthenticationOptions {
|
||||
*/
|
||||
private final URI endpointUri;
|
||||
|
||||
private AwsIamAuthenticationOptions(String path, AwsCredentialsProvider credentialsProvider, @Nullable String role,
|
||||
@Nullable String serverId, URI endpointUri) {
|
||||
private AwsIamAuthenticationOptions(String path, AwsCredentialsProvider credentialsProvider,
|
||||
AwsRegionProvider regionProvider, @Nullable String role, @Nullable String serverId, URI endpointUri) {
|
||||
|
||||
this.path = path;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.regionProvider = regionProvider;
|
||||
this.role = role;
|
||||
this.serverId = serverId;
|
||||
this.endpointUri = endpointUri;
|
||||
@@ -102,6 +110,15 @@ public class AwsIamAuthenticationOptions {
|
||||
return this.credentialsProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the region provider to obtain the AWS region to be used for computing the
|
||||
* signature.
|
||||
* @since 3.0
|
||||
*/
|
||||
public AwsRegionProvider getRegionProvider() {
|
||||
return this.regionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the role, may be {@literal null} if none.
|
||||
*/
|
||||
@@ -149,6 +166,8 @@ public class AwsIamAuthenticationOptions {
|
||||
@Nullable
|
||||
private AwsCredentialsProvider credentialsProvider;
|
||||
|
||||
private AwsRegionProvider regionProvider = DefaultAwsRegionProviderChain.builder().build();
|
||||
|
||||
@Nullable
|
||||
private String role;
|
||||
|
||||
@@ -204,6 +223,21 @@ public class AwsIamAuthenticationOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link AwsRegionProvider}, required to calculate the region to be
|
||||
* used for computing the signature.
|
||||
* @param regionProvider must not be {@literal null}.
|
||||
* @return {@code this} {@link AwsIamAuthenticationOptionsBuilder}.
|
||||
* @since 3.0
|
||||
*/
|
||||
public AwsIamAuthenticationOptionsBuilder regionProvider(AwsRegionProvider regionProvider) {
|
||||
|
||||
Assert.notNull(regionProvider, "AwsRegionProvider must not be null");
|
||||
|
||||
this.regionProvider = regionProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the name of the role against which the login is being attempted. If
|
||||
* role is not specified, the friendly name (i.e., role name or username) of the
|
||||
@@ -267,8 +301,8 @@ public class AwsIamAuthenticationOptions {
|
||||
|
||||
Assert.state(this.credentialsProvider != null, "Credentials or CredentialProvider must not be null");
|
||||
|
||||
return new AwsIamAuthenticationOptions(this.path, this.credentialsProvider, this.role, this.serverId,
|
||||
this.endpointUri);
|
||||
return new AwsIamAuthenticationOptions(this.path, this.credentialsProvider, this.regionProvider, this.role,
|
||||
this.serverId, this.endpointUri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ package org.springframework.vault.authentication;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -29,11 +29,9 @@ import org.springframework.vault.client.VaultClients;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AwsIamAuthentication}.
|
||||
@@ -68,7 +66,7 @@ class AwsIamAuthenticationUnitTests {
|
||||
+ "}"));
|
||||
|
||||
AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder().role("foo-role")
|
||||
.credentials(AwsBasicCredentials.create("foo", "bar")).build();
|
||||
.regionProvider(() -> Region.US_WEST_1).credentials(AwsBasicCredentials.create("foo", "bar")).build();
|
||||
AwsIamAuthentication sut = new AwsIamAuthentication(options, this.restTemplate);
|
||||
|
||||
VaultToken login = sut.login();
|
||||
@@ -91,7 +89,7 @@ class AwsIamAuthenticationUnitTests {
|
||||
+ "}"));
|
||||
|
||||
AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder().role("foo-role")
|
||||
.credentials(AwsBasicCredentials.create("foo", "bar")).build();
|
||||
.regionProvider(() -> Region.US_WEST_1).credentials(AwsBasicCredentials.create("foo", "bar")).build();
|
||||
|
||||
AuthenticationSteps steps = AwsIamAuthentication.createAuthenticationSteps(options);
|
||||
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, this.restTemplate);
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<netty.version>4.1.72.Final</netty.version>
|
||||
<okhttp3.version>3.14.9</okhttp3.version>
|
||||
<jackson-databind.version>2.13.3</jackson-databind.version>
|
||||
<aws-java-sdk.version>2.17.152</aws-java-sdk.version>
|
||||
<aws-java-sdk.version>2.17.195</aws-java-sdk.version>
|
||||
<google-api-services-iam.version>v1-rev20210226-1.31.0</google-api-services-iam.version>
|
||||
<google-cloud-iamcredentials.version>1.2.2</google-cloud-iamcredentials.version>
|
||||
<google-auth-library-oauth2-http.version>0.22.2</google-auth-library-oauth2-http.version>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
[[new-features]]
|
||||
== New & Noteworthy
|
||||
|
||||
[[new-features.2-4-0]]
|
||||
[[new-features.3-0-0]]
|
||||
=== What's new in Spring Vault 3.0
|
||||
|
||||
* Upgrade to Spring Framework 6 and Java 17 baseline
|
||||
* Upgrade to AWS SDK 2.
|
||||
|
||||
=== What's new in Spring Vault 2.4
|
||||
|
||||
* Support for <<vault.authentication.userpass,Username/Password authentication>> for Username/Password, LDAP, Okta, and RADIUS authentication.
|
||||
|
||||
Reference in New Issue
Block a user