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.
This commit is contained in:
Gavin Williams 2017-04-15 15:11:28 +01:00 committed by Paul Stack
parent 1a016ac37f
commit c63ad9c0f8
4 changed files with 56 additions and 17 deletions

View File

@ -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,
},
}

View File

@ -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

View File

@ -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
}

View File

@ -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 {