Skip to content

Commit

Permalink
Merge pull request #63 from PeakBI/master
Browse files Browse the repository at this point in the history
Add support for assuming roles from web identities.
  • Loading branch information
jon-mago authored Feb 22, 2022
2 parents 5689733 + 6ad5b59 commit 8631c42
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 8 deletions.
14 changes: 10 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
Package: aws.signature
Type: Package
Title: Amazon Web Services Request Signatures
Version: 0.6.0
Version: 0.6.1
Date: 2020-06-01
Authors@R: c(person("Thomas J.", "Leeper",
role = c("aut"),
email = "[email protected]",
comment = c(ORCID = "0000-0003-4097-6326")),
person("Jonathan", "Stott", email = "[email protected]", role = c("cre", "aut")),
person("Mike", "Kaminsky", email = "[email protected]", role = "ctb")
person("Mike", "Kaminsky", email = "[email protected]", role = "ctb"),
person("Mark", "Douthwaite", email = "[email protected]", role = "ctb"),
person("Jason", "Gofford", email = "[email protected]", role = "ctb"),
person("Luke", "Dyer", email = "[email protected]", role = "ctb")
)
Description: Generates version 2 and version 4 request signatures for Amazon Web Services ('AWS') <https://aws.amazon.com/> Application Programming Interfaces ('APIs') and provides a mechanism for retrieving credentials from environment variables, 'AWS' credentials files, and 'EC2' instance metadata. For use on 'EC2' instances, users will need to install the suggested package 'aws.ec2metadata' <https://cran.r-project.org/package=aws.ec2metadata>.
License: GPL (>= 2)
Imports:
digest,
base64enc
base64enc,
jsonlite,
curl
Suggests:
devtools,
testthat (>= 2.1.0),
aws.ec2metadata (>= 0.1.6)
URL: https://github.com/cloudyr/aws.signature
BugReports: https://github.com/cloudyr/aws.signature/issues
RoxygenNote: 7.1.0
RoxygenNote: 7.1.2
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Generated by roxygen2: do not edit by hand

export(assume_role_with_web_identity)
export(canonical_request)
export(default_credentials_file)
export(locate_credentials)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# aws.signature 0.6.1

