Skip to content
This repository was archived by the owner on Feb 11, 2025. It is now read-only.

Commit f32236f

Browse files
authored
Add optional param for AWS IAM role assumption (#143)
Upgrade minio version and add user & policy initialization Fix permissions Organize imports
1 parent c6fa146 commit f32236f

File tree

8 files changed

+101
-33
lines changed

8 files changed

+101
-33
lines changed

.drone.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ steps:
1717
- git fetch --tags
1818

1919
- name: configure-buckets
20-
image: minio/mc:RELEASE.2018-09-26T00-42-43Z
20+
image: minio/mc:RELEASE.2020-10-03T02-54-56Z
2121
commands:
2222
- sleep 5
2323
- mc config host add minio http://minio:9000 AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
2424
- mc mb --region=eu-west-1 minio/drone-cache-bucket
25+
- mc admin user add minio foo barbarbar
26+
- "echo '{\"Version\": \"2012-10-17\", \"Statement\": [ { \"Action\": [ \"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\", \"s3:CreateBucket\", \"s3:DeleteBucket\" ], \"Effect\": \"Allow\", \"Resource\": [ \"arn:aws:s3:::s3-round-trip-with-role/*\", \"arn:aws:s3:::s3-round-trip-with-role\" ], \"Sid\": \"\" } ] }' >> /tmp/policy.json"
27+
- mc admin policy add minio userpolicy /tmp/policy.json
28+
- mc admin policy set minio userpolicy user=foo
2529

2630
- name: build
2731
image: golang:1.14.4-alpine3.12
@@ -206,7 +210,7 @@ steps:
206210

207211
services:
208212
- name: minio
209-
image: minio/minio:RELEASE.2020-03-05T01-04-19Z
213+
image: minio/minio:RELEASE.2020-11-06T23-17-07Z
210214
commands:
211215
- minio server /data
212216
environment:

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Fixes [#132](https://github.com/meltwater/drone-cache/issues/132)
1212
- [#138](https://github.com/meltwater/drone-cache/pull/138) backend/gcs: Fix GCS to pass credentials correctly when `GCS_ENDPOINT` is not set.
1313
- [#135](https://github.com/meltwater/drone-cache/issues/135) backend/gcs: Fixed parsing of GCS JSON key.
14-
14+
- [#142](https://github.com/meltwater/drone-cache/issues/142) backend/s3: Add option to assume AWS IAM role
1515
### Added
1616

1717
- [#102](https://github.com/meltwater/drone-cache/pull/102) Implement option to disable cache rebuild if it already exists in storage.

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ GLOBAL OPTIONS:
178178
--path-style AWS path style to use for bucket paths. (true for minio, false for aws) (default: false) [$PLUGIN_PATH_STYLE, $AWS_PLUGIN_PATH_STYLE]
179179
--acl value upload files with acl (private, public-read, ...) (default: "private") [$PLUGIN_ACL, $AWS_ACL]
180180
--encryption value server-side encryption algorithm, defaults to none. (AES256, aws:kms) [$PLUGIN_ENCRYPTION, $AWS_ENCRYPTION]
181+
--role-arn value AWS IAM role ARN to assume [$PLUGIN_ASSUME_ROLE_ARN, $AWS_ASSUME_ROLE_ARN]
181182
--gcs.api-key value Google service account API key [$PLUGIN_API_KEY, $GCP_API_KEY]
182183
--gcs.json-key value Google service account JSON key [$PLUGIN_JSON_KEY, $GCS_CACHE_JSON_KEY]
183184
--gcs.acl value upload files with acl (private, public-read, ...) (default: "private") [$PLUGIN_GCS_ACL, $GCS_ACL]

docker-compose.yml

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: '3'
22
services:
33
minio:
4-
image: minio/minio:RELEASE.2020-03-05T01-04-19Z
4+
image: minio/minio:RELEASE.2020-11-06T23-17-07Z
55
environment:
66
MINIO_ACCESS_KEY: AKIAIOSFODNN7EXAMPLE
77
MINIO_SECRET_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
@@ -25,8 +25,16 @@ services:
2525
- "10000:10000"
2626
command: azurite-blob --blobHost 0.0.0.0
2727
configure-buckets:
28-
image: minio/mc:RELEASE.2020-02-20T23-49-54Z
28+
image: minio/mc:RELEASE.2020-10-03T02-54-56Z
2929
entrypoint: sh
3030
depends_on:
3131
- minio
32-
command: -c "sleep 5 && mc config host add minio http://minio:9000 AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
32+
command:
33+
- -c
34+
- |
35+
sleep 2 &&
36+
mc config host add minio http://minio:9000 AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY &&
37+
mc admin user add minio foo barbarbar &&
38+
echo '{"Version": "2012-10-17", "Statement": [{"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:CreateBucket", "s3:DeleteBucket"], "Effect": "Allow", "Resource": ["arn:aws:s3:::s3-round-trip-with-role/*", "arn:aws:s3:::s3-round-trip-with-role"], "Sid": ""}]}' > /tmp/policy.json &&
39+
mc admin policy add minio userpolicy /tmp/policy.json &&
40+
mc admin policy set minio userpolicy user=foo

main.go

+6
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ func main() {
363363
Usage: "server-side encryption algorithm, defaults to none. (AES256, aws:kms)",
364364
EnvVars: []string{"PLUGIN_ENCRYPTION", "AWS_ENCRYPTION"},
365365
},
366+
&cli.StringFlag{
367+
Name: "role-arn",
368+
Usage: "AWS IAM role ARN to assume",
369+
Value: "",
370+
EnvVars: []string{"PLUGIN_ASSUME_ROLE_ARN", "AWS_ASSUME_ROLE_ARN"},
371+
},
366372

367373
// GCS specific Configs flags
368374

storage/backend/s3/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Config struct {
1515
Encryption string // if not "", enables server-side encryption. valid values are: AES256, aws:kms.
1616
Endpoint string
1717
Key string
18+
RoleArn string // if "", do not assume IAM role i.e. use the IAM user.
1819

1920
// us-east-1
2021
// us-west-1

storage/backend/s3/s3.go

+25
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
"github.com/aws/aws-sdk-go/aws"
1010
"github.com/aws/aws-sdk-go/aws/awserr"
1111
"github.com/aws/aws-sdk-go/aws/credentials"
12+
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
1213
"github.com/aws/aws-sdk-go/aws/session"
1314
"github.com/aws/aws-sdk-go/service/s3"
1415
"github.com/aws/aws-sdk-go/service/s3/s3manager"
16+
"github.com/aws/aws-sdk-go/service/sts"
1517
"github.com/go-kit/kit/log"
1618
"github.com/go-kit/kit/log/level"
1719

@@ -44,6 +46,12 @@ func New(l log.Logger, c Config, debug bool) (*Backend, error) {
4446
level.Warn(l).Log("msg", "aws key and/or Secret not provided (falling back to anonymous credentials)")
4547
}
4648

49+
if c.RoleArn != "" {
50+
conf.Credentials = credentials.NewStaticCredentials(c.Key, c.Secret, "")
51+
crds := assumeRole(l, conf, c.RoleArn)
52+
conf.Credentials = credentials.NewStaticCredentials(crds.AccessKeyID, crds.SecretAccessKey, crds.SessionToken)
53+
}
54+
4755
level.Debug(l).Log("msg", "s3 backend", "config", fmt.Sprintf("%#v", c))
4856

4957
if debug {
@@ -138,3 +146,20 @@ func (b *Backend) Exists(ctx context.Context, p string) (bool, error) {
138146
// Minio can return success status for without ETag, detect that here.
139147
return *out.ETag != "", nil
140148
}
149+
150+
func assumeRole(l log.Logger, c *aws.Config, roleArn string) credentials.Value {
151+
client := sts.New(session.Must(session.NewSessionWithOptions(session.Options{})), c)
152+
153+
stsProvider := stscreds.AssumeRoleProvider{
154+
Client: client,
155+
RoleARN: roleArn,
156+
RoleSessionName: "drone-cache",
157+
}
158+
159+
role, err := stsProvider.Retrieve()
160+
if err != nil {
161+
level.Error(l).Log("msg", "s3 backend", "assume-role", err.Error())
162+
}
163+
164+
return role
165+
}

storage/backend/s3/s3_test.go

+50-27
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,58 @@ import (
2020
)
2121

2222
const (
23-
defaultEndpoint = "127.0.0.1:9000"
24-
defaultAccessKey = "AKIAIOSFODNN7EXAMPLE"
25-
defaultSecretAccessKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
26-
defaultRegion = "eu-west-1"
27-
defaultACL = "private"
23+
defaultEndpoint = "127.0.0.1:9000"
24+
defaultAccessKey = "AKIAIOSFODNN7EXAMPLE"
25+
defaultSecretAccessKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
26+
defaultRegion = "eu-west-1"
27+
defaultACL = "private"
28+
defaultUserAccessKey = "foo"
29+
defaultUserSecretAccessKey = "barbarbar"
2830
)
2931

3032
var (
31-
endpoint = getEnv("TEST_S3_ENDPOINT", defaultEndpoint)
32-
accessKey = getEnv("TEST_S3_ACCESS_KEY", defaultAccessKey)
33-
secretAccessKey = getEnv("TEST_S3_SECRET_KEY", defaultSecretAccessKey)
34-
acl = getEnv("TEST_S3_ACL", defaultACL)
33+
endpoint = getEnv("TEST_S3_ENDPOINT", defaultEndpoint)
34+
accessKey = getEnv("TEST_S3_ACCESS_KEY", defaultAccessKey)
35+
secretAccessKey = getEnv("TEST_S3_SECRET_KEY", defaultSecretAccessKey)
36+
acl = getEnv("TEST_S3_ACL", defaultACL)
37+
userAccessKey = getEnv("TEST_USER_S3_ACCESS_KEY", defaultUserAccessKey)
38+
userSecretAccessKey = getEnv("TEST_USER_S3_SECRET_KEY", defaultUserSecretAccessKey)
3539
)
3640

3741
func TestRoundTrip(t *testing.T) {
3842
t.Parallel()
3943

40-
backend, cleanUp := setup(t)
44+
backend, cleanUp := setup(t, Config{
45+
ACL: acl,
46+
Bucket: "s3-round-trip",
47+
Endpoint: endpoint,
48+
Key: accessKey,
49+
PathStyle: true, // Should be true for minio and false for AWS.
50+
Region: defaultRegion,
51+
Secret: secretAccessKey,
52+
})
53+
t.Cleanup(cleanUp)
54+
roundTrip(t, backend)
55+
}
56+
57+
func TestRoundTripWithAssumeRole(t *testing.T) {
58+
t.Parallel()
59+
60+
backend, cleanUp := setup(t, Config{
61+
ACL: acl,
62+
Bucket: "s3-round-trip-with-role",
63+
Endpoint: endpoint,
64+
Key: userAccessKey,
65+
PathStyle: true, // Should be true for minio and false for AWS.
66+
Region: defaultRegion,
67+
Secret: userSecretAccessKey,
68+
RoleArn: "arn:aws:iam::account-id:role/TestRole",
69+
})
4170
t.Cleanup(cleanUp)
71+
roundTrip(t, backend)
72+
}
4273

74+
func roundTrip(t *testing.T, backend *Backend) {
4375
content := "Hello world4"
4476

4577
// Test Put
@@ -62,44 +94,35 @@ func TestRoundTrip(t *testing.T) {
6294

6395
// Helpers
6496

65-
func setup(t *testing.T) (*Backend, func()) {
66-
client := newClient()
67-
bucket := "s3-round-trip"
97+
func setup(t *testing.T, config Config) (*Backend, func()) {
98+
client := newClient(config)
6899

69100
_, err := client.CreateBucketWithContext(context.Background(), &s3.CreateBucketInput{
70-
Bucket: aws.String(bucket),
101+
Bucket: aws.String(config.Bucket),
71102
})
72103
test.Ok(t, err)
73104

74105
b, err := New(
75106
log.NewNopLogger(),
76-
Config{
77-
ACL: acl,
78-
Bucket: bucket,
79-
Endpoint: endpoint,
80-
Key: accessKey,
81-
PathStyle: true, // Should be true for minio and false for AWS.
82-
Region: defaultRegion,
83-
Secret: secretAccessKey,
84-
},
107+
config,
85108
false,
86109
)
87110
test.Ok(t, err)
88111

89112
return b, func() {
90-
_, _ = client.DeleteBucket(&s3.DeleteBucketInput{
91-
Bucket: aws.String(bucket),
113+
_, err = client.DeleteBucket(&s3.DeleteBucketInput{
114+
Bucket: aws.String(config.Bucket),
92115
})
93116
}
94117
}
95118

96-
func newClient() *s3.S3 {
119+
func newClient(config Config) *s3.S3 {
97120
conf := &aws.Config{
98121
Region: aws.String(defaultRegion),
99122
Endpoint: aws.String(endpoint),
100123
DisableSSL: aws.Bool(!strings.HasPrefix(endpoint, "https://")),
101124
S3ForcePathStyle: aws.Bool(true),
102-
Credentials: credentials.NewStaticCredentials(accessKey, secretAccessKey, ""),
125+
Credentials: credentials.NewStaticCredentials(config.Key, config.Secret, ""),
103126
}
104127

105128
return s3.New(session.Must(session.NewSessionWithOptions(session.Options{})), conf)

0 commit comments

Comments
 (0)