This SBT plugin adds support for using Amazon S3 for resolving and publishing using s3:// urls.
Table of Contents generated with DocToc
- SBT 1.3 Support
- SBT 1.1 Support
- SBT 1.0 Support
- Examples
- Usage
- IAM Policy Examples
- IAM Role Policy Examples
- S3 Server-Side Encryption
- Authors
- Copyright
- License
SBT 1.3 support is available using version >= 0.19.0
:
addSbtPlugin("com.frugalmechanic" % "fm-sbt-s3-resolver" % "0.20.0")
SBT 1.1 support is available using version >= 0.14.0
:
Note: You need to use at least SBT 1.0.4 for this plugin to work with SBT 1.0 due to sbt/librarymanagement#175 which was fixed in this pull request: sbt/librarymanagement#183
Maven Style:
resolvers += "FrugalMechanic Snapshots" at "s3://fm-sbt-s3-resolver-example-bucket/snapshots"
Ivy Style:
resolvers += Resolver.url("FrugalMechanic Snapshots", url("s3://fm-sbt-s3-resolver-example-bucket/snapshots"))(Resolver.ivyStylePatterns)
Maven Style:
publishMavenStyle := true
publishTo := Some("FrugalMechanic Snapshots" at "s3://fm-sbt-s3-resolver-example-bucket/snapshots")
Ivy Style:
publishMavenStyle := false
publishTo := Some(Resolver.url("FrugalMechanic Snapshots", url("s3://fm-sbt-s3-resolver-example-bucket/snapshots"))(Resolver.ivyStylePatterns))
The examples above are using the Static Website Using a Custom Domain functionality of S3.
These would also be equivalent (for the fm-sbt-s3-resolver-example-bucket bucket):
s3://s3-us-west-2.amazonaws.com/fm-sbt-s3-resolver-example-bucket/snapshots
s3://fm-sbt-s3-resolver-example-bucket.s3-us-west-2.amazonaws.com/snapshots
s3://fm-sbt-s3-resolver-example-bucket.s3.amazonaws.com/snapshots
s3://s3.amazonaws.com/fm-sbt-s3-resolver-example-bucket/snapshots
All of these forms should work:
s3://[BUCKET]/[OPTIONAL_PATH]
s3://s3.amazonaws.com/[BUCKET]/[OPTIONAL_PATH]
s3://[BUCKET].s3.amazonaws.com/[OPTIONAL_PATH]
s3://s3-[REGION].amazonaws.com/[BUCKET]/[OPTIONAL_PATH]
s3://[BUCKET].s3-[REGION].amazonaws.com/[OPTIONAL_PATH]
addSbtPlugin("com.frugalmechanic" % "fm-sbt-s3-resolver" % "0.20.0")
S3 Credentials are checked in the following places and order (e.g. bucket specific settings (~/.sbt/.<bucket_name>_s3credentials) get resolved before global settings (~/.sbt/.s3credentials)):
Note: I think this logic has changed a little bit. See the S3URLHandler.defaultCredentialsProviderChain for the current implementation: https://github.com/frugalmechanic/fm-sbt-s3-resolver/blob/master/src/main/scala/fm/sbt/S3URLHandler.scala#L166
AWS_ACCESS_KEY_ID_<BUCKET_NAME> -or- <BUCKET_NAME>_AWS_ACCESS_KEY_ID
AWS_SECRET_KEY_<BUCKET_NAME> -or- <BUCKET_NAME>_AWS_SECRET_KEY
NOTE - The following transforms are applied to the bucket name before looking up the environment variable:
- The name is upper-cased
- Dots (.) and dashes (-) are replaced with an underscore (_)
- Everything other than A-Z, 0-9, and underscores are removed.
Example:
The bucket name "fm-sbt-s3-resolver-example-bucket" becomes "MAVEN_FRUGALMECHANIC_COM":
AWS_ACCESS_KEY_ID_MAVEN_FRUGALMECHANIC_COM="XXXXXX" AWS_SECRET_KEY_MAVEN_FRUGALMECHANIC_COM="XXXXXX" sbt
-Daws.accessKeyId.<bucket_name>=XXXXXX -Daws.secretKey.<bucket_name>=XXXXXX
-D<bucket_name>.aws.accessKeyId=XXXXXX -D<bucket_name>.aws.secretKey=XXXXXX
Example:
SBT_OPTS="-Daws.accessKeyId.fm-sbt-s3-resolver-example-bucket=XXXXXX -Daws.secretKey.fm-sbt-s3-resolver-example-bucket=XXXXXX" sbt
~/.sbt/.<bucket_name>_s3credentials
~/.sbt/.s3credentials_<bucket_name>
AWS_ACCESS_KEY_ID (or AWS_ACCESS_KEY)
AWS_SECRET_KEY (or AWS_SECRET_ACCESS_KEY)
AWS_ROLE_ARN
Example:
// Basic Credentials
AWS_ACCESS_KEY_ID="XXXXXX" AWS_SECRET_KEY="XXXXXX" sbt
// IAM Role Credentials
AWS_ACCESS_KEY_ID="XXXXXX" AWS_SECRET_KEY="XXXXXX" AWS_ROLE_ARN="arn:aws:iam::123456789012:role/RoleName" sbt
// Basic Credentials
-Daws.accessKeyId=XXXXXX -Daws.secretKey=XXXXXX
// IAM Role
-Daws.accessKeyId=XXXXXX -Daws.secretKey=XXXXXX -Daws.arnRole=arn:aws:iam::123456789012:role/RoleName
Example:
// Basic Credentials
SBT_OPTS="-Daws.accessKeyId=XXXXXX -Daws.secretKey=XXXXXX" sbt
// IAM Role Credentials
SBT_OPTS="-Daws.accessKeyId=XXXXXX -Daws.secretKey=XXXXXX -Daws.arnRole=arn:aws:iam::123456789012:role/RoleName" sbt
~/.sbt/.s3credentials
The property files should have the following format:
accessKey = XXXXXXXXXX
secretKey = XXXXXXXXXX
// Optional IAM Role
roleArn = arn:aws:iam::123456789012:role/RoleName
If the default credential providers do not work for you then you can specify your own AWSCredentialsProvider using the s3CredentialsProvider
SettingKey in your build.sbt
file:
import com.amazonaws.auth.{AWSCredentialsProviderChain, DefaultAWSCredentialsProviderChain}
import com.amazonaws.auth.profile.ProfileCredentialsProvider
s3CredentialsProvider := { (bucket: String) =>
new AWSCredentialsProviderChain(
new ProfileCredentialsProvider("my_profile"),
DefaultAWSCredentialsProviderChain.getInstance()
)
}
If you are really lazy and want to provide static credentials using this in your build.sbt
file will work:
import com.amazonaws.auth.{AWSStaticCredentialsProvider, BasicAWSCredentials}
s3CredentialsProvider := { (bucket: String) =>
new AWSStaticCredentialsProvider(new BasicAWSCredentials("your_accessKey", "your_secretKey"))
}
I recommend that you create IAM Credentials for reading/writing your Maven S3 Bucket. Here are some examples for our fm-sbt-s3-resolver-example-bucket bucket:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetBucketLocation"], "Resource": "arn:aws:s3:::*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket"] }, { "Effect": "Allow", "Action": ["s3:DeleteObject","s3:GetObject","s3:PutObject"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket/*"] } ] }
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetBucketLocation"], "Resource": "arn:aws:s3:::*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket"] }, { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket/*"] } ] }
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetBucketLocation"], "Resource": "arn:aws:s3:::*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket"] }, { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket/releases/*"] }, { "Effect": "Allow", "Action": ["s3:DeleteObject","s3:GetObject","s3:PutObject"], "Resource": ["arn:aws:s3:::fm-sbt-s3-resolver-example-bucket/snapshots/*"] } ] }
This is a simple example where a Host AWS Account, can create a Role with permissions for a Client AWS Account to access the Host maven bucket.
- Host AWS Account, creates an IAM Role named "ClientAccessRole" with policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[Client AWS Account Id]:user/[Client User Name]" }, "Action": "sts:AssumeRole" } ] }
- Associate the proper IAM Policy Examples to the Host Role
- Client AWS Account needs to create an AWS IAM User [Client User Name] and associated a policy to gives it permissions to AssumeRole from the Host AWS Account:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::[Host AWS Account Id]:role/ClientAccessRole" } ] }
S3 supports server side encryption.
The plugin will automatically detect if it needs to ask S3 to use SSE, based on the policies you have on your bucket. If
your bucket denies PutObject
requests that aren't using SSE, the plugin will include the SSE header in future requests.
To make use of SSE, configure your bucket to enforce the SSE header for PutObject
requests.
Example:
{ "Version": "2012-10-17", "Id": "PutObjPolicy", "Statement": [ { "Sid": "DenyIncorrectEncryptionHeader", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::YOUR_BUCKET_HERE/*", "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption": "AES256" } } }, { "Sid": "DenyUnEncryptedObjectUploads", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::YOUR_BUCKET_HERE/*", "Condition": { "Null": { "s3:x-amz-server-side-encryption": "true" } } } ] }
Tim Underwood (GitHub, LinkedIn, Twitter)
Copyright Frugal Mechanic