From c63ad9c0f857a8d82e6154aa8fc4fbfb87ceb5f4 Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Sat, 15 Apr 2017 15:11:28 +0100 Subject: [PATCH] state/remote/swift: Support Openstack request logging (#13583) * provider/openstack: Expose LogRoundTripper fields externally * state/remote/swift: Add support for debugging Openstack calls using OS_DEBUG env variable. * provider/openstack: Update LogRoundTripper to log headers aswell as body. * Add `RedactHeaders` function in order to redact sensitive http Headers. Refactor `logRequest` and `logResponse` to use `RedactHeaders` func. --- builtin/providers/openstack/config.go | 4 ++-- builtin/providers/openstack/types.go | 33 +++++++++++++++------------ builtin/providers/openstack/util.go | 22 ++++++++++++++++++ state/remote/swift.go | 14 +++++++++++- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index 651b667835..0c6cb89950 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -113,8 +113,8 @@ func (c *Config) loadAndValidate() error { transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config} client.HTTPClient = http.Client{ Transport: &LogRoundTripper{ - rt: transport, - osDebug: osDebug, + Rt: transport, + OsDebug: osDebug, }, } diff --git a/builtin/providers/openstack/types.go b/builtin/providers/openstack/types.go index 9c6a4f67ee..c6c6a268a8 100644 --- a/builtin/providers/openstack/types.go +++ b/builtin/providers/openstack/types.go @@ -24,8 +24,8 @@ import ( // LogRoundTripper satisfies the http.RoundTripper interface and is used to // customize the default http client RoundTripper to allow for logging. type LogRoundTripper struct { - rt http.RoundTripper - osDebug bool + Rt http.RoundTripper + OsDebug bool } // RoundTrip performs a round-trip HTTP request and logs relevant information about it. @@ -37,36 +37,36 @@ func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, er }() // for future reference, this is how to access the Transport struct: - //tlsconfig := lrt.rt.(*http.Transport).TLSClientConfig + //tlsconfig := lrt.Rt.(*http.Transport).TLSClientConfig var err error - if lrt.osDebug { + if lrt.OsDebug { log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL) if request.Body != nil { - request.Body, err = lrt.logRequestBody(request.Body, request.Header) + request.Body, err = lrt.logRequest(request.Body, request.Header) if err != nil { return nil, err } } } - response, err := lrt.rt.RoundTrip(request) + response, err := lrt.Rt.RoundTrip(request) if response == nil { return nil, err } - if lrt.osDebug { - response.Body, err = lrt.logResponseBody(response.Body, response.Header) + if lrt.OsDebug { + response.Body, err = lrt.logResponse(response.Body, response.Header) } return response, err } -// logRequestBody will log the HTTP Request body. +// logRequest will log the HTTP Request details. // If the body is JSON, it will attempt to be pretty-formatted. -func (lrt *LogRoundTripper) logRequestBody(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { +func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { defer original.Close() var bs bytes.Buffer @@ -75,20 +75,25 @@ func (lrt *LogRoundTripper) logRequestBody(original io.ReadCloser, headers http. return nil, err } + log.Printf("[DEBUG] Openstack Request headers:\n%s", strings.Join(RedactHeaders(headers), "\n")) + + // Handle request contentType contentType := headers.Get("Content-Type") if strings.HasPrefix(contentType, "application/json") { debugInfo := lrt.formatJSON(bs.Bytes()) - log.Printf("[DEBUG] OpenStack Request Options: %s", debugInfo) + log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) } else { - log.Printf("[DEBUG] OpenStack Request Options: %s", bs.String()) + log.Printf("[DEBUG] OpenStack Request Body: %s", bs.String()) } return ioutil.NopCloser(strings.NewReader(bs.String())), nil } -// logResponseBody will log the HTTP Response body. +// logResponse will log the HTTP Response details. // If the body is JSON, it will attempt to be pretty-formatted. -func (lrt *LogRoundTripper) logResponseBody(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { +func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, headers http.Header) (io.ReadCloser, error) { + log.Printf("[DEBUG] Openstack Response headers:\n%s", strings.Join(RedactHeaders(headers), "\n")) + contentType := headers.Get("Content-Type") if strings.HasPrefix(contentType, "application/json") { var bs bytes.Buffer diff --git a/builtin/providers/openstack/util.go b/builtin/providers/openstack/util.go index 70fb8914f7..eb5ccdf090 100644 --- a/builtin/providers/openstack/util.go +++ b/builtin/providers/openstack/util.go @@ -2,8 +2,10 @@ package openstack import ( "fmt" + "net/http" "os" + "github.com/Unknwon/com" "github.com/gophercloud/gophercloud" "github.com/hashicorp/terraform/helper/schema" ) @@ -58,3 +60,23 @@ func MapValueSpecs(d *schema.ResourceData) map[string]string { } return m } + +// List of headers that need to be redacted +var REDACT_HEADERS = []string{"x-auth-token", "x-auth-key", "x-service-token", + "x-storage-token", "x-account-meta-temp-url-key", "x-account-meta-temp-url-key-2", + "x-container-meta-temp-url-key", "x-container-meta-temp-url-key-2", "set-cookie", + "x-subject-token"} + +// RedactHeaders processes a headers object, returning a redacted list +func RedactHeaders(headers http.Header) (processedHeaders []string) { + for name, header := range headers { + for _, v := range header { + if com.IsSliceContainsStr(REDACT_HEADERS, name) { + processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, "***")) + } else { + processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, v)) + } + } + } + return +} diff --git a/state/remote/swift.go b/state/remote/swift.go index 6fe490d405..2f12aa4e76 100644 --- a/state/remote/swift.go +++ b/state/remote/swift.go @@ -18,6 +18,7 @@ import ( "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" + tf_openstack "github.com/hashicorp/terraform/builtin/providers/openstack" ) const TFSTATE_NAME = "tfstate.tf" @@ -249,8 +250,19 @@ func (c *SwiftClient) validateConfig(conf map[string]string) (err error) { config.BuildNameToCertificate() } + // if OS_DEBUG is set, log the requests and responses + var osDebug bool + if os.Getenv("OS_DEBUG") != "" { + osDebug = true + } + transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config} - provider.HTTPClient.Transport = transport + provider.HTTPClient = http.Client{ + Transport: &tf_openstack.LogRoundTripper{ + Rt: transport, + OsDebug: osDebug, + }, + } err = openstack.Authenticate(provider, ao) if err != nil {