Skip to content

Commit c1b0777

Browse files
committed
fix!: support IPv6 address as custem GCE_METADATA_HOST
The url for metadata server would fail if user provided IPv6 address as GCE_METADATA_HOST env. For backwards compatibility new code accepts the following: * domain, i.e. `mymetadataserver.domain.com` * domain with port, i.e. `mymetadataserver.domain.com:8080` * IPv4 address, i.e. `127.0.0.1` * IPv4 address with port, i.e. `127.0.0.1:8080` * IPv6 address, i.e. `::1` * IPv6 address within square brackers, i.e. `[::1]` * IPv6 address with port, i.e. `[::1]:8080` BREAKING CHANGE: As the new code performs URL validation, it will fallback to the `DEFAULT_METADATA_SERVER_URL` if provided env variable results in invalid URL (this is behavioral change as prior to it, the malformed URL would be passed to upper layers and result in invalid request attempt). Signed-off-by: Marek Chodor <[email protected]>
1 parent 7ccb2fc commit c1b0777

File tree

2 files changed

+143
-4
lines changed

2 files changed

+143
-4
lines changed

google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/OAuth2Utils.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import com.google.api.client.util.Beta;
2323
import java.io.IOException;
2424
import java.net.SocketTimeoutException;
25+
import java.net.URI;
26+
import java.net.URISyntaxException;
2527
import java.nio.charset.Charset;
2628
import java.util.Collection;
2729
import java.util.logging.Level;
@@ -103,9 +105,38 @@ public static String getMetadataServerUrl() {
103105
}
104106

