Skip to content

Commit

Permalink
incorporate credentials file environment variables; update locate_cre…
Browse files Browse the repository at this point in the history
…dentials()
  • Loading branch information
leeper committed Apr 17, 2018
1 parent 60236a0 commit c0259f1
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 58 deletions.
6 changes: 3 additions & 3 deletions DESCRIPTION
@@ -1,14 +1,14 @@
Package: aws.signature
Type: Package
Title: Amazon Web Services Request Signatures
Version: 0.4.2
Date: 2018-04-11
Version: 0.4.3
Date: 2018-04-17
Authors@R: c(person("Thomas J.", "Leeper",
role = c("aut", "cre"),
email = "thosjleeper@gmail.com",
comment = c(ORCID = "0000-0003-4097-6326"))
)
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', users will need to install the suggested package 'aws.ec2metadata' <https://cran.r-project.org/package=aws.ec2metadata>.
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,
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
@@ -1,3 +1,9 @@
# aws.signature 0.4.3

* Incorporated standard environment variables `AWS_SHARED_CREDENTIALS_FILE` and `AWS_PROFILE` into code as appropriate and tweaked `locate_credentials()` accordingly.
* `signature_v4_auth()` and `signature_v2_auth()` now returns all inputs to facilitate using the return value in constructing an HTTP request.
* Updated documentation.

# aws.signature 0.4.2

