Skip to content

Commit

Permalink
Add proxy support (#95)
Browse files Browse the repository at this point in the history
* Add proxy support

Add a new optional parameter to the provider configuration to allow for
setting a proxy.  Using a proxy can be an easier method for connecting
to clusters within a VPC.

Signed-off-by: Tim Wisbauer <[email protected]>

* Add proxy support docs

Signed-off-by: Tim Wisbauer <[email protected]>

---------

Signed-off-by: Tim Wisbauer <[email protected]>
  • Loading branch information
timwisbauer-contsec authored Nov 7, 2023
1 parent 4de3492 commit 97c7627
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ EOF
- `insecure` (Boolean) Disable SSL verification of API calls
- `opensearch_version` (String) opensearch Version
- `password` (String) Password to use to connect to opensearch using basic auth
- `proxy` (String) Proxy URL to use for requests to opensearch.
- `sign_aws_requests` (Boolean) Enable signing of AWS opensearch requests. The `url` must refer to AWS ES domain (`*.<region>.es.amazonaws.com`), or `aws_region` must be specified explicitly.
- `sniff` (Boolean) Set the node sniffing option for the opensearch client. Client won't work with sniffing if nodes are not routable.
- `token` (String) A bearer token or ApiKey for an Authorization header, e.g. Active Directory API key.
Expand Down
51 changes: 42 additions & 9 deletions provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type ProviderConf struct {
certPemPath string
keyPemPath string
hostOverride string
proxy string
// determined after connecting to the server
flavor ServerFlavor
}
Expand Down Expand Up @@ -214,6 +215,11 @@ func Provider() *schema.Provider {
Default: "",
Description: "If provided, sets the 'Host' header of requests and the 'ServerName' for certificate validation to this value. See the documentation on connecting to opensearch via an SSH tunnel.",
},
"proxy": {
Type: schema.TypeString,
Optional: true,
Description: "Proxy URL to use for requests to opensearch.",
},
},

ResourcesMap: map[string]*schema.Resource{
Expand Down Expand Up @@ -280,6 +286,7 @@ func providerConfigure(c context.Context, d *schema.ResourceData) (interface{},
certPemPath: d.Get("client_cert_path").(string),
keyPemPath: d.Get("client_key_path").(string),
hostOverride: d.Get("host_override").(string),
proxy: d.Get("proxy").(string),
}, nil
}

Expand Down Expand Up @@ -457,20 +464,18 @@ func awsSession(region string, conf *ProviderConf, endpoint string) *awssession.
sessOpts.Profile = conf.awsProfile
}

transport := http.Transport{}
// If configured as insecure, turn off SSL verification
if conf.insecure {
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
sessOpts.Config.HTTPClient = client
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else if conf.hostOverride != "" {
// Only use `host_override` to set `ServerName` if we're using a secure connection
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{ServerName: conf.hostOverride},
}}
sessOpts.Config.HTTPClient = client
transport.TLSClientConfig = &tls.Config{ServerName: conf.hostOverride}
}

client := &http.Client{Transport: &transport}
sessOpts.Config.HTTPClient = client

return awssession.Must(awssession.NewSessionWithOptions(sessOpts))
}

Expand All @@ -482,6 +487,17 @@ func awsHttpClient(region string, conf *ProviderConf, headers map[string]string)
if err != nil {
return nil, err
}

// Set the proxy URL after configuring AWS credentials since the proxy
// should be not used for credential sources that call a URL like ECS Task
// Roles or EC2 Instance Roles.
if conf.proxy != "" {
proxyURL, _ := url.Parse(conf.proxy)
transport, _ := session.Config.HTTPClient.Transport.(*http.Transport)
transport.Proxy = http.ProxyURL(proxyURL)
session.Config.HTTPClient.Transport = transport
}

signer := awssigv4.NewSigner(session.Config.Credentials)
client, err := aws_signing_client.New(signer, session.Config.HTTPClient, conf.awsSig4Service, region)
if err != nil {
Expand Down Expand Up @@ -509,6 +525,12 @@ func tokenHttpClient(conf *ProviderConf, headers map[string]string) *http.Client

// Wrapper to inject headers as needed
transport := &http.Transport{TLSClientConfig: tlsConfig}
// Configure a proxy URL if one is provided.
if conf.proxy != "" {
proxyURL, _ := url.Parse(conf.proxy)
transport.Proxy = http.ProxyURL(proxyURL)
}

rt := WithHeader(transport)
rt.hostOverride = conf.hostOverride
rt.Set("Authorization", fmt.Sprintf("%s %s", conf.tokenName, conf.token))
Expand Down Expand Up @@ -557,6 +579,11 @@ func tlsHttpClient(conf *ProviderConf, headers map[string]string) *http.Client {
}

transport := &http.Transport{TLSClientConfig: tlsConfig}
// Configure a proxy URL if one is provided.
if conf.proxy != "" {
proxyURL, _ := url.Parse(conf.proxy)
transport.Proxy = http.ProxyURL(proxyURL)
}

rt := WithHeader(transport)
rt.hostOverride = conf.hostOverride
Expand All @@ -578,8 +605,14 @@ func defaultHttpClient(conf *ProviderConf, headers map[string]string) *http.Clie
tlsConfig.ServerName = conf.hostOverride
}

// Wrapper to inject headers as needed
transport := &http.Transport{TLSClientConfig: tlsConfig}
// Configure a proxy URL if one is provided.
if conf.proxy != "" {
proxyURL, _ := url.Parse(conf.proxy)
transport.Proxy = http.ProxyURL(proxyURL)
}

// Wrapper to inject headers as needed
rt := WithHeader(transport)
rt.hostOverride = conf.hostOverride
for k, v := range headers {
Expand Down
23 changes: 23 additions & 0 deletions provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,29 @@ func getCreds(t *testing.T, region string, config *ProviderConf, endpoint string
return creds
}

// Given:
// 1. A proxy URL is specified.
// 2. No additional AWS configuration is provided to the provider
//
// This tests that: the proxy value is set for the transport. Note we cannot get the credentials, because that requires connecting to AWS.
func TestAWSSocksProxy(t *testing.T) {
testRegion := "us-east-1"

testConfig := map[string]interface{}{
"proxy": "socks://127.0.0.1:8080",
}

testConfigData := schema.TestResourceDataRaw(t, Provider().Schema, testConfig)

conf := &ProviderConf{
proxy: testConfigData.Get("proxy").(string),
}
s := awsSession(testRegion, conf, "")
if s == nil {
t.Fatalf("awsSession returned nil")
}
}

type mockServer struct {
ResponseFixturePath string
ExpectedAccessKeyId string
Expand Down

0 comments on commit 97c7627

Please sign in to comment.