diff --git a/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java b/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java index 33195b7d..22204171 100644 --- a/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java +++ b/src/main/java/org/ice4j/ice/harvest/AwsCandidateHarvester.java @@ -19,8 +19,10 @@ import org.ice4j.*; -import java.io.*; import java.net.*; +import java.net.http.*; +import java.time.*; +import java.util.*; import java.util.logging.*; /** @@ -39,6 +41,14 @@ public class AwsCandidateHarvester private static final Logger logger = Logger.getLogger(AwsCandidateHarvester.class.getName()); + /** + * The HttpClient used by the AwsCandidateHarvester + * class and its instances for making requests to IMDS. + */ + private static final HttpClient httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofMillis(500)) + .build(); + /** * The URL where one obtains AWS public addresses. */ @@ -52,10 +62,28 @@ public class AwsCandidateHarvester = "http://169.254.169.254/latest/meta-data/local-ipv4"; /** - * The URL to use to test whether we are running on Amazon EC2. + * The URL to get IMDSv2 API token for further meta-data requests. + */ + private static final String IMDS_API_TOKEN_URL + = "http://169.254.169.254/latest/api/token"; + + /** + * The HTTP header name to provide session token. */ - private static final String EC2_TEST_URL - = "http://169.254.169.254/latest/meta-data/"; + private static final String EC2_METADATA_TOKEN_HEADER + = "X-aws-ec2-metadata-token"; + + /** + * The HTTP header name to request session token TTL. + */ + private static final String EC2_METADATA_TOKEN_TTL_HEADER + = "X-aws-ec2-metadata-token-ttl-seconds"; + + /** + * The default session token TTL value. + */ + private static final String EC2_METADATA_TOKEN_DEFAULT_TTL + = "21600"; /** * Whether we are running on Amazon EC2. @@ -103,8 +131,11 @@ private static synchronized void obtainEC2Addresses() try { - localIPStr = fetch(LOCAL_IP_URL); - publicIPStr = fetch(PUBLIC_IP_URL); + String metaDataToken = fetch(IMDS_API_TOKEN_URL, + Collections.singletonMap(EC2_METADATA_TOKEN_TTL_HEADER, EC2_METADATA_TOKEN_DEFAULT_TTL), "PUT"); + Map tokenHeader = Collections.singletonMap(EC2_METADATA_TOKEN_HEADER, metaDataToken); + localIPStr = fetch(LOCAL_IP_URL, tokenHeader); + publicIPStr = fetch(PUBLIC_IP_URL, tokenHeader); //now let's cross our fingers and hope that what we got above are //real IP addresses @@ -183,11 +214,10 @@ private static boolean doTestEc2() { try { - URLConnection conn = new URL(EC2_TEST_URL).openConnection(); - conn.setConnectTimeout(500); //don't hang for too long - conn.getContent(); + String metaDataToken = fetch(IMDS_API_TOKEN_URL, + Collections.singletonMap(EC2_METADATA_TOKEN_TTL_HEADER, EC2_METADATA_TOKEN_DEFAULT_TTL), "PUT"); - return true; + return metaDataToken != null; } catch(Exception exc) { @@ -201,21 +231,40 @@ private static boolean doTestEc2() * * @param url the URL we'd like to open and query. * + * @param headers the HTTP headers to put into the request. + * + * @throws Exception if anything goes wrong. + */ + private static String fetch(String url, Map headers) + throws Exception + { + return fetch(url, headers, "GET"); + } + + /** + * Retrieves the content at the specified url. No more, no less. + * + * @param url the URL we'd like to open and query. + * + * @param headers the HTTP headers to put into the request. + * + * @param method the HTTP method we'd like to use. + * * @return the String we retrieved from the URL. * * @throws Exception if anything goes wrong. */ - private static String fetch(String url) + private static String fetch(String url, Map headers, String method) throws Exception { - URLConnection conn = new URL(url).openConnection(); - BufferedReader in = new BufferedReader(new InputStreamReader( - conn.getInputStream(), "UTF-8")); - - String retString = in.readLine(); - - in.close(); - - return retString; + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(new URI(url)) + .method(method, HttpRequest.BodyPublishers.noBody()); + for (Map.Entry header : headers.entrySet()) + { + builder.setHeader(header.getKey(), header.getValue()); + } + HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString()); + return response.body(); } }