All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cognitect.aws.ec2_metadata_utils.clj Maven / Gradle / Ivy

The newest version!
;; Copyright (c) Cognitect, Inc.
;; All rights reserved.

(ns ^:skip-wiki cognitect.aws.ec2-metadata-utils
  "Impl, don't call directly"
  (:require [clojure.string :as str]
            [clojure.data.json :as json]
            [clojure.core.async :as a]
            [cognitect.aws.http :as http]
            [cognitect.aws.util :as u]
            [cognitect.aws.retry :as retry])
  (:import (java.net URI)))

(set! *warn-on-reflection* true)

(def ^:const ec2-metadata-service-override-system-property "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride")
(def ^:const dynamic-data-root "/latest/dynamic/")
(def ^:const imds-v2-token-path "/latest/api/token")
(def ^:const security-credentials-path "/latest/meta-data/iam/security-credentials/")
(def ^:const instance-identity-document "instance-identity/document")

;; ECS
(def ^:const container-credentials-relative-uri-env-var "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
(def ^:const container-credentials-full-uri-env-var "AWS_CONTAINER_CREDENTIALS_FULL_URI")
(def ^:const container-authorization-token-env-var "AWS_CONTAINER_AUTHORIZATION_TOKEN")

(def ^:const ec2-metadata-host "http://169.254.169.254")
(def ^:const ecs-metadata-host "http://169.254.170.2")

(defn in-container? []
  (or (u/getenv container-credentials-relative-uri-env-var)
      (u/getenv container-credentials-full-uri-env-var)))

(defn build-path [& components]
  (str/replace (str/join \/ components) #"\/\/+" (constantly "/")))

(defn- build-uri
  [host path]
  (str host "/" (cond-> path (str/starts-with? path "/") (subs 1))))

(defn get-host-address
  "Gets the EC2 (or ECS) metadata host address"
  []
  (or (u/getProperty ec2-metadata-service-override-system-property)
      (when (in-container?) ecs-metadata-host)
      ec2-metadata-host))

(defn- request-map
  [^URI uri]
  (let [auth-token (u/getenv container-authorization-token-env-var)]
    {:scheme (.getScheme uri)
     :server-name (.getHost uri)
     :server-port (or (when (pos? (.getPort uri)) (.getPort uri))
                      (when (#{"https"} (.getScheme uri)) 443)
                      80)
     :uri (.getPath uri)
     :request-method :get
     :headers (cond-> {"Accept" "*/*"}
                auth-token
                (assoc "Authorization" auth-token))}))

(defn- get-response-data [request-map http-client]
  (let [response (a/str (:body response)))))

(defn ^:deprecated get-data [uri http-client]
  "DEPRECATED use `request-map`, `get-response-data`"
  (get-response-data (request-map (URI. uri)) http-client))

(defn ^:deprecated get-data-at-path [path http-client]
  "DEPRECATED use `build-uri`, `request-map`, `get-response-data`"
  (get-data (build-uri (get-host-address) path) http-client))

(defn ^:deprecated get-listing [uri http-client]
  "DEPRECATED use `get-listing-from-response`"
  (some-> (get-data uri http-client) str/split-lines))

(defn ^:deprecated get-listing-at-path [path http-client]
  "DEPRECATED use `get-listing-from-response`"
  (get-listing (build-uri (get-host-address) path) http-client))

(defn- add-header-when-truthy
  "Add a header to a request map IFF the header value is truthy, return the 'updated' request map,
  else return the unmodified request map."
  [request header value]
  (if value
    (assoc-in request [:headers header] value)
    request))

(defn- get-listing-from-response [data]
  (some-> data str/split-lines))

(defn get-ec2-instance-data
  ([http-client]
   (get-ec2-instance-data http-client nil))
  ([http-client IMDSv2-token]
   (some-> (get-host-address)
           (build-uri (build-path dynamic-data-root instance-identity-document))
           (URI.)
           (request-map)
           (add-header-when-truthy "X-aws-ec2-metadata-token" IMDSv2-token)
           (get-response-data http-client)
           (json/read-str :key-fn keyword))))

(defn get-ec2-instance-region
  ([http-client]
   (get-ec2-instance-region http-client nil))
  ([http-client IMDSv2-token]
   (:region (get-ec2-instance-data http-client IMDSv2-token))))

(defn container-credentials [http-client]
  (let [endpoint (or (when-let [path (u/getenv container-credentials-relative-uri-env-var)]
                       (str (get-host-address) path))
                     (u/getenv container-credentials-full-uri-env-var))]
    (some-> endpoint
            (URI.)
            (request-map)
            (get-response-data http-client)
            (json/read-str :key-fn keyword))))

(defn instance-credentials
  ([http-client]
   (instance-credentials http-client nil))
  ([http-client IMDSv2-token]
   (when (not (in-container?))
     (when-let [cred-name (-> (get-host-address)
                              (build-uri security-credentials-path)
                              (URI.)
                              (request-map)
                              (add-header-when-truthy "X-aws-ec2-metadata-token" IMDSv2-token)
                              (get-response-data http-client)
                              (get-listing-from-response)
                              first)]
       (some-> (get-host-address)
               (build-uri (str security-credentials-path cred-name))
               (URI.)
               (request-map)
               (add-header-when-truthy "X-aws-ec2-metadata-token" IMDSv2-token)
               (get-response-data http-client)
               (json/read-str :key-fn keyword))))))

(defn IMDSv2-token
  "Retrieve and return an IMDS v2 session token, or nil if IMDS v2 is not in effect."
  [http-client]
  (-> (get-host-address)
      (build-uri imds-v2-token-path)
      (URI.)
      (request-map)
      (assoc :request-method :put)
      (add-header-when-truthy "X-aws-ec2-metadata-token-ttl-seconds" "21600")
      (get-response-data http-client)))




© 2015 - 2025 Weber Informatics LLC | Privacy Policy