Skip to content

Instantly share code, notes, and snippets.

@gws
Created December 31, 2020 18:33
Show Gist options
  • Save gws/130ad8bfec5495c25c3dbc0ed2a69d42 to your computer and use it in GitHub Desktop.
Save gws/130ad8bfec5495c25c3dbc0ed2a69d42 to your computer and use it in GitHub Desktop.
Cognitect AWS API with web identity
;; Copyright 2020 Gordon Stratton
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
(ns cognitect-aws-api-web-identity
(:require [clojure.java.io :as io]
[cognitect.aws.client.api :as aws]
[cognitect.aws.client.shared :as shared]
[cognitect.aws.credentials :as credentials]
[cognitect.aws.util :as util])
(:import (java.io File)))
(def ^:private sts-client
"An AWS STS client using static, empty credentials."
(aws/client {:api :sts
:credentials-provider (credentials/basic-credentials-provider
{:access-key-id ""
:secret-access-key ""})}))
(defn- assume-role-with-web-identity
"Call AssumeRoleWithWebIdentity on the STS service with `role-arn`,
`session-name`, and `token`, returning the `:Credentials` map if it exists,
otherwise `nil`. Passing `nil` for `session-name` will cause a session name to
be automatically generated."
[role-arn session-name token]
(let [session-name (or session-name
(str "cognitect-aws-api-" (System/currentTimeMillis)))]
(-> (aws/invoke sts-client {:op :AssumeRoleWithWebIdentity
:request {:RoleArn role-arn
:RoleSessionName session-name
:WebIdentityToken token}})
:Credentials)))
(defn web-identity-credentials-provider
"Obtains temporary credentials from STS using the AssumeRoleWithWebIdentity
API call.
The role to assume is expected to be in the environment variable AWS_ROLE_ARN,
and the file containing the web identity token to exchange for the temporary
credentials is expected to be named by the AWS_WEB_IDENTITY_TOKEN_FILE
environment variable. Will optionally use AWS_ROLE_SESSION_NAME from the
environment to name the session, or else a session name will be automatically
generated."
([]
(web-identity-credentials-provider
(util/getenv "AWS_ROLE_ARN")
(util/getenv "AWS_ROLE_SESSION_NAME")
(io/file (util/getenv "AWS_WEB_IDENTITY_TOKEN_FILE"))))
([role-arn session-name ^File token]
(credentials/cached-credentials-with-auto-refresh
(reify credentials/CredentialsProvider
(fetch [_]
(when (and role-arn token (.isFile token) (.canRead token))
(when-let [creds (assume-role-with-web-identity role-arn
session-name
(slurp token))]
(credentials/valid-credentials
{:aws/access-key-id (:AccessKeyId creds)
:aws/secret-access-key (:SecretAccessKey creds)
:aws/session-token (:SessionToken creds)
::credentials/ttl (credentials/calculate-ttl creds)}
"web identity"))))))))
(defn default-credentials-provider
"Like [[credentials/default-credentials-provider]], but with the addition of
[[web-identity-credentials-provider]] in the same position as in the AWS SDK
for Java's default credential provider chain."
([]
(default-credentials-provider (shared/http-client)))
([http-client]
(credentials/chain-credentials-provider
[(credentials/environment-credentials-provider)
(credentials/system-property-credentials-provider)
(web-identity-credentials-provider)
(credentials/profile-credentials-provider)
(credentials/container-credentials-provider http-client)
(credentials/instance-profile-credentials-provider http-client)])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment