diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 374c63acdb05..8164397c26eb 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -14,6 +14,7 @@ - Default to port 80 and 443 for Kibana and Fleet Server connections. {pull}25723[25723] - Remove deprecated/undocumented IncludeCreatorMetadata setting from kubernetes metadata config options {pull}28006[28006] - The `/processes/` endpoint proxies to the subprocess's monitoring endpoint, instead of querying its `/stats` endpoint {pull}28165[28165] +- Remove username/password for fleet-server authentication. {pull}29458[29458] ==== Bugfixes - Fix rename *ConfigChange to *PolicyChange to align on changes in the UI. {pull}20779[20779] diff --git a/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl b/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl index e8f4c31e8e18..bbadcdc1055f 100644 --- a/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl +++ b/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl @@ -5,8 +5,9 @@ outputs: default: type: elasticsearch hosts: [127.0.0.1:9200] - username: elastic - password: changeme + api-key: "example-key" + # username: "elastic" + # password: "changeme" inputs: - type: system/metrics @@ -74,8 +75,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "example-token" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl b/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl index 8a3ef0773578..bfb84102e3ca 100644 --- a/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl +++ b/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl @@ -5,8 +5,9 @@ outputs: default: type: elasticsearch hosts: [127.0.0.1:9200] - username: elastic - password: changeme + api-key: "example-key" + # username: "elastic" + # password: "changeme" inputs: - type: system/metrics @@ -43,8 +44,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "example-token" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl b/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl index 17201aa6dcea..b039db330912 100644 --- a/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl +++ b/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl @@ -43,8 +43,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "${FLEET_SERVER_SERVICE_TOKEN}" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/_meta/elastic-agent.yml b/x-pack/elastic-agent/_meta/elastic-agent.yml index 493887180138..7c24af477d24 100644 --- a/x-pack/elastic-agent/_meta/elastic-agent.yml +++ b/x-pack/elastic-agent/_meta/elastic-agent.yml @@ -43,8 +43,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "example-token" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/elastic-agent.docker.yml b/x-pack/elastic-agent/elastic-agent.docker.yml index b7d5ff2017ea..91148cee08e6 100644 --- a/x-pack/elastic-agent/elastic-agent.docker.yml +++ b/x-pack/elastic-agent/elastic-agent.docker.yml @@ -43,8 +43,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "${FLEET_SERVER_SERVICE_TOKEN}" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/elastic-agent.reference.yml b/x-pack/elastic-agent/elastic-agent.reference.yml index da04df95ea81..7770b036dbaa 100644 --- a/x-pack/elastic-agent/elastic-agent.reference.yml +++ b/x-pack/elastic-agent/elastic-agent.reference.yml @@ -11,8 +11,9 @@ outputs: default: type: elasticsearch hosts: [127.0.0.1:9200] - username: elastic - password: changeme + api-key: "example-key" + # username: "elastic" + # password: "changeme" inputs: - type: system/metrics @@ -49,8 +50,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "example-token" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/elastic-agent.yml b/x-pack/elastic-agent/elastic-agent.yml index 802df992ba7c..d2cfa19d3847 100644 --- a/x-pack/elastic-agent/elastic-agent.yml +++ b/x-pack/elastic-agent/elastic-agent.yml @@ -11,8 +11,9 @@ outputs: default: type: elasticsearch hosts: [127.0.0.1:9200] - username: elastic - password: changeme + api-key: "example-key" + # username: "elastic" + # password: "changeme" inputs: - type: system/metrics @@ -80,8 +81,7 @@ inputs: # # optional values # #protocol: "https" -# #username: "elastic" -# #password: "changeme" +# #service_token: "example-token" # #path: "" # #ssl.verification_mode: full # #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] diff --git a/x-pack/elastic-agent/pkg/agent/cmd/container.go b/x-pack/elastic-agent/pkg/agent/cmd/container.go index d72b0128430e..f127d972bf10 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/container.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/container.go @@ -79,8 +79,6 @@ The following actions are possible and grouped based on the actions. The following vars are need in the scenario that Elastic Agent should automatically fetch its own token. KIBANA_FLEET_HOST - kibana host to enable create enrollment token on [$KIBANA_HOST] - KIBANA_FLEET_USERNAME - kibana username to create enrollment token [$KIBANA_USERNAME] - KIBANA_FLEET_PASSWORD - kibana password to create enrollment token [$KIBANA_PASSWORD] FLEET_TOKEN_NAME - token name to use for fetching token from Kibana. This requires Kibana configs to be set. FLEET_TOKEN_POLICY_NAME - token policy name to use for fetching token from Kibana. This requires Kibana configs to be set. @@ -93,8 +91,6 @@ The following actions are possible and grouped based on the actions. FLEET_SERVER_ENABLE - set to 1 enables bootstrapping of Fleet Server inside Elastic Agent (forces FLEET_ENROLL enabled) FLEET_SERVER_ELASTICSEARCH_HOST - elasticsearch host for Fleet Server to communicate with [$ELASTICSEARCH_HOST] - FLEET_SERVER_ELASTICSEARCH_USERNAME - elasticsearch username for Fleet Server [$ELASTICSEARCH_USERNAME] - FLEET_SERVER_ELASTICSEARCH_PASSWORD - elasticsearch password for Fleet Server [$ELASTICSEARCH_PASSWORD] FLEET_SERVER_ELASTICSEARCH_CA - path to certificate authority to use with communicate with elasticsearch [$ELASTICSEARCH_CA] FLEET_SERVER_ELASTICSEARCH_CA_TRUSTED_FINGERPRINT - The sha-256 fingerprint value of the certificate authority to trust FLEET_SERVER_ELASTICSEARCH_INSECURE - disables cert validation for communication with Elasticsearch @@ -113,8 +109,6 @@ The following actions are possible and grouped based on the actions. KIBANA_FLEET_SETUP - set to 1 enables the setup of Fleet in Kibana by Elastic Agent. This was previously FLEET_SETUP. KIBANA_FLEET_HOST - Kibana host accessible from fleet-server. [$KIBANA_HOST] - KIBANA_FLEET_USERNAME - kibana username to enable Fleet [$KIBANA_USERNAME] - KIBANA_FLEET_PASSWORD - kibana password to enable Fleet [$KIBANA_PASSWORD] KIBANA_FLEET_CA - path to certificate authority to use with communicate with Kibana [$KIBANA_CA] KIBANA_REQUEST_RETRY_SLEEP - specifies sleep duration taken when agent performs a request to kibana [default 1s] KIBANA_REQUEST_RETRY_COUNT - specifies number of retries agent performs when executing a request to kibana [default 30] @@ -123,12 +117,8 @@ The following environment variables are provided as a convenience to prevent a l be used when the same credentials will be used across all the possible actions above. ELASTICSEARCH_HOST - elasticsearch host [http://elasticsearch:9200] - ELASTICSEARCH_USERNAME - elasticsearch username [elastic] - ELASTICSEARCH_PASSWORD - elasticsearch password [changeme] ELASTICSEARCH_CA - path to certificate authority to use with communicate with elasticsearch KIBANA_HOST - kibana host [http://kibana:5601] - KIBANA_USERNAME - kibana username [$ELASTICSEARCH_USERNAME] - KIBANA_PASSWORD - kibana password [$ELASTICSEARCH_PASSWORD] KIBANA_CA - path to certificate authority to use with communicate with Kibana [$ELASTICSEARCH_CA] @@ -427,10 +417,7 @@ func buildFleetServerConnStr(cfg fleetServerConfig) (string, error) { if u.Path != "" { path += "/" + strings.TrimLeft(u.Path, "/") } - if cfg.Elasticsearch.ServiceToken != "" { - return fmt.Sprintf("%s://%s%s", u.Scheme, u.Host, path), nil - } - return fmt.Sprintf("%s://%s:%s@%s%s", u.Scheme, cfg.Elasticsearch.Username, cfg.Elasticsearch.Password, u.Host, path), nil + return fmt.Sprintf("%s://%s%s", u.Scheme, u.Host, path), nil } func kibanaSetup(cfg setupConfig, client *kibana.Client, streams *cli.IOStreams) error { @@ -485,8 +472,6 @@ func kibanaClient(cfg kibanaConfig, headers map[string]string) (*kibana.Client, return kibana.NewClientWithConfigDefault(&kibana.ClientConfig{ Host: cfg.Fleet.Host, - Username: cfg.Fleet.Username, - Password: cfg.Fleet.Password, ServiceToken: cfg.Fleet.ServiceToken, IgnoreVersion: true, Transport: transport, diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd_test.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd_test.go index d58202753716..17bcbcedd25f 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd_test.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd_test.go @@ -157,8 +157,6 @@ func TestEnroll(t *testing.T) { require.NoError(t, err) require.Equal(t, "my-access-api-key", config.AccessAPIKey) require.Equal(t, host, config.Client.Host) - require.Equal(t, "", config.Client.Username) - require.Equal(t, "", config.Client.Password) }, )) @@ -217,8 +215,6 @@ func TestEnroll(t *testing.T) { require.NoError(t, err) require.Equal(t, "my-access-api-key", config.AccessAPIKey) require.Equal(t, host, config.Client.Host) - require.Equal(t, "", config.Client.Username) - require.Equal(t, "", config.Client.Password) }, )) @@ -277,8 +273,6 @@ func TestEnroll(t *testing.T) { require.NoError(t, err) require.Equal(t, "my-access-api-key", config.AccessAPIKey) require.Equal(t, host, config.Client.Host) - require.Equal(t, "", config.Client.Username) - require.Equal(t, "", config.Client.Password) }, )) diff --git a/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go b/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go index b33c0f8fa8ee..39cf43de28c2 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go @@ -43,8 +43,6 @@ type elasticsearchConfig struct { CA string `config:"ca"` CATrustedFingerprint string `config:"ca_trusted_fingerprint"` Host string `config:"host"` - Username string `config:"username"` - Password string `config:"password"` ServiceToken string `config:"service_token"` Insecure bool `config:"insecure"` } @@ -59,9 +57,7 @@ type kibanaConfig struct { type kibanaFleetConfig struct { CA string `config:"ca"` Host string `config:"host"` - Password string `config:"password"` Setup bool `config:"setup"` - Username string `config:"username"` ServiceToken string `config:"service_token"` } @@ -93,8 +89,6 @@ func defaultAccessConfig() (setupConfig, error) { CertKey: envWithDefault("", "FLEET_SERVER_CERT_KEY"), Elasticsearch: elasticsearchConfig{ Host: envWithDefault("http://elasticsearch:9200", "FLEET_SERVER_ELASTICSEARCH_HOST", "ELASTICSEARCH_HOST"), - Username: envWithDefault("elastic", "FLEET_SERVER_ELASTICSEARCH_USERNAME", "ELASTICSEARCH_USERNAME"), - Password: envWithDefault("changeme", "FLEET_SERVER_ELASTICSEARCH_PASSWORD", "ELASTICSEARCH_PASSWORD"), ServiceToken: envWithDefault("", "FLEET_SERVER_SERVICE_TOKEN"), CA: envWithDefault("", "FLEET_SERVER_ELASTICSEARCH_CA", "ELASTICSEARCH_CA"), CATrustedFingerprint: envWithDefault("", "FLEET_SERVER_ELASTICSEARCH_CA_TRUSTED_FINGERPRINT"), @@ -115,8 +109,6 @@ func defaultAccessConfig() (setupConfig, error) { // reflect that its setting up Fleet in Kibana versus setting up Fleet Server. Setup: envBool("KIBANA_FLEET_SETUP", "FLEET_SETUP"), Host: envWithDefault("http://kibana:5601", "KIBANA_FLEET_HOST", "KIBANA_HOST"), - Username: envWithDefault("elastic", "KIBANA_FLEET_USERNAME", "KIBANA_USERNAME", "ELASTICSEARCH_USERNAME"), - Password: envWithDefault("changeme", "KIBANA_FLEET_PASSWORD", "KIBANA_PASSWORD", "ELASTICSEARCH_PASSWORD"), ServiceToken: envWithDefault("", "KIBANA_FLEET_SERVICE_TOKEN", "FLEET_SERVER_SERVICE_TOKEN"), CA: envWithDefault("", "KIBANA_FLEET_CA", "KIBANA_CA", "ELASTICSEARCH_CA"), }, diff --git a/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go b/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go index 425d899a55b3..5a4e135afae5 100644 --- a/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go +++ b/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go @@ -37,8 +37,6 @@ type Elasticsearch struct { Protocol string `config:"protocol" yaml:"protocol"` Hosts []string `config:"hosts" yaml:"hosts"` Path string `config:"path" yaml:"path,omitempty"` - Username string `config:"username" yaml:"username,omitempty"` - Password string `config:"password" yaml:"password,omitempty"` ServiceToken string `config:"service_token" yaml:"service_token,omitempty"` TLS *tlscommon.Config `config:"ssl" yaml:"ssl,omitempty"` Headers map[string]string `config:"headers" yaml:"headers,omitempty"` @@ -70,18 +68,9 @@ func ElasticsearchFromConnStr(conn string, serviceToken string, insecure bool) ( VerificationMode: tlscommon.VerifyNone, } } - if serviceToken != "" { - cfg.ServiceToken = serviceToken - return cfg, nil + if serviceToken == "" { + return Elasticsearch{}, errors.New("invalid connection string: must include a service token") } - if u.User == nil || u.User.Username() == "" { - return Elasticsearch{}, errors.New("invalid connection string: must include a username unless a service token is provided") - } - password, ok := u.User.Password() - if !ok { - return Elasticsearch{}, errors.New("invalid connection string: must include a password unless a service token is provided") - } - cfg.Username = u.User.Username() - cfg.Password = password + cfg.ServiceToken = serviceToken return cfg, nil } diff --git a/x-pack/elastic-agent/pkg/remote/client.go b/x-pack/elastic-agent/pkg/remote/client.go index 19e1da1dbb8f..23f6162c08e3 100644 --- a/x-pack/elastic-agent/pkg/remote/client.go +++ b/x-pack/elastic-agent/pkg/remote/client.go @@ -60,19 +60,10 @@ func NewConfigFromURL(kURL string) (Config, error) { return Config{}, errors.Wrap(err, "could not parse url") } - var username, password string - if u.User != nil { - username = u.User.Username() - // _ is true when password is set. - password, _ = u.User.Password() - } - c := DefaultClientConfig() c.Protocol = Protocol(u.Scheme) c.Host = u.Host c.Path = u.Path - c.Username = username - c.Password = password return c, nil } @@ -126,11 +117,6 @@ func NewWithConfig(log *logger.Logger, cfg Config, wrapper wrapperFunc) (*Client return nil, err } - if cfg.IsBasicAuth() { - // Pass basic auth credentials to all the underlying calls. - transport = NewBasicAuthRoundTripper(transport, cfg.Username, cfg.Password) - } - if wrapper != nil { transport, err = wrapper(transport) if err != nil { diff --git a/x-pack/elastic-agent/pkg/remote/client_test.go b/x-pack/elastic-agent/pkg/remote/client_test.go index a48ebe82daf1..ef8a5f0d626b 100644 --- a/x-pack/elastic-agent/pkg/remote/client_test.go +++ b/x-pack/elastic-agent/pkg/remote/client_test.go @@ -160,58 +160,6 @@ func TestHTTPClient(t *testing.T) { }, )) - t.Run("Basic auth when credentials are valid", withServer( - func(t *testing.T) *http.ServeMux { - msg := `{ message: "hello" }` - mux := http.NewServeMux() - mux.HandleFunc("/echo-hello", basicAuthHandler(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, msg) - }, "hello", "world", "testing")) - return mux - }, func(t *testing.T, host string) { - cfg := config.MustNewConfigFrom(map[string]interface{}{ - "username": "hello", - "password": "world", - "host": host, - }) - - client, err := NewWithRawConfig(nil, cfg, nil) - require.NoError(t, err) - resp, err := client.Send(ctx, "GET", "/echo-hello", nil, nil, nil) - require.NoError(t, err) - - body, err := ioutil.ReadAll(resp.Body) - require.NoError(t, err) - defer resp.Body.Close() - assert.Equal(t, `{ message: "hello" }`, string(body)) - }, - )) - - t.Run("Basic auth when credentials are invalid", withServer( - func(t *testing.T) *http.ServeMux { - msg := `{ message: "hello" }` - mux := http.NewServeMux() - mux.HandleFunc("/echo-hello", basicAuthHandler(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, msg) - }, "hello", "world", "testing")) - return mux - }, func(t *testing.T, host string) { - cfg := config.MustNewConfigFrom(map[string]interface{}{ - "username": "bye", - "password": "world", - "host": host, - }) - - client, err := NewWithRawConfig(nil, cfg, nil) - require.NoError(t, err) - resp, err := client.Send(ctx, "GET", "/echo-hello", nil, nil, nil) - require.NoError(t, err) - assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) - }, - )) - t.Run("Custom user agent", withServer( func(t *testing.T) *http.ServeMux { msg := `{ message: "hello" }` @@ -400,19 +348,6 @@ func withServer(m func(t *testing.T) *http.ServeMux, test func(t *testing.T, hos } } -func basicAuthHandler(handler http.HandlerFunc, username, password, realm string) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - u, p, ok := r.BasicAuth() - - if !ok || u != username || p != password { - w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - handler(w, r) - } -} - type debugStack struct { sync.Mutex messages []string diff --git a/x-pack/elastic-agent/pkg/remote/config.go b/x-pack/elastic-agent/pkg/remote/config.go index 31ae29f70bad..495f850f5bc8 100644 --- a/x-pack/elastic-agent/pkg/remote/config.go +++ b/x-pack/elastic-agent/pkg/remote/config.go @@ -15,8 +15,6 @@ import ( type Config struct { Protocol Protocol `config:"protocol" yaml:"protocol"` SpaceID string `config:"space.id" yaml:"space.id,omitempty"` - Username string `config:"username" yaml:"username,omitempty"` - Password string `config:"password" yaml:"password,omitempty"` Path string `config:"path" yaml:"path,omitempty"` Host string `config:"host" yaml:"host,omitempty"` Hosts []string `config:"hosts" yaml:"hosts,omitempty"` @@ -55,17 +53,10 @@ func DefaultClientConfig() Config { Host: "localhost:5601", Path: "", SpaceID: "", - Username: "", - Password: "", Transport: transport, } } -// IsBasicAuth returns true if the username and password are both defined. -func (c *Config) IsBasicAuth() bool { - return len(c.Username) > 0 && len(c.Password) > 0 -} - // GetHosts returns the hosts to connect. // // This looks first at `Hosts` and then at `Host` when `Hosts` is not defined. diff --git a/x-pack/elastic-agent/pkg/remote/config_test.go b/x-pack/elastic-agent/pkg/remote/config_test.go index 403609735ddc..5a71cb9b6cdc 100644 --- a/x-pack/elastic-agent/pkg/remote/config_test.go +++ b/x-pack/elastic-agent/pkg/remote/config_test.go @@ -21,8 +21,6 @@ func TestPackUnpack(t *testing.T) { c := Config{ Protocol: Protocol("https"), SpaceID: "123", - Username: "foo", - Password: "bar", Path: "/ok", Transport: httpcommon.HTTPTransportSettings{ Timeout: 10 * time.Second, diff --git a/x-pack/elastic-agent/pkg/remote/round_trippers.go b/x-pack/elastic-agent/pkg/remote/round_trippers.go index 8c5b86f45ca2..e6583af57a84 100644 --- a/x-pack/elastic-agent/pkg/remote/round_trippers.go +++ b/x-pack/elastic-agent/pkg/remote/round_trippers.go @@ -123,36 +123,6 @@ func NewDebugRoundTripper(wrapped http.RoundTripper, log debugLogger) http.Round return &DebugRoundTripper{rt: wrapped, log: log} } -// BasicAuthRoundTripper wraps any request using a basic auth. -type BasicAuthRoundTripper struct { - rt http.RoundTripper - username string - password string -} - -// RoundTrip add username and password on every request send to the remove service. -func (r *BasicAuthRoundTripper) RoundTrip( - req *http.Request, -) (*http.Response, error) { - // if we already have authorization set on the request we do not force our username, password. - const key = "Authorization" - - if len(req.Header.Get(key)) > 0 { - return r.rt.RoundTrip(req) - } - - req.SetBasicAuth(r.username, r.password) - return r.rt.RoundTrip(req) -} - -// NewBasicAuthRoundTripper returns a Basic Auth round tripper. -func NewBasicAuthRoundTripper( - wrapped http.RoundTripper, - username, password string, -) http.RoundTripper { - return &BasicAuthRoundTripper{rt: wrapped, username: username, password: password} -} - func prettyBody(data []byte) []byte { var pretty bytes.Buffer