Default to IMDSv2 for AWS-EC2 metadata retrieval.

See gh-865
This commit is contained in:
Mark Paluch
2024-06-06 09:58:28 +02:00
parent dac9e36c37
commit af060ac44f
2 changed files with 47 additions and 22 deletions

View File

@@ -29,6 +29,8 @@ import org.springframework.util.Assert;
* Authentication options provide the path, the Identity Document URI and an optional
* role. {@link AwsEc2AuthenticationOptions} can be constructed using {@link #builder()}.
* Instances of this class are immutable once constructed.
* <p>
* Metadata retrieval defaults to IMDSv2 (session-token).
*
* @author Mark Paluch
* @see AwsEc2Authentication
@@ -94,7 +96,7 @@ public class AwsEc2AuthenticationOptions {
private AwsEc2AuthenticationOptions() {
this(DEFAULT_AWS_AUTHENTICATION_PATH, DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI, "", Nonce.generated(),
Duration.ofMinutes(1), DEFAULT_IMDSV2_TOKEN_URI, InstanceMetadataServiceVersion.V1);
Duration.ofMinutes(1), DEFAULT_IMDSV2_TOKEN_URI, InstanceMetadataServiceVersion.V2);
}
private AwsEc2AuthenticationOptions(String path, URI identityDocumentUri, @Nullable String role, Nonce nonce,
@@ -187,7 +189,7 @@ public class AwsEc2AuthenticationOptions {
private URI metadataTokenRequestUri = DEFAULT_IMDSV2_TOKEN_URI;
private InstanceMetadataServiceVersion version = InstanceMetadataServiceVersion.V1;
private InstanceMetadataServiceVersion version = InstanceMetadataServiceVersion.V2;
AwsEc2AuthenticationOptionsBuilder() {
}

View File

@@ -36,6 +36,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder;
import org.springframework.vault.VaultException;
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.InstanceMetadataServiceVersion;
import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.Nonce;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.support.VaultToken;
@@ -69,23 +70,23 @@ class AwsEc2AuthenticationUnitTests {
}
@Test
void shouldObtainIdentityDocument() {
void shouldObtainIdentityDocumentV1() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
.andRespond(withSuccess().body("Hello, world"));
AwsEc2Authentication authentication = new AwsEc2Authentication(this.restTemplate);
AwsEc2Authentication authentication = new AwsEc2Authentication(options, this.restTemplate, this.restTemplate);
assertThat(authentication.getEc2Login()).containsEntry("pkcs7", "Hello, world").containsKey("nonce").hasSize(2);
}
@Test
void shouldObtainIdentityDocumentV2() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(AwsEc2AuthenticationOptions.InstanceMetadataServiceVersion.V2)
.build();
void shouldObtainIdentityDocument() {
this.mockRest.expect(requestTo("http://169.254.169.254/latest/api/token")) //
.andExpect(method(HttpMethod.PUT)) //
@@ -97,7 +98,7 @@ class AwsEc2AuthenticationUnitTests {
.andExpect(header("X-aws-ec2-metadata-token", "my-token")) //
.andRespond(withSuccess().body("Hello, world"));
AwsEc2Authentication authentication = new AwsEc2Authentication(options, this.restTemplate, this.restTemplate);
AwsEc2Authentication authentication = new AwsEc2Authentication(this.restTemplate);
assertThat(authentication.getEc2Login()).containsEntry("pkcs7", "Hello, world").containsKey("nonce").hasSize(2);
}
@@ -106,7 +107,7 @@ class AwsEc2AuthenticationUnitTests {
void shouldObtainIdentityDocumentWithImperativeAuthenticationStepsV2() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(AwsEc2AuthenticationOptions.InstanceMetadataServiceVersion.V2)
.version(InstanceMetadataServiceVersion.V2)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/api/token")) //
@@ -134,11 +135,15 @@ class AwsEc2AuthenticationUnitTests {
@Test
void shouldCleanUpIdentityResponse() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
.andRespond(withSuccess().body("Hello, \r\r\n\nworld"));
AwsEc2Authentication authentication = new AwsEc2Authentication(this.restTemplate);
AwsEc2Authentication authentication = new AwsEc2Authentication(options, this.restTemplate, this.restTemplate);
assertThat(authentication.getEc2Login()).containsEntry("pkcs7", "Hello, world");
}
@@ -146,7 +151,10 @@ class AwsEc2AuthenticationUnitTests {
@Test
void shouldContainRole() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder().role("ami").build();
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.role("ami")
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
@@ -166,7 +174,10 @@ class AwsEc2AuthenticationUnitTests {
Nonce nonce = Nonce.provided("foo".toCharArray());
AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions.builder().nonce(nonce).build();
AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions.builder()
.nonce(nonce)
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
@@ -195,7 +206,10 @@ class AwsEc2AuthenticationUnitTests {
Nonce nonce = Nonce.provided("foo".toCharArray());
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder().nonce(nonce).build();
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.nonce(nonce)
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
@@ -221,25 +235,34 @@ class AwsEc2AuthenticationUnitTests {
@Test
void loginShouldFailWhileObtainingIdentityDocument() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andRespond(withServerError());
assertThatExceptionOfType(VaultException.class)
.isThrownBy(() -> new AwsEc2Authentication(this.restTemplate).login());
.isThrownBy(() -> new AwsEc2Authentication(options, this.restTemplate, this.restTemplate).login());
}
@Test
void loginShouldFail() {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder()
.version(InstanceMetadataServiceVersion.V1)
.build();
this.mockRest.expect(requestTo("/auth/aws-ec2/login")) //
.andRespond(withServerError());
assertThatExceptionOfType(VaultException.class).isThrownBy(() -> new AwsEc2Authentication(this.restTemplate) {
@Override
protected Map<String, String> getEc2Login() {
return Collections.singletonMap("pkcs7", "value");
}
}.login());
assertThatExceptionOfType(VaultException.class)
.isThrownBy(() -> new AwsEc2Authentication(options, this.restTemplate, this.restTemplate) {
@Override
protected Map<String, String> getEc2Login() {
return Collections.singletonMap("pkcs7", "value");
}
}.login());
}
}