Skip to content

Commit 9915555

Browse files
committed
Validate that localhost resolves to loopback address
1 parent 83871d0 commit 9915555

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "Amazon Elastic Container Service (ECS)",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "HTTP(S) credential provider requires the implementation to verify that the resolved addresses for the host are actually loopback addresses."
6+
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515

1616
package software.amazon.awssdk.auth.credentials;
1717

18-
import static java.util.Collections.unmodifiableSet;
19-
2018
import java.io.IOException;
19+
import java.net.Inet6Address;
20+
import java.net.InetAddress;
2121
import java.net.URI;
22+
import java.net.UnknownHostException;
2223
import java.time.Instant;
2324
import java.time.temporal.ChronoUnit;
2425
import java.util.Arrays;
2526
import java.util.HashMap;
26-
import java.util.HashSet;
2727
import java.util.Map;
28-
import java.util.Set;
28+
import java.util.Objects;
29+
import java.util.function.Predicate;
2930
import software.amazon.awssdk.annotations.SdkPublicApi;
3031
import software.amazon.awssdk.auth.credentials.internal.ContainerCredentialsRetryPolicy;
3132
import software.amazon.awssdk.auth.credentials.internal.HttpCredentialsLoader;
@@ -65,7 +66,9 @@
6566
public final class ContainerCredentialsProvider
6667
implements HttpCredentialsProvider,
6768
ToCopyableBuilder<ContainerCredentialsProvider.Builder, ContainerCredentialsProvider> {
68-
private static final Set<String> ALLOWED_HOSTS = unmodifiableSet(new HashSet<>(Arrays.asList("localhost", "127.0.0.1")));
69+
private static final Predicate<InetAddress> ALLOWED_HOSTS_IPv4_RULES = InetAddress::isLoopbackAddress;
70+
private static final Predicate<InetAddress> ALLOWED_HOSTS_IPv6_RULES = InetAddress::isLoopbackAddress;
71+
private static final String HTTPS = "https";
6972

7073
private final String endpoint;
7174
private final HttpCredentialsLoader httpCredentialsLoader;
@@ -207,18 +210,44 @@ private URI createUri(String relativeUri) {
207210

208211
private URI createGenericContainerUrl() {
209212
URI uri = URI.create(SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI.getStringValueOrThrow());
210-
if (!ALLOWED_HOSTS.contains(uri.getHost())) {
213+
if (!isHttps(uri) && !isAllowedHost(uri.getHost())) {
211214
String envVarName = SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable();
212215
throw SdkClientException.builder()
213-
.message(String.format("The full URI (%s) contained within environment " +
214-
"variable %s has an invalid host. Host can only be one of [%s].",
215-
uri,
216-
envVarName,
217-
String.join(",", ALLOWED_HOSTS)))
216+
.message(String.format("The full URI (%s) contained within environment variable " +
217+
"%s has an invalid host. Host should resolve to a loopback" +
218+
" address.",
219+
uri, envVarName))
218220
.build();
219221
}
220222
return uri;
221223
}
224+
225+
private boolean isHttps(URI endpoint) {
226+
return Objects.equals(HTTPS, endpoint.getScheme());
227+
}
228+
229+
private boolean isAllowedHost(String host) {
230+
try {
231+
InetAddress[] addresses = InetAddress.getAllByName(host);
232+
233+
return addresses.length > 0 && Arrays.stream(addresses)
234+
.allMatch(this::matchesAllowedHostRules);
235+
236+
} catch (UnknownHostException e) {
237+
throw SdkClientException.builder()
238+
.cause(e)
239+
.message(String.format("host (%s) could not be resolved to an IP address.", host))
240+
.build();
241+
}
242+
}
243+
244+
private boolean matchesAllowedHostRules(InetAddress inetAddress) {
245+
if (inetAddress instanceof Inet6Address) {
246+
return ALLOWED_HOSTS_IPv6_RULES.test(inetAddress);
247+
}
248+
249+
return ALLOWED_HOSTS_IPv4_RULES.test(inetAddress);
250+
}
222251
}
223252

224253
/**

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsEndpointProviderTest.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,49 @@ public void theLoopbackAddressIsAlsoAcceptable() throws IOException {
7171
assertThat(sut.endpoint().toString(), equalTo(fullUri));
7272
}
7373

74+
@Test
75+
public void theLoopbackIpv6AddressIsAlsoAcceptable() throws IOException {
76+
String fullUri = "http://[::1]:9851/endpoint";
77+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), fullUri);
78+
79+
assertThat(sut.endpoint().toString(), equalTo(fullUri));
80+
}
81+
82+
@Test
83+
public void anyHttpsAddressIsAlsoAcceptable() throws IOException {
84+
String fullUri = "https://192.168.10.120:9851/endpoint";
85+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), fullUri);
86+
87+
assertThat(sut.endpoint().toString(), equalTo(fullUri));
88+
}
89+
90+
@Test
91+
public void anyHttpsIpv6AddressIsAlsoAcceptable() throws IOException {
92+
String fullUri = "https://[::FFFF:152.16.24.123]/endpoint";
93+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), fullUri);
94+
95+
assertThat(sut.endpoint().toString(), equalTo(fullUri));
96+
}
97+
98+
@Test(expected = SdkClientException.class)
99+
public void nonLoopbackAddressIsNotAcceptable() throws IOException {
100+
String fullUri = "http://192.168.10.120:9851/endpoint";
101+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), fullUri);
102+
103+
assertThat(sut.endpoint().toString(), equalTo(fullUri));
104+
}
105+
106+
@Test(expected = SdkClientException.class)
107+
public void nonLoopbackIpv6AddressIsNotAcceptable() throws IOException {
108+
String fullUri = "http://[::FFFF:152.16.24.123]/endpoint";
109+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), fullUri);
110+
111+
assertThat(sut.endpoint().toString(), equalTo(fullUri));
112+
}
113+
74114
@Test(expected = SdkClientException.class)
75115
public void onlyLocalHostAddressesAreValid() throws IOException {
76-
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), "https://google.com/endpoint");
116+
helper.set(AWS_CONTAINER_CREDENTIALS_FULL_URI.environmentVariable(), "http://google.com/endpoint");
77117
sut.endpoint();
78118
}
79119

0 commit comments

Comments
 (0)