2020-12-16 17:16:55 +00:00
|
|
|
package s3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-11-15 14:11:38 +00:00
|
|
|
"fmt"
|
|
|
|
|
2020-12-16 17:16:55 +00:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
2022-07-15 15:20:24 +00:00
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
2020-12-16 17:16:55 +00:00
|
|
|
"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"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/config"
|
2020-12-16 17:16:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const awsRegionHint = "us-east-1"
|
|
|
|
|
2022-07-14 17:14:24 +00:00
|
|
|
type s3store struct {
|
|
|
|
s3client *s3.S3
|
|
|
|
bucket string
|
|
|
|
prefix string
|
2020-12-16 17:16:55 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 17:14:24 +00:00
|
|
|
// newS3store initializes an S3 Datastore
|
|
|
|
func newS3store(config config.S3Config) (*s3store, error) {
|
2020-12-16 17:16:55 +00:00
|
|
|
conf := &aws.Config{}
|
|
|
|
|
|
|
|
// Use default auth provire if no static credentials were provided
|
|
|
|
if config.AccessKeyID != "" && config.SecretAccessKey != "" {
|
|
|
|
conf.Credentials = credentials.NewStaticCredentials(
|
|
|
|
config.AccessKeyID,
|
|
|
|
config.SecretAccessKey,
|
|
|
|
"",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:06 +00:00
|
|
|
if config.EndpointURL != "" {
|
|
|
|
conf.Endpoint = &config.EndpointURL
|
|
|
|
}
|
|
|
|
|
|
|
|
conf.DisableSSL = &config.DisableSSL
|
|
|
|
conf.S3ForcePathStyle = &config.ForceS3PathStyle
|
|
|
|
|
2020-12-16 17:16:55 +00:00
|
|
|
sess, err := session.NewSession(conf)
|
|
|
|
if err != nil {
|
2021-11-15 14:11:38 +00:00
|
|
|
return nil, fmt.Errorf("create S3 client: %w", err)
|
2020-12-16 17:16:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Assume role if configured
|
|
|
|
if config.StsAssumeRoleArn != "" {
|
|
|
|
stscreds.NewCredentials(sess, config.StsAssumeRoleArn)
|
|
|
|
creds := stscreds.NewCredentials(sess, config.StsAssumeRoleArn)
|
|
|
|
conf.Credentials = creds
|
|
|
|
sess, err = session.NewSession(conf)
|
|
|
|
if err != nil {
|
2021-11-15 14:11:38 +00:00
|
|
|
return nil, fmt.Errorf("create S3 client: %w", err)
|
2020-12-16 17:16:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-12 19:32:06 +00:00
|
|
|
if len(config.Region) == 0 {
|
|
|
|
region, err := s3manager.GetBucketRegion(context.TODO(), sess, config.Bucket, awsRegionHint)
|
|
|
|
if err != nil {
|
2021-11-15 14:11:38 +00:00
|
|
|
return nil, fmt.Errorf("create S3 client: %w", err)
|
2021-10-12 19:32:06 +00:00
|
|
|
}
|
|
|
|
config.Region = region
|
2020-12-16 17:16:55 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 17:14:24 +00:00
|
|
|
return &s3store{
|
|
|
|
s3client: s3.New(sess, &aws.Config{Region: &config.Region}),
|
|
|
|
bucket: config.Bucket,
|
|
|
|
prefix: config.Prefix,
|
2020-12-16 17:16:55 +00:00
|
|
|
}, nil
|
|
|
|
}
|
2022-07-15 15:20:24 +00:00
|
|
|
|
|
|
|
// CreateTestBucket creates a bucket with the provided name and a default
|
|
|
|
// bucket config. Only recommended for local testing.
|
|
|
|
func (s *s3store) CreateTestBucket(name string) error {
|
|
|
|
_, err := s.s3client.CreateBucket(&s3.CreateBucketInput{
|
|
|
|
Bucket: &name,
|
|
|
|
CreateBucketConfiguration: &s3.CreateBucketConfiguration{},
|
|
|
|
})
|
|
|
|
|
|
|
|
// Don't error if the bucket already exists
|
|
|
|
if aerr, ok := err.(awserr.Error); ok {
|
|
|
|
switch aerr.Code() {
|
|
|
|
case s3.ErrCodeBucketAlreadyExists, s3.ErrCodeBucketAlreadyOwnedByYou:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|