Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Elastic Agent] Add ability to communicate with Kibana through service token #28096

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions libbeat/kibana/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ var (
)

type Connection struct {
URL string
Username string
Password string
APIKey string
Headers http.Header
URL string
Username string
Password string
APIKey string
ServiceToken string
Headers http.Header

HTTP *http.Client
Version common.Version
Expand Down Expand Up @@ -196,12 +197,13 @@ func NewClientWithConfigDefault(config *ClientConfig, defaultPort int, beatname

client := &Client{
Connection: Connection{
URL: kibanaURL,
Username: username,
Password: password,
APIKey: config.APIKey,
Headers: headers,
HTTP: rt,
URL: kibanaURL,
Username: username,
Password: password,
APIKey: config.APIKey,
ServiceToken: config.ServiceToken,
Headers: headers,
HTTP: rt,
},
log: log,
}
Expand Down Expand Up @@ -267,6 +269,10 @@ func (conn *Connection) SendWithContext(ctx context.Context, method, extraPath s
v := "ApiKey " + base64.StdEncoding.EncodeToString([]byte(conn.APIKey))
req.Header.Set("Authorization", v)
}
if conn.ServiceToken != "" {
v := "Bearer " + conn.ServiceToken
req.Header.Set("Authorization", v)
}

addHeaders(req.Header, conn.Headers)
addHeaders(req.Header, headers)
Expand Down
32 changes: 17 additions & 15 deletions libbeat/kibana/client_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ import (

// ClientConfig to connect to Kibana
type ClientConfig struct {
Protocol string `config:"protocol" yaml:"protocol,omitempty"`
Host string `config:"host" yaml:"host,omitempty"`
Path string `config:"path" yaml:"path,omitempty"`
SpaceID string `config:"space.id" yaml:"space.id,omitempty"`
Username string `config:"username" yaml:"username,omitempty"`
Password string `config:"password" yaml:"password,omitempty"`
APIKey string `config:"api_key" yaml:"api_key,omitempty"`
Protocol string `config:"protocol" yaml:"protocol,omitempty"`
Host string `config:"host" yaml:"host,omitempty"`
Path string `config:"path" yaml:"path,omitempty"`
SpaceID string `config:"space.id" yaml:"space.id,omitempty"`
Username string `config:"username" yaml:"username,omitempty"`
Password string `config:"password" yaml:"password,omitempty"`
APIKey string `config:"api_key" yaml:"api_key,omitempty"`
ServiceToken string `config:"service_token" yaml:"service_token,omitempty"`

// Headers holds headers to include in every request sent to Kibana.
Headers map[string]string `config:"headers" yaml:"headers,omitempty"`
Expand All @@ -44,14 +45,15 @@ type ClientConfig struct {
// DefaultClientConfig connects to a locally running kibana over HTTP
func DefaultClientConfig() ClientConfig {
return ClientConfig{
Protocol: "http",
Host: "localhost:5601",
Path: "",
SpaceID: "",
Username: "",
Password: "",
APIKey: "",
Transport: httpcommon.DefaultHTTPTransportSettings(),
Protocol: "http",
Host: "localhost:5601",
Path: "",
SpaceID: "",
Username: "",
Password: "",
APIKey: "",
ServiceToken: "",
Transport: httpcommon.DefaultHTTPTransportSettings(),
}
}

Expand Down
6 changes: 6 additions & 0 deletions libbeat/kibana/client_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func TestClientConfigValdiate(t *testing.T) {
APIKey: "api-key",
},
err: nil,
}, {
name: "service_token",
c: &ClientConfig{
ServiceToken: "service_token",
},
err: nil,
}, {
name: "username and api_key",
c: &ClientConfig{
Expand Down
20 changes: 20 additions & 0 deletions libbeat/kibana/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,26 @@ func TestSuccess(t *testing.T) {
assert.NoError(t, err)
}

func TestServiceToken(t *testing.T) {
serviceToken := "fakeservicetoken"

kibanaTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{}`))

assert.Equal(t, "Bearer "+serviceToken, r.Header.Get("Authorization"))
}))
defer kibanaTs.Close()

conn := Connection{
URL: kibanaTs.URL,
HTTP: http.DefaultClient,
ServiceToken: serviceToken,
}
code, _, err := conn.Request(http.MethodPost, "", url.Values{}, http.Header{"foo": []string{"bar"}}, nil)
assert.Equal(t, http.StatusOK, code)
assert.NoError(t, err)
}

func TestNewKibanaClient(t *testing.T) {
var requests []*http.Request
kibanaTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down
1 change: 1 addition & 0 deletions x-pack/elastic-agent/CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,4 @@
- Agent now adapts the beats queue size based on output settings. {issue}26638[26638] {pull}27429[27429]
- Support ephemeral containers in Kubernetes dynamic provider. {issue}27020[#27020] {pull}27707[27707]
- Add complete k8s metadata through composable provider. {pull}27691[27691]
- Add `KIBANA_FLEET_SERVICE_TOKEN` to Elastic Agent container. {pull}28096[28096]
1 change: 1 addition & 0 deletions x-pack/elastic-agent/pkg/agent/cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ func kibanaClient(cfg kibanaConfig, headers map[string]string) (*kibana.Client,
Host: cfg.Fleet.Host,
Username: cfg.Fleet.Username,
Password: cfg.Fleet.Password,
ServiceToken: cfg.Fleet.ServiceToken,
IgnoreVersion: true,
Transport: transport,
Headers: headers,
Expand Down
22 changes: 12 additions & 10 deletions x-pack/elastic-agent/pkg/agent/cmd/setup_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ 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"`
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"`
}

func defaultAccessConfig() (setupConfig, error) {
Expand Down Expand Up @@ -104,11 +105,12 @@ func defaultAccessConfig() (setupConfig, error) {
// Remove FLEET_SETUP in 8.x
// The FLEET_SETUP environment variable boolean is a fallback to the old name. The name was updated to
// 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"),
CA: envWithDefault("", "KIBANA_FLEET_CA", "KIBANA_CA", "ELASTICSEARCH_CA"),
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"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it is the fleet-server service toke, should we indicate this here by calling it KIBANA_FLEET_SERVER_SERVICE_TOKEN ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have to be a Fleet Server token, it could be any token that has the correct permissions. It could be a different token then the one provided to Fleet Server.

I think its best to keep the name KIBANA_FLEET_SERVICE_TOKEN because KIBANA_FLEET_{name} is the same for all other environment variables that control Elastic Agent containers communication to Kibana.

It also matches with the configuration that can be used:

kibana:
  fleet:
    service_token: abc123

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshdover Do we check on the Fleet side if it is a fleet-server service token or if it has the correct permissions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll be checking that is has correct permissions, so it technically doesn't have to be the elastic/fleet-server service token. It just needs to have the "application privilege" that is being granted to the service account by default as defined in this issue: elastic/kibana#112647

So by default, Kibana would accept the elastic/fleet-server service account's token or any credentials for a super user (basic, apikey, etc.)

CA: envWithDefault("", "KIBANA_FLEET_CA", "KIBANA_CA", "ELASTICSEARCH_CA"),
},
RetrySleepDuration: retrySleepDuration,
RetryMaxCount: retryMaxCount,
Expand Down