Skip to content

Commit

Permalink
Merge branch 'c0state-creds_from_profile_and_assume_role'
Browse files Browse the repository at this point in the history
  • Loading branch information
laurilehmijoki committed Oct 11, 2017
2 parents 9611e79 + f95aaea commit cbdc089
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 22 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ the project's root you can specify the directory like so:

If you omit `s3_id` from your `s3_website.yml`, S3_website will fall back to reading from the [default AWS SDK locations](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html). For instance, if you've used `aws configure` to set up credentials in `~/.aws/credentials`, S3_website can use these.

### Using an AWS profile or a profile that assumes a role

If you omit `s3_id`, `s3_secret`, and `session_token` you can specify an AWS credentials profile to use via the `profile` configuration variable, eg:

profile: name_of_aws_profile

In addition, if you want this profile to assume a role before executing against S3, use the `profile_assume_role_arn` variable, eg:

profile_assume_role_arn: arn_of_role_to_assume

(Note: you have to use a regular profile with an ID and SECRET and specify the role ARN via a variable like this instead of a profile that specifies a `role_arn` as documented [here](http://docs.aws.amazon.com/cli/latest/userguide/cli-roles.html) since it does not look like the Java SDK supports that format, yet...)

### Using environment variables

You can use ERB in your `s3_website.yml` file which incorporates environment variables:
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ libraryDependencies += "org.yaml" % "snakeyaml" % "1.13"

libraryDependencies += "org.jruby" % "jruby" % "1.7.11"

libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.11.32"
libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.11.172"

libraryDependencies += "log4j" % "log4j" % "1.2.17"

Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

This project uses [Semantic Versioning](http://semver.org).

## 3.3.0

* Support `http_error_code_returned_equals` in redirect rules

See <https://github.com/laurilehmijoki/configure-s3-website/pull/21> for
discussion

## 3.2.0

* Fall back to [the default credentials sources](http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) if `s3_id` is not provided in `s3_website.yml`
Expand Down
2 changes: 1 addition & 1 deletion lib/s3_website/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module S3Website
VERSION = '3.2.0'
VERSION = '3.3.0'
end
2 changes: 1 addition & 1 deletion s3_website.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.default_executable = %q{s3_website}

s.add_dependency 'thor', '~> 0.18'
s.add_dependency 'configure-s3-website', '= 2.1.0'
s.add_dependency 'configure-s3-website', '= 2.2.0'
s.add_dependency 'colored', '1.2'
s.add_dependency 'dotenv', '~> 1.0'

Expand Down
33 changes: 22 additions & 11 deletions src/main/scala/s3/website/model/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import scala.util.{Failure, Try}
import scala.collection.JavaConversions._
import s3.website.Ruby.rubyRuntime
import s3.website._
import com.amazonaws.auth.{AWSCredentialsProvider, BasicAWSCredentials, BasicSessionCredentials, AWSStaticCredentialsProvider, DefaultAWSCredentialsProviderChain}
import com.amazonaws.auth.{AWSCredentialsProvider, BasicAWSCredentials, BasicSessionCredentials, AWSStaticCredentialsProvider, DefaultAWSCredentialsProviderChain, STSAssumeRoleSessionCredentialsProvider}
import com.amazonaws.auth.profile.ProfileCredentialsProvider

case class Config(
s3_id: Option[String], // If undefined, use IAM Roles (http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-roles.html)
s3_secret: Option[String], // If undefined, use IAM Roles (http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-roles.html)
session_token: Option[String], // If defined, the AWS Security Token Service session token (http://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html)
profile: Option[String], // If defined, the AWS profile to use for credentials
profile_assume_role_arn: Option[String], // If defined, the ARN of the role to assume
s3_bucket: String,
s3_endpoint: S3Endpoint,
site: Option[String],
Expand All @@ -37,21 +40,29 @@ case class Config(
object Config {

def awsCredentials(config: Config): AWSCredentialsProvider = {
val credentialsFromConfigFile: Option[AWSStaticCredentialsProvider] =
if (config.s3_id.isEmpty) {
None
} else if (config.session_token.isEmpty) {
val credentialsFromConfigFile: Option[AWSCredentialsProvider] =
(
for {
s3_id <- config.s3_id
s3_secret <- config.s3_secret
} yield new AWSStaticCredentialsProvider(new BasicAWSCredentials(s3_id, s3_secret))
} else {
session_token <- config.session_token
} yield new AWSStaticCredentialsProvider(new BasicSessionCredentials(s3_id, s3_secret, session_token))
) orElse (
for {
s3_id <- config.s3_id
s3_secret <- config.s3_secret
session_token <- config.session_token
} yield new AWSStaticCredentialsProvider(new BasicSessionCredentials(s3_id, s3_secret, session_token))
}
} yield new AWSStaticCredentialsProvider(new BasicAWSCredentials(s3_id, s3_secret))
) orElse (
for {
profile <- config.profile
profile_assume_role_arn <- config.profile_assume_role_arn
} yield new STSAssumeRoleSessionCredentialsProvider.Builder(profile_assume_role_arn, "s3_website_assume_role_session")
.withLongLivedCredentialsProvider(new ProfileCredentialsProvider(profile)).build()
) orElse (
for {
profile <- config.profile
} yield new ProfileCredentialsProvider(profile)
)
credentialsFromConfigFile getOrElse new DefaultAWSCredentialsProviderChain
}

Expand Down Expand Up @@ -235,4 +246,4 @@ object Config {
case class S3_website_yml(file: File) {
override def toString = file.getPath
}
}
}
6 changes: 5 additions & 1 deletion src/main/scala/s3/website/model/Site.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ object Site {
s3_id <- loadOptionalString("s3_id").right
s3_secret <- loadOptionalString("s3_secret").right
session_token <- loadOptionalString("session_token").right
profile <- loadOptionalString("profile").right
profile_assume_role_arn <- loadOptionalString("profile_assume_role_arn").right
s3_bucket <- loadRequiredString("s3_bucket").right
s3_endpoint <- loadEndpoint.right
site <- loadOptionalString("site").right
Expand Down Expand Up @@ -67,6 +69,8 @@ object Site {
s3_id,
s3_secret,
session_token,
profile,
profile_assume_role_arn,
s3_bucket,
s3_endpoint getOrElse S3Endpoint.defaultEndpoint,
site,
Expand Down Expand Up @@ -152,4 +156,4 @@ object Site {
Right(None)
}
}
}
}
77 changes: 70 additions & 7 deletions src/test/scala/s3/website/ConfigSpec.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
package s3.website
import com.amazonaws.auth.{BasicAWSCredentials, BasicSessionCredentials, DefaultAWSCredentialsProviderChain}
import com.amazonaws.auth.profile.ProfileCredentialsProvider
import com.amazonaws.auth.{BasicAWSCredentials, BasicSessionCredentials, DefaultAWSCredentialsProviderChain, STSAssumeRoleSessionCredentialsProvider}
import org.specs2.mutable.Specification
import s3.website.model.{Config, S3Endpoint}

class ConfigSpec extends Specification {

"Config#awsCredentials" should {
s"return ${classOf[BasicSessionCredentials]} when s3_id, s3_secret and session_token are defined in the config" in {
Config.awsCredentials(Config(
s3_id = Some("test"),
s3_secret = Some("secret"),
session_token = Some("Token"),
profile = None,
profile_assume_role_arn = None,
s3_bucket = "foo",
s3_endpoint = S3Endpoint.defaultEndpoint,
site = None,
max_age = None,
cache_control = None,
gzip = None,
gzip_zopfli = None,
s3_key_prefix = None,
ignore_on_server = None,
exclude_from_upload = None,
s3_reduced_redundancy = None,
cloudfront_distribution_id = None,
cloudfront_invalidate_root = None,
content_type = None,
redirects = None,
concurrency_level = 1,
cloudfront_wildcard_invalidation = None,
treat_zero_length_objects_as_redirects = None
)).getCredentials must beAnInstanceOf[BasicSessionCredentials]
}

s"return ${classOf[BasicAWSCredentials]} when s3_id and s3_secret are defined in the config" in {
Config.awsCredentials(Config(
s3_id = Some("test"),
s3_secret = Some("secret"),
session_token = None,
profile = None,
profile_assume_role_arn = None,
s3_bucket = "foo",
s3_endpoint = S3Endpoint.defaultEndpoint,
site = None,
Expand All @@ -32,11 +63,13 @@ class ConfigSpec extends Specification {
)).getCredentials must beAnInstanceOf[BasicAWSCredentials]
}

s"return ${classOf[BasicSessionCredentials]} when s3_id, s3_secret and session_token are defined in the config" in {
s"return ${classOf[STSAssumeRoleSessionCredentialsProvider]} when profile and profile_assume_role_arn are defined in the config" in {
Config.awsCredentials(Config(
s3_id = Some("test"),
s3_secret = Some("secret"),
session_token = Some("Token"),
s3_id = None,
s3_secret = None,
session_token = None,
profile = Some("profile_name"),
profile_assume_role_arn = Some("arn:aws:iam::account-id:role/role-name"),
s3_bucket = "foo",
s3_endpoint = S3Endpoint.defaultEndpoint,
site = None,
Expand All @@ -55,14 +88,44 @@ class ConfigSpec extends Specification {
concurrency_level = 1,
cloudfront_wildcard_invalidation = None,
treat_zero_length_objects_as_redirects = None
)).getCredentials must beAnInstanceOf[BasicSessionCredentials]
)) must beAnInstanceOf[STSAssumeRoleSessionCredentialsProvider]
}

s"return ${classOf[ProfileCredentialsProvider]} when profile is defined in the config" in {
Config.awsCredentials(Config(
s3_id = None,
s3_secret = None,
session_token = None,
profile = Some("profile_name"),
profile_assume_role_arn = None,
s3_bucket = "foo",
s3_endpoint = S3Endpoint.defaultEndpoint,
site = None,
max_age = None,
cache_control = None,
gzip = None,
gzip_zopfli = None,
s3_key_prefix = None,
ignore_on_server = None,
exclude_from_upload = None,
s3_reduced_redundancy = None,
cloudfront_distribution_id = None,
cloudfront_invalidate_root = None,
content_type = None,
redirects = None,
concurrency_level = 1,
cloudfront_wildcard_invalidation = None,
treat_zero_length_objects_as_redirects = None
)) must beAnInstanceOf[ProfileCredentialsProvider]
}

s"return ${classOf[DefaultAWSCredentialsProviderChain]} when s3_id and s3_secret are not defined in the config" in {
s"return ${classOf[DefaultAWSCredentialsProviderChain]} when s3_id, s3_secret, profile and profile_assume_role_arn are not defined in the config" in {
Config.awsCredentials(Config(
s3_id = None,
s3_secret = None,
session_token = None,
profile = None,
profile_assume_role_arn = None,
s3_bucket = "foo",
s3_endpoint = S3Endpoint.defaultEndpoint,
site = None,
Expand Down

0 comments on commit cbdc089

Please sign in to comment.