diff --git a/x-pack/agent/pkg/fleetapi/client.go b/x-pack/agent/pkg/fleetapi/client.go index ed6cb510aacb..724f780a05ba 100644 --- a/x-pack/agent/pkg/fleetapi/client.go +++ b/x-pack/agent/pkg/fleetapi/client.go @@ -59,17 +59,17 @@ func init() { // NewAuthWithConfig returns a Kibana client that will: // -// - Send the AccessToken on every HTTP request. +// - Send the API Key on every HTTP request. // - Ensure a minimun version of Kibana is required. // - Send the Fleet User Agent on every HTTP request. -func NewAuthWithConfig(log *logger.Logger, config *config.Config, accessToken string) (*kibana.Client, error) { +func NewAuthWithConfig(log *logger.Logger, config *config.Config, apiKey string) (*kibana.Client, error) { return kibana.NewWithRawConfig(log, config, func(rt http.RoundTripper) (http.RoundTripper, error) { rt, err := baseRoundTrippers(rt) if err != nil { return nil, err } - rt, err = NewFleetAccessTokenRoundTripper(rt, accessToken) + rt, err = NewFleetAuthRoundTripper(rt, apiKey) if err != nil { return nil, err } diff --git a/x-pack/agent/pkg/fleetapi/client_test.go b/x-pack/agent/pkg/fleetapi/client_test.go index 85aebf55417b..28e383392b15 100644 --- a/x-pack/agent/pkg/fleetapi/client_test.go +++ b/x-pack/agent/pkg/fleetapi/client_test.go @@ -21,11 +21,11 @@ import ( ) func TestHTTPClient(t *testing.T) { - t.Run("Access Token is valid", withServer( + t.Run("API Key is valid", withServer( func(t *testing.T) *http.ServeMux { msg := `{ message: "hello" }` mux := http.NewServeMux() - mux.HandleFunc("/echo-hello", accessTokenHandler(func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/echo-hello", authHandler(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, msg) }, "abc123")) @@ -36,7 +36,7 @@ func TestHTTPClient(t *testing.T) { }) client, err := kibana.NewWithRawConfig(nil, cfg, func(wrapped http.RoundTripper) (http.RoundTripper, error) { - return NewFleetAccessTokenRoundTripper(wrapped, "abc123") + return NewFleetAuthRoundTripper(wrapped, "abc123") }) require.NoError(t, err) @@ -50,6 +50,30 @@ func TestHTTPClient(t *testing.T) { }, )) + t.Run("API Key is not valid", withServer( + func(t *testing.T) *http.ServeMux { + msg := `{ message: "hello" }` + mux := http.NewServeMux() + mux.HandleFunc("/echo-hello", authHandler(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, msg) + }, "secret")) + return mux + }, func(t *testing.T, host string) { + cfg := config.MustNewConfigFrom(map[string]interface{}{ + "host": host, + }) + + client, err := kibana.NewWithRawConfig(nil, cfg, func(wrapped http.RoundTripper) (http.RoundTripper, error) { + return NewFleetAuthRoundTripper(wrapped, "abc123") + }) + + require.NoError(t, err) + _, err = client.Send("GET", "/echo-hello", nil, nil, nil) + require.Error(t, err) + }, + )) + t.Run("Fleet user agent", withServer( func(t *testing.T) *http.ServeMux { msg := `{ message: "hello" }` @@ -81,9 +105,13 @@ func TestHTTPClient(t *testing.T) { )) } -func accessTokenHandler(handler http.HandlerFunc, accessToken string) http.HandlerFunc { +func authHandler(handler http.HandlerFunc, apiKey string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("kbn-fleet-access-token") != accessToken { + const key = "Authorization" + const prefix = "ApiKey " + + v := strings.TrimPrefix(r.Header.Get(key), prefix) + if v != apiKey { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } diff --git a/x-pack/agent/pkg/fleetapi/round_trippers.go b/x-pack/agent/pkg/fleetapi/round_trippers.go index 17898c1eb858..0f8a904e2484 100644 --- a/x-pack/agent/pkg/fleetapi/round_trippers.go +++ b/x-pack/agent/pkg/fleetapi/round_trippers.go @@ -11,6 +11,9 @@ import ( "github.com/elastic/beats/agent/kibana" ) +// ErrInvalidAPIKey is returned when authentication fail to fleet. +var ErrInvalidAPIKey = errors.New("invalid api key to authenticate with fleet") + // FleetUserAgentRoundTripper adds the Fleet user agent. type FleetUserAgentRoundTripper struct { rt http.RoundTripper @@ -31,27 +34,35 @@ func NewFleetUserAgentRoundTripper(wrapped http.RoundTripper, version string) ht } } -// FleetAccessTokenRoundTripper allow all calls to be authenticated using the accessToken. +// FleetAuthRoundTripper allow all calls to be authenticated using the api key. // The token is added as a header key. -type FleetAccessTokenRoundTripper struct { - rt http.RoundTripper - accessToken string +type FleetAuthRoundTripper struct { + rt http.RoundTripper + apiKey string } // RoundTrip makes all the calls to the service authenticated. -func (r *FleetAccessTokenRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - const key = "kbn-fleet-access-token" - req.Header.Set(key, r.accessToken) - return r.rt.RoundTrip(req) +func (r *FleetAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + const key = "Authorization" + const prefix = "ApiKey " + + req.Header.Set(key, prefix+r.apiKey) + resp, err := r.rt.RoundTrip(req) + + if resp.StatusCode == http.StatusUnauthorized { + defer resp.Body.Close() + return resp, ErrInvalidAPIKey + } + return resp, err } -// NewFleetAccessTokenRoundTripper wrap an existing http.RoundTripper and adds the accessToken in the header. -func NewFleetAccessTokenRoundTripper( +// NewFleetAuthRoundTripper wrap an existing http.RoundTripper and adds the API in the header. +func NewFleetAuthRoundTripper( wrapped http.RoundTripper, - accessToken string, + apiKey string, ) (http.RoundTripper, error) { - if len(accessToken) == 0 { - return nil, errors.New("empty access token received") + if len(apiKey) == 0 { + return nil, errors.New("empty api key received") } - return &FleetAccessTokenRoundTripper{rt: wrapped, accessToken: accessToken}, nil + return &FleetAuthRoundTripper{rt: wrapped, apiKey: apiKey}, nil }