105107
static String getMetadataServerUrl(SystemEnvironmentProvider environment) {
106-
String metadataServerAddress = environment.getEnv("GCE_METADATA_HOST");
107-
if (metadataServerAddress != null) {
108-
return "http://" + metadataServerAddress;
108+
String metadataServerHost = environment.getEnv("GCE_METADATA_HOST");
109+
if (metadataServerHost != null) {
110+
try {
111+
int idx = metadataServerHost.indexOf(":");
112+
if (idx >= 0 && idx == metadataServerHost.lastIndexOf(":")) {
113+
// only one occurrence of ':' indicate this is ipv4/domain and port
114+
return new URI(
115+
"http",
116+
null,
117+
metadataServerHost.substring(0, idx),
118+
Integer.parseInt(metadataServerHost.substring(idx + 1)),
119+
null,
120+
null,
121+
null)
122+
.toString();
123+
}
124+
return new URI("http", metadataServerHost, null, null).toString();
125+
} catch (NumberFormatException e) {
126+
LOGGER.log(
127+
Level.WARNING,
128+
"Invalid GCE_METADATA_HOST env provided, falling back to '"
129+
+ DEFAULT_METADATA_SERVER_URL
130+
+ "'.",
131+
e);
132+
} catch (URISyntaxException e) {
133+
LOGGER.log(
134+
Level.WARNING,
135+
"Invalid GCE_METADATA_HOST env provided, falling back to '"
136+
+ DEFAULT_METADATA_SERVER_URL
137+
+ "'.",
138+
e);
139+
}
109140
}
110141
return DEFAULT_METADATA_SERVER_URL;
111142
}

google-api-client/src/test/java/com/google/api/client/googleapis/auth/oauth2/DefaultCredentialProviderTest.java

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,33 @@ public void testDefaultCredentialNoGceCheck() throws IOException {
239239
assertEquals(0, transport.getRequestCount());
240240
}
241241

242-
public void testDefaultCredentialWithCustomMetadataServerAddress() throws IOException {
242+
public void testDefaultCredentialWithInvalidCustomMetadataServerAddress() throws IOException {
243+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
244+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
245+
testProvider.setEnv("GCE_METADATA_HOST", "this::domain.contains.invalid.chars");
246+
247+
try {
248+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
249+
fail("No credential expected for default test provider.");
250+
} catch (IOException expected) {
251+
}
252+
assertTrue(transport.urlWasRequested("http://169.254.169.254"));
253+
}
254+
255+
public void testDefaultCredentialWithInvalidCustomMetadataServerPort() throws IOException {
256+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
257+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
258+
testProvider.setEnv("GCE_METADATA_HOST", "test.metadata.server.address:portShouldBeANumber");
259+
260+
try {
261+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
262+
fail("No credential expected for default test provider.");
263+
} catch (IOException expected) {
264+
}
265+
assertTrue(transport.urlWasRequested("http://169.254.169.254"));
266+
}
267+
268+
public void testDefaultCredentialWithCustomMetadataServerDomainAddress() throws IOException {
243269
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
244270
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
245271
testProvider.setEnv("GCE_METADATA_HOST", "test.metadata.server.address");
@@ -252,6 +278,88 @@ public void testDefaultCredentialWithCustomMetadataServerAddress() throws IOExce
252278
assertTrue(transport.urlWasRequested("http://test.metadata.server.address"));
253279
}
254280

281+
public void testDefaultCredentialWithCustomMetadataServerDomainAddressAndCustomPort()
282+
throws IOException {
283+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
284+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
285+
testProvider.setEnv("GCE_METADATA_HOST", "test.metadata.server.address:8080");
286+
287+
try {
288+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
289+
fail("No credential expected for default test provider.");
290+
} catch (IOException expected) {
291+
}
292+
assertTrue(transport.urlWasRequested("http://test.metadata.server.address:8080"));
293+
}
294+
295+
public void testDefaultCredentialWithCustomMetadataServerIPv4Address() throws IOException {
296+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
297+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
298+
testProvider.setEnv("GCE_METADATA_HOST", "169.254.0.1");
299+
300+
try {
301+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
302+
fail("No credential expected for default test provider.");
303+
} catch (IOException expected) {
304+
}
305+
assertTrue(transport.urlWasRequested("http://169.254.0.1"));
306+
}
307+
308+
public void testDefaultCredentialWithCustomMetadataServerIPv4AddressAndCustomPort()
309+
throws IOException {
310+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
311+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
312+
testProvider.setEnv("GCE_METADATA_HOST", "169.254.0.1:8080");
313+
314+
try {
315+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
316+
fail("No credential expected for default test provider.");
317+
} catch (IOException expected) {
318+
}
319+
assertTrue(transport.urlWasRequested("http://169.254.0.1:8080"));
320+
}
321+
322+
public void testDefaultCredentialWithCustomMetadataServerIPv6Address() throws IOException {
323+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
324+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
325+
testProvider.setEnv("GCE_METADATA_HOST", "fe80::1");
326+
327+
try {
328+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
329+
fail("No credential expected for default test provider.");
330+
} catch (IOException expected) {
331+
}
332+
assertTrue(transport.urlWasRequested("http://[fe80::1]"));
333+
}
334+
335+
public void testDefaultCredentialWithCustomMetadataServerIPv6AddressProvidedWithBrackets()
336+
throws IOException {
337+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
338+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
339+
testProvider.setEnv("GCE_METADATA_HOST", "[fe80::1]");
340+
341+
try {
342+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
343+
fail("No credential expected for default test provider.");
344+
} catch (IOException expected) {
345+
}
346+
assertTrue(transport.urlWasRequested("http://[fe80::1]"));
347+
}
348+
349+
public void testDefaultCredentialWithCustomMetadataServerIPv6AddressAndCustomPort()
350+
throws IOException {
351+
MockRequestUrlRecordingTransport transport = new MockRequestUrlRecordingTransport();
352+
TestDefaultCredentialProvider testProvider = new TestDefaultCredentialProvider();
353+
testProvider.setEnv("GCE_METADATA_HOST", "[fe80::1]:8080");
354+
355+
try {
356+
testProvider.getDefaultCredential(transport, JSON_FACTORY);
357+
fail("No credential expected for default test provider.");
358+
} catch (IOException expected) {
359+
}
360+
assertTrue(transport.urlWasRequested("http://[fe80::1]:8080"));
361+
}
362+
255363
public void testDefaultCredentialNonExistentFileThrows() throws Exception {
256364
File nonExistentFile = new java.io.File(getTempDirectory(), "DefaultCredentialBadFile.json");
257365
assertFalse(nonExistentFile.exists());

0 commit comments

Comments
 (0)