github.com/rudderlabs/rudder-go-kit@v0.30.0/awsutil/awsutil.go (about)

     1  package awsutil
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/credentials"
    12  	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
    13  	"github.com/aws/aws-sdk-go/aws/session"
    14  	"github.com/mitchellh/mapstructure"
    15  )
    16  
    17  // Some AWS destinations are using SecretAccessKey instead of accessKey
    18  type SessionConfig struct {
    19  	Region           string         `mapstructure:"region"`
    20  	AccessKeyID      string         `mapstructure:"accessKeyID"`
    21  	AccessKey        string         `mapstructure:"accessKey"`
    22  	SecretAccessKey  string         `mapstructure:"secretAccessKey"`
    23  	RoleBasedAuth    bool           `mapstructure:"roleBasedAuth"`
    24  	IAMRoleARN       string         `mapstructure:"iamRoleARN"`
    25  	ExternalID       string         `mapstructure:"externalID"`
    26  	WorkspaceID      string         `mapstructure:"workspaceID"`
    27  	Endpoint         *string        `mapstructure:"endpoint"`
    28  	S3ForcePathStyle *bool          `mapstructure:"s3ForcePathStyle"`
    29  	DisableSSL       *bool          `mapstructure:"disableSSL"`
    30  	Service          string         `mapstructure:"service"`
    31  	Timeout          *time.Duration `mapstructure:"timeout"`
    32  }
    33  
    34  // CreateSession creates a new AWS session using the provided config
    35  func CreateSession(config *SessionConfig) (*session.Session, error) {
    36  	var (
    37  		awsCredentials *credentials.Credentials
    38  		err            error
    39  	)
    40  	if config.RoleBasedAuth {
    41  		awsCredentials, err = createCredentialsForRole(config)
    42  	} else if config.AccessKey != "" && config.AccessKeyID != "" {
    43  		awsCredentials, err = credentials.NewStaticCredentials(config.AccessKeyID, config.AccessKey, ""), nil
    44  	}
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return session.NewSession(&aws.Config{
    49  		HTTPClient:                    getHttpClient(config),
    50  		Region:                        aws.String(config.Region),
    51  		CredentialsChainVerboseErrors: aws.Bool(true),
    52  		Credentials:                   awsCredentials,
    53  		Endpoint:                      config.Endpoint,
    54  		S3ForcePathStyle:              config.S3ForcePathStyle,
    55  		DisableSSL:                    config.DisableSSL,
    56  	})
    57  }
    58  
    59  // NewSimpleSessionConfig creates a new session config using the provided config map
    60  func NewSimpleSessionConfig(config map[string]interface{}, serviceName string) (*SessionConfig, error) {
    61  	if config == nil {
    62  		return nil, errors.New("config should not be nil")
    63  	}
    64  	sessionConfig := SessionConfig{}
    65  	if err := mapstructure.Decode(config, &sessionConfig); err != nil {
    66  		return nil, fmt.Errorf("unable to populate session config using destinationConfig: %w", err)
    67  	}
    68  
    69  	if !isRoleBasedAuthFieldExist(config) {
    70  		sessionConfig.RoleBasedAuth = sessionConfig.IAMRoleARN != ""
    71  	}
    72  
    73  	if sessionConfig.IAMRoleARN == "" {
    74  		sessionConfig.RoleBasedAuth = false
    75  	}
    76  
    77  	// Some AWS destinations are using SecretAccessKey instead of accessKey
    78  	if sessionConfig.SecretAccessKey != "" {
    79  		sessionConfig.AccessKey = sessionConfig.SecretAccessKey
    80  	}
    81  	sessionConfig.Service = serviceName
    82  	return &sessionConfig, nil
    83  }
    84  
    85  func getHttpClient(config *SessionConfig) *http.Client {
    86  	var httpClient *http.Client
    87  	if config.Timeout != nil {
    88  		httpClient = &http.Client{
    89  			Timeout: *config.Timeout,
    90  		}
    91  	}
    92  	return httpClient
    93  }
    94  
    95  func createDefaultSession(config *SessionConfig) (*session.Session, error) {
    96  	return session.NewSession(&aws.Config{
    97  		HTTPClient: getHttpClient(config),
    98  		Region:     aws.String(config.Region),
    99  	})
   100  }
   101  
   102  func createRoleSessionName(serviceName string) string {
   103  	return fmt.Sprintf("rudderstack-aws-%s-access", strings.ToLower(strings.ReplaceAll(serviceName, " ", "-")))
   104  }
   105  
   106  func createCredentialsForRole(config *SessionConfig) (*credentials.Credentials, error) {
   107  	if config.ExternalID == "" {
   108  		return nil, errors.New("externalID is required for IAM role")
   109  	}
   110  	hostSession, err := createDefaultSession(config)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	return stscreds.NewCredentials(hostSession, config.IAMRoleARN,
   115  		func(p *stscreds.AssumeRoleProvider) {
   116  			p.ExternalID = aws.String(config.ExternalID)
   117  			p.RoleSessionName = createRoleSessionName(config.Service)
   118  		}), err
   119  }
   120  
   121  func isRoleBasedAuthFieldExist(config map[string]interface{}) bool {
   122  	_, ok := config["roleBasedAuth"].(bool)
   123  	return ok
   124  }