* Fixed a bug in the default `datetime` argument in several functions. (#28, h/t @yansonz)
Expand Down
65 changes: 43 additions & 22 deletions R/locate_credentials.R
Expand Up @@ -5,31 +5,31 @@
#' @param secret An AWS Secret Access Key
#' @param session_token Optionally, an AWS Security Token Service (STS) temporary Session Token
#' @param region A character string containing the AWS region for the request. If missing, \dQuote{us-east-1} is assumed.
#' @param file A character string containing a path to a \samp{.aws/credentials} file.
#' @param profile A character string specifying which profile to use from the file. By default, the \dQuote{default} profile is used.
#' @param file A character string containing a path to a centralized \samp{.aws/credentials} file.
#' @param profile A character string specifying which profile to use from the file. By default, the profile named in \env{AWS_PROFILE} is used, otherwise the \dQuote{default} profile is used.
#' @param default_region A character string specifying a default string to use of no user-supplied value is found.
#' @param verbose A logical indicating whether to be verbose.
#' @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_SESSION_TOKEN})
#' \item an IAM instance role (on the running EC2 instance from which this function is called) as identified by \code{\link[aws.ec2metadata]{metadata}}
#' \item a specified profile in a local credentials dot file in the current working directory
#' \item the default profile in a local credentials dot file in the current working directory
#' \item a specified profile in a global credentials dot file in, typically in \file{~/.aws/credentials}. See \code{\link{use_credentials}} for details
#' \item the default profile in a global credentials dot file in, typically in \file{~/.aws/credentials}. See \code{\link{use_credentials}} for details
#' \item environment variables (\env{AWS_ACCESS_KEY_ID}, \env{AWS_SECRET_ACCESS_KEY}, \env{AWS_DEFAULT_REGION}, and \env{AWS_SESSION_TOKEN})
#' \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.ec2metadtaa 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}
#' \item the default profile in that local credentials file
#' \item a profile in a global credentials dot file in a location set by \env{AWS_SHARED_CREDENTIALS_FILE} or defaulting typically to \file{~/.aws/credentials} (or another OS-specific location), using the profile specified by \env{AWS_PROFILE}
#' \item the default profile in that global credentials file
#' }
#'
#' \code{use_credentials} is called by default on package load if \env{AWS_ACCESS_KEY_ID} and \env{AWS_SECRET_ACCESS_KEY} environment variables are not present.
#' If \env{AWS_ACCESS_KEY_ID} and \env{AWS_SECRET_ACCESS_KEY} environment variables are not present when the package is loaded, then \code{use_credentials} is invoked using the file specified in \env{AWS_SHARED_CREDENTIALS_FILE} (or another default location) and the profile specified in \env{AWS_PROFILE} (or, if missing, the \dQuote{default} profile).
#'
#' To use this (and any cloudyr package) on AWS EC2 instances, users will also need to install the \href{https://cran.r-project.org/package=aws.ec2metadata}{aws.ec2metadata} package, which allows \code{locate_credentials} to know it is running in an instance and check for relevant values. If this package is not installed, instance metadata is not checked.
#'
#' Because region is often handled slightly differently from credentials and is required for most requests (whereas some services allow anonymous requests without specifying credentials), the value of region is searched for in the same order as the above but lacking a value there fails safe with the following preference ranking of possible region values (regardless of location of other credentials):
#' \enumerate{
#' \item a user-supplied value
#' \item (if a credentials file is being used) the value specified therein
#' \item the \env{AWS_DEFAULT_REGION} environment variable
#' \item (only on EC2 instances) a region declared in the instance metadata
#' \item (if a credentials file is being used) the value specified therein
#' \item the default value specified in \code{default_region} (i.e., \dQuote{us-east-1})
#' }
#'
Expand All @@ -38,12 +38,12 @@
#' @seealso \code{\link{signature_v4}}, \code{\link{signature_v2_auth}}, \code{\link{use_credentials}}
#' @export
locate_credentials <-
function(key = NULL,
secret = NULL,
session_token = NULL,
region = NULL,
file = NULL,
profile = "default",
function(key = NULL,
secret = NULL,
session_token = NULL,
region = NULL,
file = Sys.getenv("AWS_SHARED_CREDENTIALS_FILE", default_credentials_file()),
profile = Sys.getenv("AWS_PROFILE", "default"),
default_region = "us-east-1",
verbose = FALSE) {

Expand Down Expand Up @@ -218,19 +218,40 @@ function(key = NULL,
if (file.exists(file.path(".aws", "credentials"))) {
## in working directory
cred <- read_credentials(file.path(".aws", "credentials"))[[profile]]
if (profile %in% names(cred)) {
cred <- cred[[profile]]
} else {
cred <- cred[["default"]]
if (isTRUE(verbose)) {
warning(sprintf("Requested profile '%s' not found in file. Using 'default' profile.", profile))
}
}
if (isTRUE(verbose)) {
message(sprintf("Using local credentials files from '%s'", file.path(".aws", "credentials")))
message(sprintf("Using profile '%s' from local credentials files from '%s'", profile, file.path(".aws", "credentials")))
}
} else if (file.exists(file) || file.exists(default_credentials_file())) {
## in specified location
if (file.exists(file)) {
cred <- read_credentials(file = file)[[profile]]
} else {
## otherwise, default to default location
cred <- read_credentials(file = default_credentials_file())[[profile]]
}
if (profile %in% names(cred)) {
cred <- cred[[profile]]
} else {
cred <- cred[["default"]]
if (isTRUE(verbose)) {
warning(sprintf("Requested profile '%s' not found in file. Using 'default' profile.", profile))
}
}
} else if (file.exists(default_credentials_file())) {
## in central location
cred <- read_credentials()[[profile]]
if (isTRUE(verbose)) {
message(sprintf("Using global credentials files from '%s'", default_credentials_file()))
message(sprintf("Using profile '%s' from global credentials files from '%s'", profile, default_credentials_file()))
}
} else {
# if that fails, no credentials can be found anywhere
if (isTRUE(verbose)) {
message("No instance metadata, environment variables, or credentials file found!")
message("No user-supplied credentials, environment variables, instance metadata, or credentials file found!")
}
# now find region, with fail safes
if (!is.null(region) && region != "") {
Expand Down
25 changes: 17 additions & 8 deletions R/read_credentials.R
Expand Up @@ -2,24 +2,28 @@
#' @title Use Credentials from .aws/credentials File
#' @description Use a profile from a \samp{.aws/credentials} file
#' @param profile A character string specifing which profile to use from the file. By default, the \dQuote{default} profile is used.
#' @param file A character string containing a path to a \samp{.aws/credentials} file. By default, the standard/centralized file is used. For \code{use_credentials}, this can also be an object of class \dQuote{aws_credentials} (as returned by \code{use_credentials}).
#' @param file A character string containing a path to a \samp{.aws/credentials} file. By default, the standard/centralized file given by \env{AWS_SHARED_CREDENTIALS_FILE} is used, otherwise an assumed default location is assumed. For \code{use_credentials}, this can also be an object of class \dQuote{aws_credentials} (as returned by \code{use_credentials}).
#' @details \code{read_credentials} reads and parses a \samp{.aws/credentials} file into an object of class \dQuote{aws_credentials}.
#'
#' \code{use_credentials} uses credentials from a profile stored in a credentials file to set the environment variables used by this package.
#' \code{use_credentials} uses credentials from a profile stored in a credentials file to set the environment variables used by this package. It is called by default during package load if the \env{AWS_ACCESS_KEY_ID} and \env{AWS_SECRET_ACCESS_KEY} environment variables are not set.
#'
#' @author Thomas J. Leeper <thosjleeper@gmail.com>
#' @references
#' \href{https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs}{Amazon blog post describing the format}
#' @seealso \code{\link{signature_v2_auth}}, \code{\link{locate_credentials}}
#' @examples
#' \dontrun{
#' # read and parse a credentials file
#' read_credentials()
#'
#' # set environment variables from a profile
#' use_credentials()
#'
#' # read and parse a file
#' read_credentials()
#' }
#' @export
read_credentials <- function(file = default_credentials_file()) {
read_credentials <-
function(
file = Sys.getenv("AWS_SHARED_CREDENTIALS_FILE", default_credentials_file())
) {
file <- path.expand(file)
if (!file.exists(file)) {
stop(paste0("File ", shQuote(file), " does not exist."))
Expand All @@ -30,7 +34,11 @@ read_credentials <- function(file = default_credentials_file()) {

#' @rdname read_credentials
#' @export
use_credentials <- function(profile = Sys.getenv("AWS_PROFILE", "default"), file = default_credentials_file()) {
use_credentials <-
function(
profile = Sys.getenv("AWS_PROFILE", "default"),
file = Sys.getenv("AWS_SHARED_CREDENTIALS_FILE", default_credentials_file())
) {
if (inherits(file, "aws_credentials")) {
x <- file
} else {
Expand All @@ -53,7 +61,8 @@ use_credentials <- function(profile = Sys.getenv("AWS_PROFILE", "default"), file

#' @rdname read_credentials
#' @export
default_credentials_file <- function() {
default_credentials_file <-
function() {
if (.Platform[["OS.type"]] == "windows") {
home <- Sys.getenv("USERPROFILE")
} else {
Expand Down
20 changes: 13 additions & 7 deletions README.md
@@ -1,10 +1,12 @@
# Amazon Web Services Request Signatures

**aws.signature** is a simple R package to create request signatures for Amazon Web Services (AWS) APIs. It supports both the current [Signature Version 4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) and the legacy [Signature Version 2](http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html). The former is used by most services. The high-level functions `signature_v4_auth()` and `signature_v2_auth()` translate request parameters into appropriate HTTP Authorization headers to pass to the APIs.
**aws.signature** is a package for creating request signatures for Amazon Web Services (AWS) APIs. It supports both the current [Signature Version 4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) and the legacy [Signature Version 2](http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html). The former is used by most services. The high-level functions `signature_v4_auth()` and `signature_v2_auth()` translate request parameters into appropriate HTTP Authorization headers to pass to the APIs.

To use the package, you will need an AWS account and to enter your credentials into R. Your keypair can be generated on the [IAM Management Console](https://aws.amazon.com/) under the heading *Access Keys*. Note that you only have access to your secret key once. After it is generated, you need to save it in a secure location. New keypairs can be generated at any time if yours has been lost, stolen, or forgotten. The [**aws.iam** package](https://github.com/cloudyr/aws.iam) profiles tools for working with IAM, including creating roles, users, groups, and credentials programmatically; it is not needed to *use* IAM credentials.

By default, when loaded the package checks for environment variables. If absent, it checks for a default credentials file and loads credentials from it. Regardless of this initial configuration, all **cloudyr** packages for AWS services allow the use of credentials specified in a number of ways, beginning with:
By default, when loaded the package checks for environment variables. If absent, it checks for a default credentials file and loads credentials from it into environment variables; the profile used from that file can be regulated by setting the `AWS_PROFILE` environment variable before loading this package (the `"default" profile is assumed if none is specified). This means the package and any dependencies should *just work* without needing to explicitly set or pass credentials within R code.

Regardless of this initial configuration, all **awspack** packages allow the use of credentials specified in a number of ways, in the following priority order:

1. User-supplied values passed directly to functions.
2. Environment variables, which can alternatively be set on the command line prior to starting R or via an `Renviron.site` or `.Renviron` file, which are used to set environment variables in R during startup (see `? Startup`). Or they can be set within R:
Expand All @@ -15,11 +17,11 @@ By default, when loaded the package checks for environment variables. If absent,
"AWS_DEFAULT_REGION" = "us-east-1",
"AWS_SESSION_TOKEN" = "mytoken")
```
3. If R is running on an EC2 instance, the role profile credentials provided by [**aws.ec2metadata**](https://cran.r-project.org/package=aws.ec2metadata).
4. Profiles saved in a `/.aws/credentials` "dot file" in the current working directory. The `"default" profile is assumed if none is specified.
5. [A centralized `~/.aws/credentials` file](https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs), containing credentials for multiple accounts. The `"default" profile is assumed if none is specified.
3. If R is running on an EC2 instance, the role profile credentials provided by [**aws.ec2metadata**](https://cran.r-project.org/package=aws.ec2metadata), if the **aws.ec2metadata** package is installed.
4. Profiles saved in a `/.aws/credentials` "dot file" in the current working directory. The profile used can be regulated by the `AWS_PROFILE` environment variable, otherwise the `"default" profile is assumed if none is specified or the specified profile is missing.
5. [A centralized credentials file](https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs), containing credentials for multiple accounts. The location of this file is given by the `AWS_SHARED_CREDENTIALS_FILE` environment variable or, if that is missing, by `~/.aws/credentials` (or an OS-specific equivalent). The profile used from that file can be regulated by the `AWS_PROFILE` environment variable, otherwise the `"default" profile is assumed if none is specified or the specified profile is missing.

Profiles stored locally or in a centralized location (e.g., `~/.aws/credentials`) can also be invoked via:
Because all functions requesting a signature walk this entire list of potential credentials sources, it typically makes sense to set environment variables otherwise a potentially large performance penalty can be paid. For this reason, it is usually better to explicitly invoke a profiles stored in a local or centralized (e.g., `~/.aws/credentials`) credentials file using:

```R
# use your 'default' account credentials
Expand All @@ -29,7 +31,11 @@ use_credentials()
use_credentials(profile = "bob")
```

Temporary session tokens are stored in environment variable `AWS_SESSION_TOKEN` (and will be stored there by the `use_credentials()` function). The [aws.iam package](https://github.com/cloudyr/aws.iam/) provides an R interface to IAM roles and the generation of temporary session tokens via the security token service (STS).
For purposes of debugging, it can be useful to set the `verbose = TRUE` argument (or globally set `options(verbose = TRUE)`) in order to see what values are being used for signing requests.

Temporary session tokens are stored in environment variable `AWS_SESSION_TOKEN` (and will be stored there by the `use_credentials()` function). The [aws.iam package](https://github.com/cloudyr/aws.iam/) provides an R interface to IAM roles and the generation of temporary session tokens via the security token service (STS). On EC2 instances, the [**aws.ec2metadata**](https://cran.r-project.org/package=aws.ec2metadata) package should be installed so that signatures are signed with appropriate, dynamically updated credentials.

As a fail safe the `us-east-1` region is used whenever a region is not found.

## Installation

Expand Down

0 comments on commit c0259f1

Please sign in to comment.