* Add support for [assuming roles from web identities](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) ([#62](https://github.com/cloudyr/aws.signature/issues/62)).

# aws.signature 0.6.0

* Allow connection to empty regions (h/t @namelessjon, @lawremi)
Expand Down
56 changes: 56 additions & 0 deletions R/assume_role.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#' @rdname assume_role_with_web_identity
#' @title Assume Role with AWS Web Identity
#' @description Assume a role from a provided Web Identity Token and ARN using AWS Secure Token Service (STS).
#' @param role_arn A character string containing the AWS Role Amazon Resource Name (ARN). This specifies the permissions you have to access other AWS services.
#' @param token_file A character string containing a path to a Web Identity Token file.
#' @param base_url The AWS STS endpoint to use to retrieve your credentials from.
#' @param session_name A character string optionally specifying the name.
#' @param duration The expiry time on the retrieved credentials.
#' @param version The AWS STS specification version to use.
#' @param verbose A logical indicating whether to be verbose.
#' @export
assume_role_with_web_identity <- function(
role_arn,
token_file,
base_url=Sys.getenv("AWS_STS_ENDPOINT", "https://sts.amazonaws.com"),
session_name=NULL,
duration=3600,
version="2011-06-15",
verbose = getOption("verbose", FALSE)
){
if (is.null(session_name)) {
# strip resource ID from arn and use as default session name
# https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
session_name <- gsub("/", "-", utils::tail(strsplit(role_arn, ":")[[1]], 1))
}

token <- readChar(token_file, file.info(token_file)$size)

query_params <- list(
Action="AssumeRoleWithWebIdentity",
DurationSeconds=duration,
RoleArn=role_arn,
RoleSessionName=session_name,
WebIdentityToken=token,
Version=version
)
query_params_names <- curl::curl_escape(names(query_params))
query_params_values <- lapply(query_params, curl::curl_escape)
query_str <- paste0(query_params_names, "=", query_params_values, collapse = "&")
query_url <- paste0(base_url, "/?", query_str)

handle <- curl::new_handle() # need to accept json headers
curl::handle_setheaders(handle, "accept" = "application/json")

response <- curl::curl_fetch_memory(query_url, handle = handle)
content <- jsonlite::fromJSON(rawToChar(response$content))

if (response$status_code == 200) {
if (isTRUE(verbose)) {
message("Successfully fetched token from web identiy provider.")
}
return(content)
} else {
stop("Failed to assume role.")
}
}
25 changes: 22 additions & 3 deletions R/locate_credentials.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#' @details These functions locate values of AWS credentials (access key, secret access key, session token, and region) from likely sources. The order in which these are searched is as follows:
#' \enumerate{
#' \item user-supplied values passed to the function
#' \item environment variables (\env{AWS_ACCESS_KEY_ID}, \env{AWS_SECRET_ACCESS_KEY}, \env{AWS_DEFAULT_REGION}, and \env{AWS_SESSION_TOKEN})
#' \item environment variables, first checking for default credentials (\env{AWS_ACCESS_KEY_ID}, \env{AWS_SECRET_ACCESS_KEY}, \env{AWS_DEFAULT_REGION}, and \env{AWS_SESSION_TOKEN}); then for Web Identity Provider credentials (\env{AWS_ROLE_ARN} and \env{AWS_WEB_IDENTITY_TOKEN_FILE})
#' \item an instance role (on the running ECS task from which this function is called) as identified by \code{\link[aws.ec2metadata]{metadata}}, if the aws.ec2metadata package is installed
#' \item an IAM instance role (on the running EC2 instance from which this function is called) as identified by \code{\link[aws.ec2metadata]{metadata}}, if the aws.ec2metadata package is installed
#' \item a profile in a local credentials dot file in the current working directory, using the profile specified by \env{AWS_PROFILE}
Expand Down Expand Up @@ -155,7 +155,7 @@ get_ec2_role <- function(role, verbose = getOption("verbose", FALSE)) {
if (inherits(out, "try-error")) {
out <- NULL
}
out
return(out)
}

credentials_to_list <-
Expand Down Expand Up @@ -286,6 +286,7 @@ check_for_user_supplied_profile <- function(profile, file, region, session_token
return(NULL)
}


check_for_env_vars <- function(region, file, default_region, session_token, verbose) {

if (isTRUE(verbose)) {
Expand All @@ -298,7 +299,9 @@ check_for_env_vars <- function(region, file, default_region, session_token, verb
secret = Sys.getenv("AWS_SECRET_ACCESS_KEY"),
profile = Sys.getenv("AWS_PROFILE"),
session_token = Sys.getenv("AWS_SESSION_TOKEN"),
region = Sys.getenv("AWS_DEFAULT_REGION"))
region = Sys.getenv("AWS_DEFAULT_REGION"),
arn=Sys.getenv("AWS_ROLE_ARN"),
token_file=Sys.getenv("AWS_WEB_IDENTITY_TOKEN_FILE"))

if (!is_blank(env$session_token)) {
session_token <- env$session_token
Expand Down Expand Up @@ -327,6 +330,22 @@ check_for_env_vars <- function(region, file, default_region, session_token, verb

# early return
return(list(key = key, secret = secret, session_token = session_token, region = region))

} else if (!is_blank(env$arn) && !is_blank(env$token_file)){ # Web Identity Provider
if (isTRUE(verbose)) {
message("Using Environment Variables 'AWS_WEB_IDENTITY_TOKEN_FILE' and `AWS_ROLE_ARN`")
message("to assume role with Web Identity Provider")
}
response <- assume_role_with_web_identity(env$arn, env$token_file)

creds <- response$AssumeRoleWithWebIdentityResponse$AssumeRoleWithWebIdentityResult$Credentials
key <- creds$AccessKeyId
secret <- creds$SecretAccessKey
session_token <- creds$SessionToken

region <- find_region_with_failsafe(region = region, default_region = default_region, verbose = verbose)

return(list(key = key, secret = secret, session_token = session_token, region = region))
} else if (!is_blank(env$profile)) {
return(check_credentials_file(env$profile, file, region, default_region, verbose))
}
Expand Down
2 changes: 1 addition & 1 deletion man/locate_credentials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8631c42

Please sign in to comment.