From af060ac44fdf3fda135094cf4d08c7d2b722176f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 6 Jun 2024 09:58:28 +0200 Subject: [PATCH] Default to IMDSv2 for AWS-EC2 metadata retrieval. See gh-865 --- .../AwsEc2AuthenticationOptions.java | 6 +- .../AwsEc2AuthenticationUnitTests.java | 63 +++++++++++++------ 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java index 8839acbb..ef635dab 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java @@ -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. + *

+ * 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() { } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java index 578e2981..49b02558 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java @@ -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 getEc2Login() { - return Collections.singletonMap("pkcs7", "value"); - } - }.login()); + assertThatExceptionOfType(VaultException.class) + .isThrownBy(() -> new AwsEc2Authentication(options, this.restTemplate, this.restTemplate) { + @Override + protected Map getEc2Login() { + return Collections.singletonMap("pkcs7", "value"); + } + }.login()); } }