diff --git a/go.mod b/go.mod index 95ad642b82..4f167e391c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 replace ( cloud.google.com/go => github.com/GoogleCloudPlatform/google-cloud-go v0.1.1-0.20160913182117-3b1ae45394a2 - github.com/graymeta/stow => github.com/kastenhq/stow v0.1.1-kasten + github.com/graymeta/stow => github.com/kastenhq/stow v0.1.2-kasten github.com/rook/operator-kit => github.com/kastenhq/operator-kit v0.0.0-20180316185208-859e831cc18d ) diff --git a/go.sum b/go.sum index bfb7bb34b1..05161caf11 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kastenhq/stow v0.1.1-kasten h1:0IuykRy/KfqUttO2n6w3XTJzyFhSThXeE5UtKvstmYs= github.com/kastenhq/stow v0.1.1-kasten/go.mod h1:ABI2whmZOX25JbmbVuHRLFuPiGnv5lxXhduCtof7UHk= +github.com/kastenhq/stow v0.1.2-kasten h1:3msAbg6woEPWzYaBPmsOY4a0V55QosWD86XMOE+gDbM= +github.com/kastenhq/stow v0.1.2-kasten/go.mod h1:ABI2whmZOX25JbmbVuHRLFuPiGnv5lxXhduCtof7UHk= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -200,6 +202,7 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/softlayer/softlayer-go v0.0.0-20190615201252-ba6e7f295217 h1:MFHQI+AYM6otrSP+l3dLhE2DjrSr5HXfV4mt4M6pjPs= github.com/softlayer/softlayer-go v0.0.0-20190615201252-ba6e7f295217/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= @@ -268,6 +271,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/pkg/apis/cr/v1alpha1/types.go b/pkg/apis/cr/v1alpha1/types.go index ea8f19c913..6c0ab7446d 100644 --- a/pkg/apis/cr/v1alpha1/types.go +++ b/pkg/apis/cr/v1alpha1/types.go @@ -224,6 +224,7 @@ type Profile struct { Location Location `json:"location"` Credential Credential `json:"credential"` SkipSSLVerify bool `json:"skipSSLVerify"` + Role string `json:"role"` } // LocationType diff --git a/pkg/location/location.go b/pkg/location/location.go index fb5cdf55f1..629c2687aa 100644 --- a/pkg/location/location.go +++ b/pkg/location/location.go @@ -127,6 +127,7 @@ func getBucket(ctx context.Context, pType objectstore.ProviderType, profile para Type: pType, Endpoint: profile.Location.Endpoint, SkipSSLVerify: profile.SkipSSLVerify, + Role: profile.Role, } secret, err := getOSSecret(pType, profile.Credential) if err != nil { diff --git a/pkg/objectstore/aws.go b/pkg/objectstore/aws.go index ca08c38eb5..f7c534c579 100644 --- a/pkg/objectstore/aws.go +++ b/pkg/objectstore/aws.go @@ -17,9 +17,12 @@ package objectstore import ( "context" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -27,9 +30,10 @@ import ( ) const ( - bucketNotFound = "NotFound" - noSuchBucket = s3.ErrCodeNoSuchBucket - gcsS3NotFound = "not found" + bucketNotFound = "NotFound" + noSuchBucket = s3.ErrCodeNoSuchBucket + gcsS3NotFound = "not found" + awsTokenDuration = 90 * time.Minute ) func config(region string) *aws.Config { @@ -40,6 +44,31 @@ func config(region string) *aws.Config { return c } +type awsCreds struct { + accessKeyID string + secretAccessKey string + token string +} + +// switchRole changes the role using the credentials provider. +// It returns the new set of credentials. +func switchRole(awsAccessKeyID, awsSecretAccessKey, role string) (*awsCreds, error) { + creds := credentials.NewStaticCredentials(awsAccessKeyID, awsSecretAccessKey, role) + sess := session.New(aws.NewConfig().WithCredentials(creds)) + creds = stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) { + p.Duration = awsTokenDuration + }) + val, err := creds.Get() + if err != nil { + return nil, errors.Wrap(err, "Failed to get role credentials") + } + return &awsCreds{ + accessKeyID: val.AccessKeyID, + secretAccessKey: val.SecretAccessKey, + token: val.SessionToken, + }, nil +} + func IsBucketNotFoundError(err error) bool { if err == nil { return false diff --git a/pkg/objectstore/models.go b/pkg/objectstore/models.go index 001b66898f..cb27f9123b 100644 --- a/pkg/objectstore/models.go +++ b/pkg/objectstore/models.go @@ -25,6 +25,8 @@ type ProviderConfig struct { // If true, disable SSL verification. If false (the default), SSL // verification is enabled. SkipSSLVerify bool + // If given, the object store access will be established after switching to the role. + Role string } // SecretAws AWS keys diff --git a/pkg/objectstore/objectstore.go b/pkg/objectstore/objectstore.go index b1a7606e2d..582b4661c5 100644 --- a/pkg/objectstore/objectstore.go +++ b/pkg/objectstore/objectstore.go @@ -132,6 +132,18 @@ func s3Config(config ProviderConfig, secret *Secret, region string) (stowKind st stows3.ConfigAccessKeyID: awsAccessKeyID, stows3.ConfigSecretKey: awsSecretAccessKey, } + if config.Role != "" { + // Switch role and replace credentials. + creds, err := switchRole(awsAccessKeyID, awsSecretAccessKey, config.Role) + if err != nil { + return "", cm, errors.New("Failed to switch role") + } + cm = stow.ConfigMap{ + stows3.ConfigAccessKeyID: creds.accessKeyID, + stows3.ConfigSecretKey: creds.secretAccessKey, + stows3.ConfigToken: creds.token, + } + } if region != "" { cm[stows3.ConfigRegion] = region } diff --git a/pkg/objectstore/objectstore_test.go b/pkg/objectstore/objectstore_test.go index e2d201c1a8..572af067fd 100644 --- a/pkg/objectstore/objectstore_test.go +++ b/pkg/objectstore/objectstore_test.go @@ -38,6 +38,7 @@ type ObjectStoreProviderSuite struct { osType ProviderType provider Provider rand *rand.Rand + role string root Bucket // root of the default test bucket suiteDirPrefix string // directory name prefix for all tests in this suite testDir string // directory name for a given test @@ -48,9 +49,11 @@ type ObjectStoreProviderSuite struct { const ( testBucketName = "kio-store-tests" testRegionS3 = "us-west-2" + testRole = "" ) var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeS3, region: testRegionS3}) +var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeS3, region: testRegionS3, role: testRole}) var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeGCS, region: ""}) var _ = Suite(&ObjectStoreProviderSuite{osType: ProviderTypeAzure, region: ""}) @@ -72,7 +75,7 @@ func (s *ObjectStoreProviderSuite) SetUpSuite(c *C) { ctx := context.Background() s.rand = rand.New(rand.NewSource(time.Now().UnixNano())) - pc := ProviderConfig{Type: s.osType} + pc := ProviderConfig{Type: s.osType, Role: s.role} secret := getSecret(c, s.osType) s.provider, err = NewProvider(ctx, pc, secret) c.Check(err, IsNil) diff --git a/pkg/param/param.go b/pkg/param/param.go index 091870ca8e..398a458151 100644 --- a/pkg/param/param.go +++ b/pkg/param/param.go @@ -83,6 +83,7 @@ type Profile struct { Location crv1alpha1.Location Credential Credential SkipSSLVerify bool + Role string } // CredentialType diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go index b45539def5..897cde8a03 100644 --- a/pkg/validate/validate.go +++ b/pkg/validate/validate.go @@ -218,6 +218,7 @@ func ReadAccess(ctx context.Context, p *crv1alpha1.Profile, cli kubernetes.Inter Type: pType, Endpoint: p.Location.Endpoint, SkipSSLVerify: p.SkipSSLVerify, + Role: p.Role, } provider, err := objectstore.NewProvider(ctx, pc, secret) if err != nil { @@ -257,6 +258,7 @@ func WriteAccess(ctx context.Context, p *crv1alpha1.Profile, cli kubernetes.Inte Type: pType, Endpoint: p.Location.Endpoint, SkipSSLVerify: p.SkipSSLVerify, + Role: p.Role, } provider, err := objectstore.NewProvider(ctx, pc, secret) if err != nil {