aws/pkg/cloud/aws/session/builder.go (130 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package session
import (
"errors"
"fmt"
"net/http"
"net/url"
"path/filepath"
"strings"
"time"
"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/ec2metadata"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/session"
)
// NewSession creates an AWS session to use AWS Services or compatible.
func NewSession(opts *Options) (*session.Session, error) {
// Check arguments
if opts == nil {
return nil, errors.New("unable to build without options")
}
if opts.Region == "" {
opts.Region = "us-east-1"
}
// Start a new AWS session
awsSession, err := session.NewSession()
if err != nil {
return nil, fmt.Errorf("unable to initialize AWS session: %w", err)
}
// Prepare credential providers
providers := []credentials.Provider{}
if opts.AccessKeyID != "" && opts.SecretAccessKey != "" {
providers = append(providers, &credentials.StaticProvider{
Value: credentials.Value{
AccessKeyID: opts.AccessKeyID,
SecretAccessKey: opts.SecretAccessKey,
SessionToken: opts.SessionToken,
},
})
}
if !opts.IgnoreEnvCreds {
providers = append(providers, &credentials.EnvProvider{})
}
if !opts.IgnoreConfigCreds {
providers = append(providers, &credentials.SharedCredentialsProvider{})
}
if !opts.IgnoreEC2RoleCreds {
providers = append(providers, &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(awsSession, &aws.Config{
HTTPClient: &http.Client{Timeout: 1 * time.Second},
}),
ExpiryWindow: 2 * time.Minute,
})
}
// Assemble credentials
creds := credentials.NewChainCredentials(providers)
// Prepare config
config := aws.Config{
Credentials: creds,
DisableSSL: aws.Bool(opts.DisableSSL),
S3ForcePathStyle: aws.Bool(opts.S3ForcePathStyle),
S3UseAccelerate: aws.Bool(opts.UseAccelerateEndpoint),
S3UsEast1RegionalEndpoint: endpoints.RegionalS3UsEast1Endpoint,
}
if opts.Endpoint != "" {
config.Endpoint = aws.String(opts.Endpoint)
}
if opts.Region != "" {
config.Region = aws.String(opts.Region)
}
// Prepare options
awsSessionOpts := session.Options{
Config: config,
}
if opts.EnvAuthentication && opts.AccessKeyID == "" && opts.SecretAccessKey == "" {
awsSessionOpts.SharedConfigState = session.SharedConfigEnable
awsSessionOpts.Config.Credentials = nil
}
if opts.Profile != "" {
awsSessionOpts.Profile = opts.Profile
}
// Build session
return session.NewSessionWithOptions(awsSessionOpts)
}
// FromURL parses the given url to build a session object.
func FromURL(u string) (*Options, error) {
// Parse input as URL
input, err := url.Parse(u)
if err != nil {
return nil, fmt.Errorf("unable to build session from url")
}
// Extract path and object key
bucketName, objectKey := filepath.Split(input.Path)
if bucketName == "" {
return nil, fmt.Errorf("bucketName is mandatory")
}
if objectKey == "" {
return nil, fmt.Errorf("objectKey is mandatory")
}
q := input.Query()
// Unescape strings
accessKeyID, err := url.QueryUnescape(q.Get("access-key-id"))
if err != nil {
return nil, fmt.Errorf("accessKeyID value is invalid: %w", err)
}
profile, err := url.QueryUnescape(q.Get("profile"))
if err != nil {
return nil, fmt.Errorf("profile value is invalid: %w", err)
}
region, err := url.QueryUnescape(q.Get("region"))
if err != nil {
return nil, fmt.Errorf("region value is invalid: %w", err)
}
secretAccessKey, err := url.QueryUnescape(q.Get("secret-access-key"))
if err != nil {
return nil, fmt.Errorf("secret-access-key value is invalid: %w", err)
}
sessionToken, err := url.QueryUnescape(q.Get("session-token"))
if err != nil {
return nil, fmt.Errorf("session-token value is invalid: %w", err)
}
const trueStringValue = "true"
// Assemble options
opts := &Options{
Endpoint: input.Host,
BucketName: strings.TrimSuffix(strings.TrimPrefix(bucketName, "/"), "/"),
ObjectKey: objectKey,
// Query params
AccessKeyID: accessKeyID,
DisableSSL: q.Get("disable-ssl") == trueStringValue,
EnvAuthentication: q.Get("env-authentication") == trueStringValue,
IgnoreConfigCreds: q.Get("ignore-config-creds") == trueStringValue,
IgnoreEC2RoleCreds: q.Get("ignore-ec2role-creds") == trueStringValue,
IgnoreEnvCreds: q.Get("ignore-env-creds") == trueStringValue,
Profile: profile,
Region: region,
S3ForcePathStyle: q.Get("s3-force-path-style") == trueStringValue,
SecretAccessKey: secretAccessKey,
SessionToken: sessionToken,
UseAccelerateEndpoint: q.Get("s3-use-accelerate-endpoint") == trueStringValue,
}
// Delegate to builder
return opts, nil
}