Skip to content

Commit

Permalink
Support AWS_CA_BUNDLE when talking to the S3 API
Browse files Browse the repository at this point in the history
If you are running behind a MITM proxy, this allows for the AWS S3
driver to respect the AWS_CA_BUNDLE environment variable.
  • Loading branch information
kirat-singh committed Feb 14, 2023
1 parent cf87e8d commit bdbdfe8
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 16 deletions.
95 changes: 79 additions & 16 deletions registry/storage/driver/s3-aws/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ import (
"context"
"crypto/tls"
"errors"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"path/filepath"
"os"
"reflect"
"sort"
"strconv"
Expand Down Expand Up @@ -105,6 +108,7 @@ type DriverParameters struct {
KeyID string
Secure bool
SkipVerify bool
CaBundle string
V4Auth bool
ChunkSize int64
MultipartCopyChunkSize int64
Expand Down Expand Up @@ -287,6 +291,14 @@ func FromParameters(parameters map[string]interface{}) (*Driver, error) {
return nil, fmt.Errorf("the skipVerify parameter should be a boolean")
}

caBundle, present := os.LookupEnv("AWS_CA_BUNDLE")
if present {
// unset AWS_CA_BUNDLE so AWS session won't raise LoadCustomCABundleError
os.Unsetenv("AWS_CA_BUNDLE")
} else {
caBundle = ""
}

v4Bool := true
v4auth := parameters["v4auth"]
switch v4auth := v4auth.(type) {
Expand Down Expand Up @@ -447,6 +459,7 @@ func FromParameters(parameters map[string]interface{}) (*Driver, error) {
fmt.Sprint(keyID),
secureBool,
skipVerifyBool,
fmt.Sprint(caBundle),
v4Bool,
chunkSize,
multipartCopyChunkSize,
Expand Down Expand Up @@ -526,23 +539,11 @@ func New(params DriverParameters) (*Driver, error) {
awsConfig.UseDualStackEndpoint = endpoints.DualStackEndpointStateEnabled
}

if params.UserAgent != "" || params.SkipVerify {
httpTransport := http.DefaultTransport
if params.SkipVerify {
httpTransport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
if params.UserAgent != "" {
awsConfig.WithHTTPClient(&http.Client{
Transport: transport.NewTransport(httpTransport, transport.NewHeaderRequestModifier(http.Header{http.CanonicalHeaderKey("User-Agent"): []string{params.UserAgent}})),
})
} else {
awsConfig.WithHTTPClient(&http.Client{
Transport: transport.NewTransport(httpTransport),
})
}
httpClient, err := createHTTPClient(params.UserAgent, params.CaBundle, params.SkipVerify)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP client: %v", err)
}
awsConfig.WithHTTPClient(httpClient)

sess, err := session.NewSession(awsConfig)
if err != nil {
Expand Down Expand Up @@ -594,6 +595,68 @@ func New(params DriverParameters) (*Driver, error) {
}, nil
}

// createHTTPClient constructs a custom HTTP client with the given user agent, root CA,
// and skipped cert verification if specified
func createHTTPClient(userAgent, caBundle string, skipVerify bool) (*http.Client, error) {
if userAgent == "" && caBundle == "" && skipVerify == false {
// if nothing to do, just return nil so the default HTTP client will be used
return nil, nil
}

httpTransport := http.DefaultTransport

if caBundle != "" || skipVerify {
tlsConfig := &tls.Config{}
if caBundle != "" {
caCertPool, err := loadCustomCABundle(caBundle)
if err != nil {
return nil, err
}
tlsConfig.RootCAs = caCertPool
}
if skipVerify {
tlsConfig.InsecureSkipVerify = true
}
httpTransport = &http.Transport{TLSClientConfig: tlsConfig}
}

if userAgent != "" {
return &http.Client{
Transport: transport.NewTransport(
httpTransport,
transport.NewHeaderRequestModifier(http.Header{http.CanonicalHeaderKey("User-Agent"): []string{userAgent}})),
}, nil
} else {
return &http.Client{
Transport: transport.NewTransport(httpTransport),
}, nil
}
}

func loadCustomCABundle(path string) (*x509.CertPool, error) {
fp, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open custom CA bundle PEM file, %w", err)
}
defer fp.Close()

return loadCertPool(fp)
}

func loadCertPool(r io.Reader) (*x509.CertPool, error) {
rawCertBundle, err := ioutil.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("failed to read custom CA bundle PEM file, %w", err)
}

certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(rawCertBundle) {
return nil, fmt.Errorf("failed to load custom CA bundle PEM file")
}

return certPool, nil
}

// Implement the storagedriver.StorageDriver interface

func (d *driver) Name() string {
Expand Down
2 changes: 2 additions & 0 deletions registry/storage/driver/s3-aws/s3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func init() {
keyID = os.Getenv("S3_KEY_ID")
secure = os.Getenv("S3_SECURE")
skipVerify = os.Getenv("S3_SKIP_VERIFY")
caBundle = os.Getenv("AWS_CA_BUNDLE")
v4Auth = os.Getenv("S3_V4_AUTH")
region = os.Getenv("AWS_REGION")
objectACL = os.Getenv("S3_OBJECT_ACL")
Expand Down Expand Up @@ -129,6 +130,7 @@ func init() {
keyID,
secureBool,
skipVerifyBool,
caBundle,
v4Bool,
minChunkSize,
defaultMultipartCopyChunkSize,
Expand Down

0 comments on commit bdbdfe8

Please sign in to comment.