Skip to content

Commit

Permalink
Enable accessing a bucket using an IAM Role
Browse files Browse the repository at this point in the history
This adds the source param `aws_role_arn` to the
resource.

If an IAM Role ARN is provided, the resource will
use the provided credentials to assume the role to
access the bucket.
  • Loading branch information
Bill Franklin committed May 28, 2021
1 parent 3639054 commit 1b44254
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ COPY --from=builder assets/ /opt/resource/
RUN chmod +x /opt/resource/*

FROM resource AS tests
ARG S3_TESTING_AWS_ROLE_ARN
ARG S3_ROLE_RESTRICTED_TESTING_BUCKET
ARG S3_TESTING_ACCESS_KEY_ID
ARG S3_TESTING_SECRET_ACCESS_KEY
ARG S3_TESTING_SESSION_TOKEN
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ version numbers.

* `bucket`: *Required.* The name of the bucket.

* `aws_role_arn`: *Optional.* The AWS role ARN to use when accessing the
bucket.

* `access_key_id`: *Optional.* The AWS access key to use when accessing the
bucket.

Expand Down Expand Up @@ -252,6 +255,22 @@ Run the tests with the following command:
docker build . -t s3-resource --build-arg S3_TESTING_ACCESS_KEY_ID="access-key" --build-arg S3_TESTING_SECRET_ACCESS_KEY="some-secret" --build-arg S3_TESTING_BUCKET="bucket-non-versioned" --build-arg S3_VERSIONED_TESTING_BUCKET="bucket-versioned" --build-arg S3_TESTING_REGION="us-east-1" --build-arg S3_ENDPOINT="https://s3.amazonaws.com"
```

This resource also supports assuming a role. Here's how you test that:

1. Do not permit the test user you are using to access the S3 bucket, e.g.
give the user a policy that only permits access to the two buckets (versioned
and unversioned).
1. Create an IAM Role with permission to access all three buckets used in the
integration tests. The user you have created should have permission to assume
the role

Add these args to the `docker build` command above.

```
--build-arg S3_ROLE_RESTRICTED_TESTING_BUCKET="role-restricted-bucket"
--build-arg S3_TESTING_AWS_ROLE_ARN="unrestricted-role"
```
### Contributing
Please make all pull requests to the `master` branch and ensure tests pass
Expand Down
1 change: 1 addition & 0 deletions cmd/check/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func main() {
request.Source.Endpoint,
request.Source.DisableSSL,
request.Source.SkipSSLVerification,
request.Source.AwsRoleArn,
)

client := s3resource.NewS3Client(
Expand Down
1 change: 1 addition & 0 deletions cmd/in/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func main() {
request.Source.Endpoint,
request.Source.DisableSSL,
request.Source.SkipSSLVerification,
request.Source.AwsRoleArn,
)

if len(request.Source.CloudfrontURL) != 0 {
Expand Down
1 change: 1 addition & 0 deletions cmd/out/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func main() {
request.Source.Endpoint,
request.Source.DisableSSL,
request.Source.SkipSSLVerification,
request.Source.AwsRoleArn,
)

client := s3resource.NewS3Client(
Expand Down
8 changes: 8 additions & 0 deletions integration/integration_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ func TestIntegration(t *testing.T) {
}

var useInstanceProfile = os.Getenv("S3_USE_INSTANCE_PROFILE")
var awsRoleArn = os.Getenv("S3_TESTING_AWS_ROLE_ARN")
var accessKeyID = os.Getenv("S3_TESTING_ACCESS_KEY_ID")
var secretAccessKey = os.Getenv("S3_TESTING_SECRET_ACCESS_KEY")
var sessionToken = os.Getenv("S3_TESTING_SESSION_TOKEN")
var roleRestrictedBucketName = os.Getenv("S3_ROLE_RESTRICTED_TESTING_BUCKET")
var versionedBucketName = os.Getenv("S3_VERSIONED_TESTING_BUCKET")
var bucketName = os.Getenv("S3_TESTING_BUCKET")
var regionName = os.Getenv("S3_TESTING_REGION")
Expand Down Expand Up @@ -81,6 +83,7 @@ func getSessionTokenS3Client(awsConfig *aws.Config) (*s3.S3, s3resource.S3Client
endpoint,
false,
false,
awsRoleArn,
)
s3Service := s3.New(session.New(newAwsConfig), newAwsConfig)
s3client := s3resource.NewS3Client(ioutil.Discard, newAwsConfig, v2signing == "true")
Expand Down Expand Up @@ -116,6 +119,10 @@ var _ = SynchronizedBeforeSuite(func() []byte {
Ω(secretAccessKey).ShouldNot(BeEmpty(), "must specify $S3_TESTING_SECRET_ACCESS_KEY")
}

if awsRoleArn != "" {
Ω(roleRestrictedBucketName).ShouldNot(BeEmpty(), "must specify $S3_ROLE_RESTRICTED_TESTING_BUCKET")
}

if accessKeyID != "" || useInstanceProfile != "" {
Ω(versionedBucketName).ShouldNot(BeEmpty(), "must specify $S3_VERSIONED_TESTING_BUCKET")
Ω(bucketName).ShouldNot(BeEmpty(), "must specify $S3_TESTING_BUCKET")
Expand All @@ -130,6 +137,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
endpoint,
false,
false,
awsRoleArn,
)

s3Service = s3.New(session.New(awsConfig), awsConfig)
Expand Down
13 changes: 13 additions & 0 deletions integration/s3client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,17 @@ var _ = Describe("S3client", func() {
Ω(err).ShouldNot(HaveOccurred())
})
})

Context("when using a roleArn", func() {
BeforeEach(func() {
if len(os.Getenv("S3_TESTING_AWS_ROLE_ARN")) == 0 || len(os.Getenv("S3_ROLE_RESTRICTED_TESTING_BUCKET")) == 0 {
Skip("'S3_TESTING_AWS_ROLE_ARN' or 'S3_ROLE_RESTRICTED_TESTING_BUCKET' is not set, skipping.")
}
})

It("can interact with buckets only available to the provided role", func() {
_, err := s3client.BucketFiles(roleRestrictedBucketName, directoryPrefix)
Ω(err).ShouldNot(HaveOccurred())
})
})
})
1 change: 1 addition & 0 deletions models.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package s3resource

type Source struct {
AwsRoleArn string `json:"aws_role_arn"`
AccessKeyID string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"`
SessionToken string `json:"session_token"`
Expand Down
12 changes: 11 additions & 1 deletion s3client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
Expand Down Expand Up @@ -88,6 +89,7 @@ func NewAwsConfig(
endpoint string,
disableSSL bool,
skipSSLVerification bool,
awsRoleArn string,
) *aws.Config {
var creds *credentials.Credentials

Expand All @@ -107,6 +109,14 @@ func NewAwsConfig(
creds = credentials.NewStaticCredentials(accessKey, secretKey, sessionToken)
}

if awsRoleArn != "" {
sesh := session.Must(session.NewSession(&aws.Config{
Region: &regionName,
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, sessionToken),
}))
creds = stscreds.NewCredentials(sesh, awsRoleArn)
}

if len(regionName) == 0 {
regionName = "us-east-1"
}
Expand Down Expand Up @@ -196,7 +206,7 @@ func (client *s3client) UploadFile(bucketName string, remotePath string, localPa
}

defer localFile.Close()

// Automatically adjust partsize for larger files.
fSize := stat.Size()
if fSize > int64(uploader.MaxUploadParts) * uploader.PartSize {
Expand Down

0 comments on commit 1b44254

Please sign in to comment.