From e4aa68a7cb99317c1e6277f3e3c7a9ef32c9916c Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Fri, 18 Jun 2021 10:00:18 +0200 Subject: [PATCH] Enable agent to send custom headers to kibana/ES (#26275) (#26361) Enable agent to send custom headers to kibana/ES (#26275) --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/info/agent_id.go | 1 + .../pkg/agent/application/info/agent_info.go | 7 + .../elastic-agent/pkg/agent/cmd/container.go | 142 ++++-------------- x-pack/elastic-agent/pkg/agent/cmd/enroll.go | 24 +++ .../elastic-agent/pkg/agent/cmd/enroll_cmd.go | 31 +++- .../pkg/agent/cmd/setup_config.go | 119 +++++++++++++++ .../pkg/agent/configuration/fleet_server.go | 1 + .../pkg/agent/program/program_test.go | 6 + .../pkg/agent/program/supported.go | 2 +- .../pkg/agent/transpiler/rules.go | 69 +++++++++ .../pkg/agent/transpiler/rules_test.go | 68 +++++++++ x-pack/elastic-agent/spec/apm-server.yml | 1 + x-pack/elastic-agent/spec/filebeat.yml | 3 + x-pack/elastic-agent/spec/fleet-server.yml | 2 + x-pack/elastic-agent/spec/metricbeat.yml | 1 + 16 files changed, 361 insertions(+), 117 deletions(-) create mode 100644 x-pack/elastic-agent/pkg/agent/cmd/setup_config.go diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index a6bf860ffba..928a1b9b084 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -112,3 +112,4 @@ - Keep http and logging config during enroll {pull}25132[25132] - Log output of container to $LOGS_PATH/elastic-agent-start.log when LOGS_PATH set {pull}25150[25150] - Use `filestream` input for internal log collection. {pull}25660[25660] +- Enable agent to send custom headers to kibana/ES {pull}26275[26275] diff --git a/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go b/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go index 386beabca61..6b62f32d396 100644 --- a/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go +++ b/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go @@ -29,6 +29,7 @@ const maxRetriesloadAgentInfo = 5 type persistentAgentInfo struct { ID string `json:"id" yaml:"id" config:"id"` + Headers map[string]string `json:"headers" yaml:"headers" config:"headers"` LogLevel string `json:"logging.level,omitempty" yaml:"logging.level,omitempty" config:"logging.level,omitempty"` MonitoringHTTP *monitoringConfig.MonitoringHTTPConfig `json:"monitoring.http,omitempty" yaml:"monitoring.http,omitempty" config:"monitoring.http,omitempty"` } diff --git a/x-pack/elastic-agent/pkg/agent/application/info/agent_info.go b/x-pack/elastic-agent/pkg/agent/application/info/agent_info.go index 8ae09c2efc3..8ae562919f3 100644 --- a/x-pack/elastic-agent/pkg/agent/application/info/agent_info.go +++ b/x-pack/elastic-agent/pkg/agent/application/info/agent_info.go @@ -13,6 +13,7 @@ import ( type AgentInfo struct { agentID string logLevel string + headers map[string]string } // NewAgentInfoWithLog creates a new agent information. @@ -30,6 +31,7 @@ func NewAgentInfoWithLog(level string, createAgentID bool) (*AgentInfo, error) { return &AgentInfo{ agentID: agentInfo.ID, logLevel: agentInfo.LogLevel, + headers: agentInfo.Headers, }, nil } @@ -84,3 +86,8 @@ func (*AgentInfo) Version() string { func (*AgentInfo) Snapshot() bool { return release.Snapshot() } + +// Headers returns custom headers used to communicate with elasticsearch. +func (i *AgentInfo) Headers() map[string]string { + return i.headers +} diff --git a/x-pack/elastic-agent/pkg/agent/cmd/container.go b/x-pack/elastic-agent/pkg/agent/cmd/container.go index 8f6f8b43fb0..26a14b87fd8 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/container.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/container.go @@ -271,7 +271,7 @@ func runContainerCmd(streams *cli.IOStreams, cmd *cobra.Command, cfg setupConfig } if cfg.Kibana.Fleet.Setup { - client, err = kibanaClient(cfg.Kibana) + client, err = kibanaClient(cfg.Kibana, cfg.Kibana.Headers) if err != nil { return err } @@ -286,7 +286,7 @@ func runContainerCmd(streams *cli.IOStreams, cmd *cobra.Command, cfg setupConfig token := cfg.Fleet.EnrollmentToken if token == "" && !cfg.FleetServer.Enable { if client == nil { - client, err = kibanaClient(cfg.Kibana) + client, err = kibanaClient(cfg.Kibana, cfg.Kibana.Headers) if err != nil { return err } @@ -363,6 +363,11 @@ func buildEnrollArgs(cfg setupConfig, token string, policyID string) ([]string, if cfg.FleetServer.CertKey != "" { args = append(args, "--fleet-server-cert-key", cfg.FleetServer.CertKey) } + + for k, v := range cfg.FleetServer.Headers { + args = append(args, "--header", k+"="+v) + } + if cfg.Fleet.URL != "" { args = append(args, "--url", cfg.Fleet.URL) } @@ -444,19 +449,21 @@ func kibanaFetchToken(cfg setupConfig, client *kibana.Client, policy *kibanaPoli return keyDetail.Item.APIKey, nil } -func kibanaClient(cfg kibanaConfig) (*kibana.Client, error) { +func kibanaClient(cfg kibanaConfig, headers map[string]string) (*kibana.Client, error) { var tls *tlscommon.Config if cfg.Fleet.CA != "" { tls = &tlscommon.Config{ CAs: []string{cfg.Fleet.CA}, } } + return kibana.NewClientWithConfig(&kibana.ClientConfig{ Host: cfg.Fleet.Host, Username: cfg.Fleet.Username, Password: cfg.Fleet.Password, IgnoreVersion: true, TLS: tls, + Headers: headers, }) } @@ -518,6 +525,27 @@ func envBool(keys ...string) bool { return false } +func envMap(key string) map[string]string { + m := make(map[string]string) + prefix := key + "=" + for _, env := range os.Environ() { + if !strings.HasPrefix(env, prefix) { + continue + } + + envVal := strings.TrimPrefix(env, prefix) + + keyValue := strings.SplitN(envVal, "=", 2) + if len(keyValue) != 2 { + continue + } + + m[keyValue[0]] = keyValue[1] + } + + return m +} + func isTrue(val string) bool { trueVals := []string{"1", "true", "yes", "y"} val = strings.ToLower(val) @@ -815,114 +843,6 @@ type kibanaAPIKeyDetail struct { Item kibanaAPIKey `json:"item"` } -// setup configuration - -type setupConfig struct { - Fleet fleetConfig `config:"fleet"` - FleetServer fleetServerConfig `config:"fleet_server"` - Kibana kibanaConfig `config:"kibana"` -} - -type elasticsearchConfig struct { - CA string `config:"ca"` - Host string `config:"host"` - Username string `config:"username"` - Password string `config:"password"` - ServiceToken string `config:"service_token"` -} - -type fleetConfig struct { - CA string `config:"ca"` - Enroll bool `config:"enroll"` - EnrollmentToken string `config:"enrollment_token"` - Force bool `config:"force"` - Insecure bool `config:"insecure"` - TokenName string `config:"token_name"` - TokenPolicyName string `config:"token_policy_name"` - URL string `config:"url"` -} - -type fleetServerConfig struct { - Cert string `config:"cert"` - CertKey string `config:"cert_key"` - Elasticsearch elasticsearchConfig `config:"elasticsearch"` - Enable bool `config:"enable"` - Host string `config:"host"` - InsecureHTTP bool `config:"insecure_http"` - PolicyID string `config:"policy_id"` - Port string `config:"port"` -} - -type kibanaConfig struct { - Fleet kibanaFleetConfig `config:"fleet"` - RetrySleepDuration time.Duration `config:"retry_sleep_duration"` - RetryMaxCount int `config:"retry_max_count"` -} - -type kibanaFleetConfig struct { - CA string `config:"ca"` - Host string `config:"host"` - Password string `config:"password"` - Setup bool `config:"setup"` - Username string `config:"username"` -} - -func defaultAccessConfig() (setupConfig, error) { - retrySleepDuration, err := envDurationWithDefault(defaultRequestRetrySleep, requestRetrySleepEnv) - if err != nil { - return setupConfig{}, err - } - - retryMaxCount, err := envIntWithDefault(defaultMaxRequestRetries, maxRequestRetriesEnv) - if err != nil { - return setupConfig{}, err - } - - cfg := setupConfig{ - Fleet: fleetConfig{ - CA: envWithDefault("", "FLEET_CA", "KIBANA_CA", "ELASTICSEARCH_CA"), - Enroll: envBool("FLEET_ENROLL", "FLEET_SERVER_ENABLE"), - EnrollmentToken: envWithDefault("", "FLEET_ENROLLMENT_TOKEN"), - Force: envBool("FLEET_FORCE"), - Insecure: envBool("FLEET_INSECURE"), - TokenName: envWithDefault("Default", "FLEET_TOKEN_NAME"), - TokenPolicyName: envWithDefault("", "FLEET_TOKEN_POLICY_NAME"), - URL: envWithDefault("", "FLEET_URL"), - }, - FleetServer: fleetServerConfig{ - Cert: envWithDefault("", "FLEET_SERVER_CERT"), - 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"), - }, - Enable: envBool("FLEET_SERVER_ENABLE"), - Host: envWithDefault("", "FLEET_SERVER_HOST"), - InsecureHTTP: envBool("FLEET_SERVER_INSECURE_HTTP"), - PolicyID: envWithDefault("", "FLEET_SERVER_POLICY_ID", "FLEET_SERVER_POLICY"), - Port: envWithDefault("", "FLEET_SERVER_PORT"), - }, - Kibana: kibanaConfig{ - Fleet: kibanaFleetConfig{ - // 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"), - }, - RetrySleepDuration: retrySleepDuration, - RetryMaxCount: retryMaxCount, - }, - } - return cfg, nil -} - func envDurationWithDefault(defVal string, keys ...string) (time.Duration, error) { valStr := defVal for _, key := range keys { diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go index 55baf76f56a..2828bc4f0da 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go @@ -10,6 +10,7 @@ import ( "os" "os/signal" "strconv" + "strings" "syscall" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" @@ -59,6 +60,7 @@ func addEnrollFlags(cmd *cobra.Command) { cmd.Flags().Uint16P("fleet-server-port", "", 0, "Fleet Server HTTP binding port (overrides the policy)") cmd.Flags().StringP("fleet-server-cert", "", "", "Certificate to use for exposed Fleet Server HTTPS endpoint") cmd.Flags().StringP("fleet-server-cert-key", "", "", "Private key to use for exposed Fleet Server HTTPS endpoint") + cmd.Flags().StringSliceP("header", "", []string{}, "Headers used in communication with elasticsearch") cmd.Flags().BoolP("fleet-server-insecure-http", "", false, "Expose Fleet Server over HTTP (not recommended; insecure)") cmd.Flags().StringP("certificate-authorities", "a", "", "Comma separated list of root certificate for server verifications") cmd.Flags().StringP("ca-sha256", "p", "", "Comma separated list of certificate authorities hash pins used for certificate verifications") @@ -81,6 +83,7 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string fPort, _ := cmd.Flags().GetUint16("fleet-server-port") fCert, _ := cmd.Flags().GetString("fleet-server-cert") fCertKey, _ := cmd.Flags().GetString("fleet-server-cert-key") + fHeaders, _ := cmd.Flags().GetStringSlice("header") fInsecure, _ := cmd.Flags().GetBool("fleet-server-insecure-http") ca, _ := cmd.Flags().GetString("certificate-authorities") sha256, _ := cmd.Flags().GetString("ca-sha256") @@ -128,6 +131,12 @@ func buildEnrollmentFlags(cmd *cobra.Command, url string, token string) []string args = append(args, "--fleet-server-cert-key") args = append(args, fCertKey) } + + for k, v := range mapFromEnvList(fHeaders) { + args = append(args, "--header") + args = append(args, k+"="+v) + } + if fInsecure { args = append(args, "--fleet-server-insecure-http") } @@ -211,6 +220,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command, args []string) error { enrollmentToken, _ := cmd.Flags().GetString("enrollment-token") fServer, _ := cmd.Flags().GetString("fleet-server-es") fElasticSearchCA, _ := cmd.Flags().GetString("fleet-server-es-ca") + fHeaders, _ := cmd.Flags().GetStringSlice("header") fServiceToken, _ := cmd.Flags().GetString("fleet-server-service-token") fPolicy, _ := cmd.Flags().GetString("fleet-server-policy") fHost, _ := cmd.Flags().GetString("fleet-server-host") @@ -246,6 +256,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command, args []string) error { CertKey: fCertKey, Insecure: fInsecure, SpawnAgent: !fromInstall, + Headers: mapFromEnvList(fHeaders), }, } @@ -285,3 +296,16 @@ func handleSignal(ctx context.Context) context.Context { return ctx } + +func mapFromEnvList(envList []string) map[string]string { + m := make(map[string]string) + for _, kv := range envList { + keyValue := strings.SplitN(kv, "=", 2) + if len(keyValue) != 2 { + continue + } + + m[keyValue[0]] = keyValue[1] + } + return m +} diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd.go index 9bb74d4d01a..cd3c9581c0b 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll_cmd.go @@ -78,6 +78,7 @@ type enrollCmdFleetServerOption struct { CertKey string Insecure bool SpawnAgent bool + Headers map[string]string } // enrollCmdOption define all the supported enrollment option. @@ -232,7 +233,8 @@ func (c *enrollCmd) fleetServerBootstrap(ctx context.Context) (string, error) { c.options.FleetServer.ConnStr, c.options.FleetServer.ServiceToken, c.options.FleetServer.PolicyID, c.options.FleetServer.Host, c.options.FleetServer.Port, - c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.ElasticsearchCA) + c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.ElasticsearchCA, + c.options.FleetServer.Headers) if err != nil { return "", err } @@ -412,7 +414,7 @@ func (c *enrollCmd) enroll(ctx context.Context, persistentConfig map[string]inte return err } - agentConfig, err := c.createAgentConfig(resp.Item.ID, persistentConfig) + agentConfig, err := c.createAgentConfig(resp.Item.ID, persistentConfig, c.options.FleetServer.Headers) if err != nil { return err } @@ -422,7 +424,8 @@ func (c *enrollCmd) enroll(ctx context.Context, persistentConfig map[string]inte c.options.FleetServer.ConnStr, c.options.FleetServer.ServiceToken, c.options.FleetServer.PolicyID, c.options.FleetServer.Host, c.options.FleetServer.Port, - c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.ElasticsearchCA) + c.options.FleetServer.Cert, c.options.FleetServer.CertKey, c.options.FleetServer.ElasticsearchCA, + c.options.FleetServer.Headers) if err != nil { return err } @@ -717,7 +720,12 @@ func storeAgentInfo(s saver, reader io.Reader) error { return nil } -func createFleetServerBootstrapConfig(connStr string, serviceToken string, policyID string, host string, port uint16, cert string, key string, esCA string) (*configuration.FleetAgentConfig, error) { +func createFleetServerBootstrapConfig( + connStr, serviceToken, policyID, host string, + port uint16, + cert, key, esCA string, + headers map[string]string, +) (*configuration.FleetAgentConfig, error) { es, err := configuration.ElasticsearchFromConnStr(connStr, serviceToken) if err != nil { return nil, err @@ -733,6 +741,15 @@ func createFleetServerBootstrapConfig(connStr string, serviceToken string, polic if port == 0 { port = defaultFleetServerPort } + if len(headers) > 0 { + if es.Headers == nil { + es.Headers = make(map[string]string) + } + // overwrites previously set headers + for k, v := range headers { + es.Headers[k] = v + } + } cfg := configuration.DefaultFleetAgentConfig() cfg.Enabled = true cfg.Server = &configuration.FleetServerConfig{ @@ -773,11 +790,15 @@ func createFleetConfigFromEnroll(accessAPIKey string, cli remote.Config) (*confi return cfg, nil } -func (c *enrollCmd) createAgentConfig(agentID string, pc map[string]interface{}) (map[string]interface{}, error) { +func (c *enrollCmd) createAgentConfig(agentID string, pc map[string]interface{}, headers map[string]string) (map[string]interface{}, error) { agentConfig := map[string]interface{}{ "id": agentID, } + if len(headers) > 0 { + agentConfig["headers"] = headers + } + if c.options.Staging != "" { staging := fmt.Sprintf("https://staging.elastic.co/%s-%s/downloads/", release.Version(), c.options.Staging[:8]) agentConfig["download"] = map[string]interface{}{ diff --git a/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go b/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go new file mode 100644 index 00000000000..4330c967e9f --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/cmd/setup_config.go @@ -0,0 +1,119 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package cmd + +import "time" + +// setup configuration + +type setupConfig struct { + Fleet fleetConfig `config:"fleet"` + FleetServer fleetServerConfig `config:"fleet_server"` + Kibana kibanaConfig `config:"kibana"` +} + +type fleetConfig struct { + CA string `config:"ca"` + Enroll bool `config:"enroll"` + EnrollmentToken string `config:"enrollment_token"` + Force bool `config:"force"` + Insecure bool `config:"insecure"` + TokenName string `config:"token_name"` + TokenPolicyName string `config:"token_policy_name"` + URL string `config:"url"` +} + +type fleetServerConfig struct { + Cert string `config:"cert"` + CertKey string `config:"cert_key"` + Elasticsearch elasticsearchConfig `config:"elasticsearch"` + Enable bool `config:"enable"` + Host string `config:"host"` + InsecureHTTP bool `config:"insecure_http"` + PolicyID string `config:"policy_id"` + Port string `config:"port"` + Headers map[string]string `config:"headers"` +} + +type elasticsearchConfig struct { + CA string `config:"ca"` + Host string `config:"host"` + Username string `config:"username"` + Password string `config:"password"` + ServiceToken string `config:"service_token"` +} + +type kibanaConfig struct { + Fleet kibanaFleetConfig `config:"fleet"` + RetrySleepDuration time.Duration `config:"retry_sleep_duration"` + RetryMaxCount int `config:"retry_max_count"` + Headers map[string]string `config:"headers"` +} + +type kibanaFleetConfig struct { + CA string `config:"ca"` + Host string `config:"host"` + Password string `config:"password"` + Setup bool `config:"setup"` + Username string `config:"username"` +} + +func defaultAccessConfig() (setupConfig, error) { + retrySleepDuration, err := envDurationWithDefault(defaultRequestRetrySleep, requestRetrySleepEnv) + if err != nil { + return setupConfig{}, err + } + + retryMaxCount, err := envIntWithDefault(defaultMaxRequestRetries, maxRequestRetriesEnv) + if err != nil { + return setupConfig{}, err + } + + cfg := setupConfig{ + Fleet: fleetConfig{ + CA: envWithDefault("", "FLEET_CA", "KIBANA_CA", "ELASTICSEARCH_CA"), + Enroll: envBool("FLEET_ENROLL", "FLEET_SERVER_ENABLE"), + EnrollmentToken: envWithDefault("", "FLEET_ENROLLMENT_TOKEN"), + Force: envBool("FLEET_FORCE"), + Insecure: envBool("FLEET_INSECURE"), + TokenName: envWithDefault("Default", "FLEET_TOKEN_NAME"), + TokenPolicyName: envWithDefault("", "FLEET_TOKEN_POLICY_NAME"), + URL: envWithDefault("", "FLEET_URL"), + }, + FleetServer: fleetServerConfig{ + Cert: envWithDefault("", "FLEET_SERVER_CERT"), + 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"), + }, + Enable: envBool("FLEET_SERVER_ENABLE"), + Host: envWithDefault("", "FLEET_SERVER_HOST"), + InsecureHTTP: envBool("FLEET_SERVER_INSECURE_HTTP"), + PolicyID: envWithDefault("", "FLEET_SERVER_POLICY_ID", "FLEET_SERVER_POLICY"), + Port: envWithDefault("", "FLEET_SERVER_PORT"), + Headers: envMap("FLEET_HEADER"), + }, + Kibana: kibanaConfig{ + Fleet: kibanaFleetConfig{ + // 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"), + }, + RetrySleepDuration: retrySleepDuration, + RetryMaxCount: retryMaxCount, + Headers: envMap("FLEET_KIBANA_HEADER"), + }, + } + return cfg, nil +} 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 e90f4bf3b1c..a87da18ecf2 100644 --- a/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go +++ b/x-pack/elastic-agent/pkg/agent/configuration/fleet_server.go @@ -40,6 +40,7 @@ type Elasticsearch struct { 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"` } // ElasticsearchFromConnStr returns an Elasticsearch configuration from the connection string. diff --git a/x-pack/elastic-agent/pkg/agent/program/program_test.go b/x-pack/elastic-agent/pkg/agent/program/program_test.go index 2691aae9e41..4498f7e5236 100644 --- a/x-pack/elastic-agent/pkg/agent/program/program_test.go +++ b/x-pack/elastic-agent/pkg/agent/program/program_test.go @@ -499,3 +499,9 @@ func (*fakeAgentInfo) Version() string { func (*fakeAgentInfo) Snapshot() bool { return false } + +func (*fakeAgentInfo) Headers() map[string]string { + return map[string]string{ + "h1": "test-header", + } +} diff --git a/x-pack/elastic-agent/pkg/agent/program/supported.go b/x-pack/elastic-agent/pkg/agent/program/supported.go index 323d8e09f0f..9ff4eab7f10 100644 --- a/x-pack/elastic-agent/pkg/agent/program/supported.go +++ b/x-pack/elastic-agent/pkg/agent/program/supported.go @@ -25,7 +25,7 @@ func init() { // spec/metricbeat.yml // spec/osquerybeat.yml // spec/packetbeat.yml - unpacked := packer.MustUnpack("eJzMWlmXozabvp+fkdtvFpZ2Jcw534WhwmabinEbCd0hyQZsgZ0yXmDO/Pc5YgfbXV2dzsxc5KRLFlrf5XmeV//1y+m4If8RHJN/O23eL5v3f88T9st//oITPUNfD+HSU5255zCSIkbC4w6D5Ytl6Fe8EgsEbQlBa+ZDWwgAinz54W8pKQ4huB5CS7Myd2WdLM3OfDCJkORlCEyEeeKdfWCfEFgq1LRFtLJOWjwNrVjUrfgaWslgzDMydMH3lIKaNvOBWHz8Pd1BWWUkcRhOl4ptZur6d/Gr69nA9eytKyjmsjjcFq+qYoVHqqW2iA2veAv5moXQl5TrxlMELCqnADpC1T4NLU09UsPL3mI1wYbH6LRtF3BxCAMwuVLoFuVaeLuhnKHkXHCCTgFwhLdYPWNJufLf5ys18+H0pe1rqhE1whfLQCcEPKFr76+tbtOEkCRehmXEoJSxzdfDrPut+i+QvMlbrEa+5DAiO1sfqsey7/KHxskRVC8kdY84If0+mWXaDANFQp7yjuC+20/zn1GOG/qA8ruYld8Y6LjRm/sUXiwzU+ozSQJwExC0tzTRTxT0960WCNyYL7sXsnt01tU81GRX1O5RlXxwExFcDNY1X6kRMYR2Ldh0Gdl1eyeSd0LAEbBs35373bzVeBcK3SuFy+HZNHeZ0gMCX14s48ZwQoVAC/cbiZ2J6QlEFo7W65dwoakRTpZhYOjFSvImM839FcuewPtsV9fQlryTDx0hAE6BgJ77UpjOlod//vKvlUNvUno8xGk2cmcXTPbEUI44XYZrydtRaB+puZ/5krh/i1WGE/eKJXammlgg4IgkYcJmeYz4VaNE39HXQ4i6MTJkeJKWluHh6EvrF+vVl99ew5kP+NK4uVfbgrI7IZXZZpbhnZGpXgIwEbTkdkGicvWheyAFP3J170NbDvjxaNblq8Fikuj5ZqXo2NALarDdXOi+n8uO4EOXzaXbBeVKb/3Cn3M+dm7xMU8BmIib10NoxcqFmMuLC24Rkd2jnyt6941SUEMX0Eo5YYlc+vucxRPeFvNrphI7I0OReViz9osXqN+WJFFSkuiZ9Ts6cveE+q1db/nvZg79RojsRtTwCDT43m/k4TyJc0DAeS/PT3YjbFxftFgIEYyYLyrcLVhjisTQhYDvLemdC3SYL3t5AN2JVferQ/GsMV2Lh/SEJZuV1bXFQoYNJW2+ma+mMZHdPYJ23rRRg2UIKCK3hUUxnRFDKajO1+8IPrid6jv+goCzRQk7oSaMNCFNsx/bWbMOQ8+R3LpoZml2O3Z/XfOV2N5J3a+ghstIavXarGwOvSuS7QgZ61G7zYikiDwtkLx3Bk/Ocdh/8hLAaT2eKgRAZNw93+KptHidzohpMyh75wBMuE2d8OthNl+pbGN4OyhxG1nX+1NL23+Lp3HfDkjnm80cEUloP/zw/Yo4ae0j7kLQ/T0+Pp8H6069UxUehRAl7AuUHYEkXoS/HsKNLMzq9hM1vBzKKMKmx1qbMh2GDW9HDSV/i9UjTlWRmovZs3A8Dun8fFq7mJbn9TT8dyHZerGMOnReD/0UxXCix9jw9vVex6E/s0w3p2Bd7gkD/Tr2J5TcGGpC+CjtzldqjoB4oYm3Lefrp4V6r0Tycpp4uVb6wyit9M6q75ODOe/TS2YZikhNVWygRrkOiI5EYhccHmZUihjeHULMY6zsHnjaqMZ0ldlq+g/rdRr6YLL/+annuMPShKfwiPsNj422meUUTEobmye8X6RY2u+KpdGIJILkaGTWpKttzDZ4E9ylKx4+gM18uGxSVBn6/MSL6PRYuUSs4gHaSx1GTe86T9gJryatGfwBuLk6zIqv/Hjj+Xodz7VpTCRPoHB65kiOGLeIGuszApPI58f2KiY+uBX3iFKMcKKniLtPuuz3F0jq3c3BXRHxtJFPTggihl/FPQK2iPIPkaqxWt/05d5TPV0xvwr09W33+3VhCrHGhBHy5ufkFvMyvHgxArqgpTbj4YGk7hZLk21zjVByDj6YpKh0SVtEy2NOwa105dLtYLQlspsjoHN0++t2yU2YnYPE25WhPnHZ5rUxZZ7a1xzNXJC8KN0pAJM/uXu24cNTriRRdgg6BXfZ2iUvmCncbBJssBJa8HCJoC1ASU94iGnC1Aaq7C1WT1iiReVOYkReR2llFAa6UNa4jnMhJtvyNAJl9wKl25HIywESpMZvL5ZZrxkueujtfq04US5k2nd774sveVf+G8jtlp1U98r21f9bplLZnmlffMkriKTkJLfpeK3UULbYYAV97SPxlnX0ztQufnQf3ZnbDCVKjpalDeTcpjFo01RCEiW7C+ume+nanHbPWg0BeDjwZbfag66U6+7Sxeje5NF6DSagkg2M2gdzLp6G72HoVFv7bkIrXxtOnROHj4Pw3ayrsuv+2WU+VK8IWiNWU9plBclKGyVD1mR4Usly67Rf+sn1MGBFZUxIlxcOtUroazoCMth5NA+jicdhquDLU76+3cD+euNQ4F7fYlVE5nS0lhIm77HkvPN9WIZ78aWMkXA4Do9Xc9lmyGAFlJ0Tlinf1wtnd7ztfv/kQmRW8O/eYrXYQKd3Dt9ilWrJEi3TK5CnjFjTd3xncCitt7GqS+3PWWgND/Y+dKM2Pq0mZx+IjMicIa9/eP55Uv5d8BT+N0OliMqLzJdu/K5lH7q7YDr8jRSLdh8+PIokWWeVfbgHCjq4W4+RYJnDXnvSj0E4dXk6b+1jvlIb2+kgi+Rc51AV/dQR/a7fgZruFUo9qteOGwnUVP8kknLu2rIIJVnU/d35yHylZgS6ve8njBrohOXOvnCxkBygi8hgQt8GeraajXyK/z0h0mAe7lddfADutevrnQMYdr9J7MxtvVtTRftqBeMv332LJ6ZP77/EGVWcbfNyhCTvXOVndKnzNo/lOyyrLURFqX3hcH4UH0vFCvXwVn8P97Db7q+lh8kOTxQQofQ7IrsXkqyfwNR27nOztu1qH/4RT6+WoZ+Rph586MwR3B9sM6vHd5W5Nk1RQ+dlh/nQ3gUaOVkazRFwjyQnJcaypcrG7JxDWI5VHMGHzsHO9x0MZZtN9lgIdStkHq4bVJ84GepYQtai/aRir5Z+4gy1uhJNzLDkMusOslWCEGeZtcDaT1OPhbFalHxqXh+woy7kDtPbvcA2YCRZj838nPmNFgp9uIYKotZn8iy0167TQNxmnc1aIGeVxm89gXXAwEKQ1yKuNmmF5maseXLHeELYiYo1g+9MvhYvG4Fyy8Mffng+dklfWjtIazuIJ1cs3Y6+vD8HYPloriZsnBda27eZ94jLcdwtMrzEh96Jmotn+34oUPbWccCyI4zEzbtzIoaS0+lDuzk3djNPnQJPv7mPtmjwMwTxcejqpe5OOE4X9wJ1nUJLv52Ov61DmaEUdNr4aI+KPIWff1m4F4nktfRtdB/PlYiPodJfGmOecIrgFcTQd+jHCgljGFb+zSn5D62vRxWGBQTrU/Y/mnuYotu+rRLzf6i6dIJ/tAneswcSysrwOP2v5IA6ZwWDtl6+GkkiAbhl/eIaSvQTkao+n5VPPlP86/Xl1CoNwCSdJzdOf05/AJf5qZfe59JG/og4BBBqiShHkKd5Tm+VM5TqO9OVXcAhhbR+aVTAvlzwRAJ5nPdERQ6ge4CcLknel34sqG28lC96cYDTxA2ReT6KGDXYjuS/nWfXjwtbP6MgNoaW3yiKVRDzh/xonPNrmP44fr0jyAo+Dloe5cDwzpxGIzBJqRFyqNfICyNfiiIiZGyz4r5U36spcNjHmsoLkbyIJBziXUO7prN2XkpzaSDpSSD9nnL4yKF8KZONfCrZZO8xeeBUX4EnkITtaiOrq+Iio6Z99KVar3xc+S4QdEWiTY7YED7WGRstM3UZhmqpVTx0yOlfqa7fLkiiR5yQMy61iquCDC+mgIzHTX1RuSJo7/i4f6zcX7+uvfV6z14/p08Oz4kkXkENPae6csGs0QDcrS9FEU4od5bKEFP1Qiow1ZZU6gRcJoZBqWlQFhMvyCyd+4w0pdhAR0BAOG+AeOrKIyoPEimCyxd+flhyy2Q9T5alrsGD2zxlGdYm+wA61Z1p1oflkjJ5JMcGBO4RDF9GmmaGoJvzoD7ico2Wtu1rhI2zY2my9SXljBIO/JYVyOZAi3PA1G35eqkhcFBc2yu5/ixt7xv661jHuy+dlHwUSvoJ649eVbRzd3P2Asb93icXLPd1LpVtDIcRc1kmtSaIk7y04SPSyv+3dYHKz9QcVy8jOCiMG8DdrvWJXhxAl/G7Hui41x/dR3uHMQKo5Ml1yVcgic5tojwnKDEBAbF4pne2+23KYc0aR+2lRtwF6oflNijTIzWiLUm8FMGo1VjvE5uaczuD8Zf31r/lRVN6e5yY+mUxpoyS0LeS2RMCaY6JaEcch2Tijnw8s1N+7mcicVDjbanEhEBXcgQo25jTgR00dzB8bdIl+9mrsvyjItz/mMen4/0Z1QSYz/F6CO0BOS/BdKlPDsuMKMeS8EinLrUcDJQ9BTfWaVRiFEje1od27o810dpG2jgxAuCVrTRr5jlp8QmNtPfd9PvLp2Og8l3fDF6GCT9Fx/3xMe7J0ffsgUKHtUC1KoO3OaR6NVXmkoTfcVOSL1+oNWtq7GIEwgZ3KHUa39i2Gn0Q9zEOxyv3uaHNk58pcQ/G/U698PGLqcFezo29/xVCVeGdTje0NPruA/Tur8p/l/VHbheBRo5a+M8WKB5Of5437/kjpCg7Nwq8fDOsYl+IrIsI2pNxJfsTVezPo8R+RRro59KagHemWm98WNKhYd+n1WubfqLCPHhEVu7bXFzw+Hy++XBMKQj0GEn3s94bzSfvB3tV6PLBlvflbYyKOOy/Dj2s8V4o9xBBY9H37zdbqc0HSGirIg8q4n9jJai1pe+sBoyo3EM57I7C9T3073u/+JlHJKR4e/XTvhceA7LfPNJA1oa+CyRPGNA1U418KWPUGNG1nGRu5e4fUDXe564vh2ZXXL4ivHfakofmol6VIL79hGTY9ylFS+ETxyLDPf+4DvK/8wD36gPnHXXa+Qcp72/QJML/F9rD7Jf//pf/CQAA//8yZgGk") + unpacked := packer.MustUnpack("eJzEWkmXo7ia3ffPqO3rgSEdVfQ5b2GIYrJNlHEaCe2QZAO2wK4wHqBP//d3xAy2M3Kqeos8kaEQGr/h3vvp/345HTfkf4Jj8l+nzftl8/7fecJ++d9fcKJn6PMhXHqqM/ccRlLESHjcYbB8sQz9ildigaAtIWjNfGgLAUCRLz/8W0qKQwiuh9DSrMxdWSdLszMfTCIkeRkCE2GeeGcf2CcElgo1bRGtrJMWT0MrFnUrvoZWMhjzjAxd8D2loKbNfCAWH39Pd1BWGUkchtOlYpuZuv5d/Ox6NnA9e+sKirksDrfFq6pY4ZFqqS1iwyveQr5mIfQl5brxFAGLyimAjlC1T0NLU4/U8LK3WE2w4TE6bdsFXBzCAEyuFLpFuRbebihnKDkXnKBTABzhLVbPWFKu/O/zlZr5cPrS9jXViBrhi2WgEwKe0LX311a3aUJIEi/DMmJQytjm82HW/a36F0je5C1WI19yGJGdrQ/VY9l3+V3j5AiqF5K6R5yQfp/MMm2GgSIhT3lHcN/tp/lnlOOGPqD8LmblNwY6bvTmPoUXy8yU+kySANwEBO0tTfQTBf19qwUCN+bL7oXsHp11NQ812RW1e1QlH9xEBBeDdc1XakQMoV0LNl1Gdt3eieSdEHAELNt35343bzXehUL3SuFyeDa9u5yv1CNOjnz928DwImR4OfeTjSzMqBQxvDuE2PDOSHYPM839tZlnu9qHf8TTq2XoZ6SpBx86cwT3B9vM6jldZa5NUwRuEZHdoy87zIf2LtDIUQv/+c9f/rNy+E1Kj4c4zUbu7oLJnhjKEafLcC15OwrtIzX3M18S92+xynDiXrHEzlQTCwQckSRM2CyPETcFlOg7+noIUTdGhgxP0tIyfBx9af1ivfry22s484EjBIC7AzsT0xOg7E5IZdaZxbdtqpcATAQtuV2QqFx96B5Iwa9E3fvQlgPw6cXSrMtng8Uk0fPNStGxoRfUYLu50H0/lx3Bhy6bS7cLypXe+oU/53zs3OJjngIwETevh9CKlQsxlxe3Ob5c0btvlIIauoBWyglL5NLf5yye8LaYXy2V2BkZisyv09ovXqB+W5JESUmiZ9bv6MjdF+q3dr3l/5s59BshshtRwyPQ4Hu/kYfzJM4BAee9PD/ZjbBxfdFiIUQwYr6ocLdhjakSQxcCvrekdy7QYb7s5QF0J1bdrw7Vs8a0LR7yE5ZsVlbXFgsZNpS0+Wa+msZEdvcI2nnTRg2WIaCI3BYWxXRGDKWgOl+/I/jgdqrv+BMCzhYl7ISaMNOEPM1+bGfNOgw9R3Lrwpml2e3Y/XXNV2J7J3W/ghouI6nVa7OyOfSuSLYjZKxH7TYjkiLytEHy3hk8Ocdh/8lLAKf1eKoQAJFh2RPe4qm0eJ3OiGkzKHvnAEy4TZ3w62E2X6lsY3g7KHEbWdf7U0vbf4uncd8OSOebzRwRSWg/PPH9ijhp7SPuQtT9PT4+nwfrTr1TFT6FECXsE5QdgSRehD9XoatuP1HDy6GMImx6rLUp02HY8HbUUPK3WD3iVBWpuZg9C9fjkM/Pp7WLaXleT9NDF7KtF8uoYgy5HvopjOFEj7Hh7eu9jlNDZpluTsG63BMG+nXsTyi5MdSE+FFanq/UHAHxQhNvW87XTxv1Xonk5TTxcq30h1Ha6Z1V3ycHc96nn8wyFJGaqthAkXIdEB2JxC44PDxMLdWYrjJbTf9hvU5DH0z2lnFjOKFCoIX7TR2jiSwcrddP4UJTI5wsw8DQi5XkTfgY3EZ4n+3qGtqSd/Ihj+9OgYCe+1KYzpbHHZYmPMVH3G94bLTNLKdgUtrYPOH9IsXSflcsjUYkESRHI7MmXW1jtsGb4C5d8fABbObDZZOiytDnJ15Ep8fKJWIVD9Bg6jBqetd5wk54NWnN4A/AzdVhVnzlxxvP1+t4rk1jInkChdMzR3rEuEXUWJ8RmEQ+P7ZXMfHBrbhHnGKEEz1F3H3SZb+/QFLvbg7uioinjXxyQhAx/CruEbBFlH+IZI3V+qYv957q6Yr5WaCvb7vfrwtTiDUmjJA5Pye3mJfhxYsR0AUttRkPDyR1t1iabJtrhJJz8MEkRaVL2iJaHnMKbqUrl24Hoy2R3RwBnaPfX7dLbsLsHCTergz1ics2r40p89S+frF4OpMXpTsFYPInd882fHjKlSTKDkGn4C5bu+QFM4WbTYINVkILHi4RtAUo6QkPMU2Y2kCVvcXqCUu0qNxJjMjrKK2MwkAXyhrXcS7EZFueRqDsXqB0OxJ5OUCK1PjtxTLrNcNFD93drxUnyoVM+27vffIl78r/BnK7ZS/VvbJ99bNlMpXtmfbFl7yCSEpOcpuO10oNZYsNVtDXPlJvWUnvTO3ie/fRnbnNUKLkaFnaQM5tGoM2TSUkUbK7sG66l67Nafes1RCAhwOOgMs96Eq57i5djO5NHq3XYAIq2cKofTDn4mn4HoZOtbXvJrTyteHUOXH4OAjfzboqu+6fXeZD9YqgNWI9pV1WkKy0UTJkVYYnlSy4Tvuln1wPA9ZUxoR0eeFQq4S+piMgg51H8zCaeBymCr485evbDeyvNw4F7vUtVkVkTkdrKWHyHkvOO9+HZbgXX8oYCYfj8Hg1l22GDFZA2TlhmfJ9vXD2x9vu908uRGYF/+4tVosNdHrn8CXWqZYs0jK9AnnKiFV9xXcGh9J6G6u61P6cpdbwYO9DN2rj02py9oHIiMwZ9Pq7558n5e8FT+F/MVSKqLzIfOnG71r2obsLpsO/kWLR7sOHR5Ek66yyD/dAQQd36zESLHPYa0/6MQinLk/nrX3MV2pjOx1kkZzrHKqinzqi3/U7UNO9QqlH9dpxI4Ga6p9EUs5dWxahJIu63zsfma/UjEC39/2EUQOdsNzZFy4WkgN0ERlM6NtAz1azkU/x3ydEGszD/aqLD8C9dn29cwDD7m8SO3Nb79ZU0b5a4fjhu2/xxPTp/Zc4o4qzbV6OkOSdq/yMLnXe5rF8h2W1hagotS8czo/iY6looR7e6u/hHnbb/bX0MNnhiUIilH5HZPdCkvUQI0gR8wGnJYtKGQqPcmB4Zx5LEZik1AgPtpl1c2iTJt+MIG0UESFjmxWHtDVMNoWTpVHWUHEieRFJnIOdX0O7jm92XmK1NJD0JJB+T+faNOV3W+KmEtoeWlVlyzab7LGQ6lbIPVw3qD9xMtSxiKxlA0nFbi39xBlsdWWamGHJZdYdpKsEJc5Ca4G2n8YeC2u1qPnU/D5gT11IHqa/e4FuwFiyHtv5OfMbLVT6cA0VhK3P5Fnor12rgcDNOpu1QM46jd96Au2AoYUgr0VgbdIK1c1Y8+SOEYWwEyVrht+5RC0QNgLnlodH/PB87JLetHaQ1nYQT65Yuh19eX8OwPLRXE1YOS+0tu+sFSbLcdwtMrzEh96Jmotn+34ocPbWccCyI4zE0btzIoaS0+lDuzk3djNPnQJPv7iPtujwMwT1cWjrpfZOeE4X9wJ3nWJLv52Ov61DnaEUdNr4aI+qPIWnPyz8i0TyWno3uo/nSsXHUOqHxpgnnEJ4BTH0Hfq+QsQYppW/c8r+XevrUYlhAcL6JvsfzT1M4W3fVqmp7aBN0wcE9D2CpW+XKfrfp9p0qS3aBO/ZAwlmZXgRSd1KTqhzWjBo6+WzkaQSgFvWL96hRD8RqerzrfLLtxQXe305NUsDMEnnyY3Tp9MfwGV+6qX3ubaRTyLG22uJKUfQEfySHitn2MAKXdkFhn5G0vqlURH7csMTCeVxXhQVOYDuAXI4Inmf+rGi9oFS/ujFCU4zN0Tm+SpiJazJfzvPrh8Xzn5GwW0MTb9QdKsg6nf52RgT1DD/cXx7R5AVfBy0fAgd/21wMdlk7zF54FSfgSeQhO1qI6ur7iKjpn30pVrvfFxZLxB0RcIxsCF8rFM2WmjqMgzVUut46JDTH6ne3y5IokeckDMutY6rggwvpoCMx019UbkiaO/4uH+s3F8/r731es9ev03fHJ4TSbyCGnpOdeWCWaMhuFtfiiKcUO4slSGm6oVUYKstydQJukwcg1LVoKwmXpBZOvcZaUqxgY6AgHDeAPHUlVdUHiRSBJcv/Pyw5JbJfJ4sS12EB7d5yjKsTfYBdKo706wPyy2DKjKY7BEMX0aaaIagm/OgPuKCjRa37WuMjbNjabL1JeWMEg4MlxUI50CMc8jUbfl+qUFw0FzbK7n+LG3wC/rtWAe8L72UfBZK+gnrj15ttHN3c/YCxv3eJxcs93UylW0MhxFzWSa1JoiTvLThI9LKn21dofIzNcfVywsOGuMGkLdrfaI3B9Bl/K4HOvD1e/fR3mGMAGp5NoKRQBKd20R5TlBiAgJi8UwvbffblNOaNY7aS425C9QPy3VQpkdqRFuSeCmCUavR3ic2Ned2BuNP761/y4umdPc4MfXLakwZJaEvJbMnBNMcE9WOWA7Jxh05eWan/NzPROKgxttSiQmBruQIULYxpwM7aO5g+JqlS/azV2X5R0XI/zGPT8f7M6oJMp/j9RDaA/Jegu1S3xyWKVGOJeGRzl1qQRgoewpurNO4xCiQvK0P7dwfa6q1jbRxYgTQK1tp1sxz0uIbNNbed9OvL7+OgcpXfTN4eSb8FB34+8e4J09fswcKHdYC1aqM3uaQ6lVWmUsSXHQl/fIFXLOmxi5GIGxwh1KnEY5tq9EXcR/jtJrdIDe0efJbSuSDcb9Sb/zSaywtpQcEPr0MydVgn+fGF36EbFVYiOYIuEeSEw40332A3v1V+f+ytsltZvyS63D687x5zx+hSNm5UeDlm2GF/EJkXUTQnoyr5N9QIf92BNmvdgP9XFoa8M5U640PS6o07Pu0Mm7Tb6heDx6olfs2Fxc8Pp8vPkpTCgI9RtL9rPc+9Mnbxb56zc7I8D69jRETpwTXofc1ng3lHlporP3+7Wgr0/kACW3F5UG1/S+sMrW29JWVhhHNeyil3dG7vvc+9sa/+4EKKd5e/bTvhceA7DeP9JG1oe8CyRMGVM5UI1/KGDVGVC4nmVu5+wc0jve568th2xWXLxTvnbbkqLmolz+lLz9PGfZ9St9S+MSxyHDP36+R/D2Pf68+cN5Rp7t/kA7/Ar3icUnr79YlZr/8/3/8KwAA//+MQCuK") SupportedMap = make(map[string]Spec) for f, v := range unpacked { diff --git a/x-pack/elastic-agent/pkg/agent/transpiler/rules.go b/x-pack/elastic-agent/pkg/agent/transpiler/rules.go index 9135c751d3a..2afb312c930 100644 --- a/x-pack/elastic-agent/pkg/agent/transpiler/rules.go +++ b/x-pack/elastic-agent/pkg/agent/transpiler/rules.go @@ -19,6 +19,7 @@ type AgentInfo interface { AgentID() string Version() string Snapshot() bool + Headers() map[string]string } // RuleList is a container that allow the same tree to be executed on multiple defined Rule. @@ -90,6 +91,8 @@ func (r *RuleList) MarshalYAML() (interface{}, error) { name = "fix_stream" case *InsertDefaultsRule: name = "insert_defaults" + case *InjectHeadersRule: + name = "inject_headers" default: return nil, fmt.Errorf("unknown rule of type %T", rule) } @@ -175,6 +178,8 @@ func (r *RuleList) UnmarshalYAML(unmarshal func(interface{}) error) error { r = &FixStreamRule{} case "insert_defaults": r = &InsertDefaultsRule{} + case "inject_headers": + r = &InjectHeadersRule{} default: return fmt.Errorf("unknown rule of type %s", name) } @@ -1505,6 +1510,70 @@ func InsertDefaults(path string, selectors ...Selector) *InsertDefaultsRule { } } +// InjectHeadersRule injects headers into output. +type InjectHeadersRule struct{} + +// Apply injects headers into output. +func (r *InjectHeadersRule) Apply(agentInfo AgentInfo, ast *AST) (err error) { + defer func() { + if err != nil { + err = errors.New(err, "failed to inject headers into configuration") + } + }() + + headers := agentInfo.Headers() + if len(headers) == 0 { + return nil + } + + outputsNode, found := Lookup(ast, "outputs") + if !found { + return nil + } + + elasticsearchNode, found := outputsNode.Find("elasticsearch") + if found { + headersNode, found := elasticsearchNode.Find("headers") + if found { + headersDict, ok := headersNode.Value().(*Dict) + if !ok { + return errors.New("headers not a dictionary") + } + + for k, v := range headers { + headersDict.value = append(headersDict.value, &Key{ + name: k, + value: &StrVal{value: v}, + }) + } + } else { + nodes := make([]Node, 0, len(headers)) + for k, v := range headers { + nodes = append(nodes, &Key{ + name: k, + value: &StrVal{value: v}, + }) + } + headersDict := NewDict(nodes) + elasticsearchDict, ok := elasticsearchNode.Value().(*Dict) + if !ok { + return errors.New("elasticsearch output is not a dictionary") + } + elasticsearchDict.value = append(elasticsearchDict.value, &Key{ + name: "headers", + value: headersDict, + }) + } + } + + return nil +} + +// InjectHeaders creates a InjectHeadersRule +func InjectHeaders() *InjectHeadersRule { + return &InjectHeadersRule{} +} + // NewRuleList returns a new list of rules to be executed. func NewRuleList(rules ...Rule) *RuleList { return &RuleList{Rules: rules} diff --git a/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go b/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go index d6f24a8d8e3..71e12ac444b 100644 --- a/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go +++ b/x-pack/elastic-agent/pkg/agent/transpiler/rules_test.go @@ -685,6 +685,7 @@ rest: of }, }, }, + "insert defaults into not existing": { givenYAML: ` level_one: @@ -713,6 +714,65 @@ rest: of }, }, }, + + "inject auth headers: no headers": { + givenYAML: ` +outputs: + elasticsearch: + hosts: + - "127.0.0.1:9201" + - "127.0.0.1:9202" + logstash: + port: 5 +`, + expectedYAML: ` +outputs: + elasticsearch: + headers: + h1: test-header + hosts: + - "127.0.0.1:9201" + - "127.0.0.1:9202" + logstash: + port: 5 +`, + rule: &RuleList{ + Rules: []Rule{ + InjectHeaders(), + }, + }, + }, + + "inject auth headers: existing headers": { + givenYAML: ` +outputs: + elasticsearch: + headers: + sample-header: existing + hosts: + - "127.0.0.1:9201" + - "127.0.0.1:9202" + logstash: + port: 5 +`, + expectedYAML: ` +outputs: + elasticsearch: + headers: + sample-header: existing + h1: test-header + hosts: + - "127.0.0.1:9201" + - "127.0.0.1:9202" + logstash: + port: 5 +`, + rule: &RuleList{ + Rules: []Rule{ + InjectHeaders(), + }, + }, + }, } for name, test := range testcases { @@ -777,6 +837,7 @@ func TestSerialization(t *testing.T) { FixStream(), SelectInto("target", "s1", "s2"), InsertDefaults("target", "s1", "s2"), + InjectHeaders(), ) y := `- rename: @@ -847,6 +908,7 @@ func TestSerialization(t *testing.T) { - s1 - s2 path: target +- inject_headers: {} ` t.Run("serialize_rules", func(t *testing.T) { @@ -877,6 +939,12 @@ func (*fakeAgentInfo) Snapshot() bool { return false } +func (*fakeAgentInfo) Headers() map[string]string { + return map[string]string{ + "h1": "test-header", + } +} + func FakeAgentInfo() AgentInfo { return &fakeAgentInfo{} } diff --git a/x-pack/elastic-agent/spec/apm-server.yml b/x-pack/elastic-agent/spec/apm-server.yml index 67aac6349e3..c66a03e2bc7 100644 --- a/x-pack/elastic-agent/spec/apm-server.yml +++ b/x-pack/elastic-agent/spec/apm-server.yml @@ -31,4 +31,5 @@ rules: - inputs - output - fleet + - inject_headers: {} when: length(${inputs}) > 0 and hasKey(${output}, 'elasticsearch') diff --git a/x-pack/elastic-agent/spec/filebeat.yml b/x-pack/elastic-agent/spec/filebeat.yml index 8207c803284..6f47c1ebdee 100644 --- a/x-pack/elastic-agent/spec/filebeat.yml +++ b/x-pack/elastic-agent/spec/filebeat.yml @@ -106,5 +106,8 @@ rules: - filebeat - output - keystore + +- inject_headers: {} + when: length(${filebeat.inputs}) > 0 and hasKey(${output}, 'elasticsearch', 'redis', 'kafka', 'logstash') diff --git a/x-pack/elastic-agent/spec/fleet-server.yml b/x-pack/elastic-agent/spec/fleet-server.yml index 8cb80280842..abb4ad4a502 100644 --- a/x-pack/elastic-agent/spec/fleet-server.yml +++ b/x-pack/elastic-agent/spec/fleet-server.yml @@ -63,4 +63,6 @@ rules: - inputs - output + - inject_headers: {} + when: length(${fleet}) > 0 and length(${inputs}) > 0 and hasKey(${output}, 'elasticsearch') diff --git a/x-pack/elastic-agent/spec/metricbeat.yml b/x-pack/elastic-agent/spec/metricbeat.yml index 7968de13e8d..22e8d97f9c4 100644 --- a/x-pack/elastic-agent/spec/metricbeat.yml +++ b/x-pack/elastic-agent/spec/metricbeat.yml @@ -95,6 +95,7 @@ rules: - metricbeat - output - keystore +- inject_headers: {} when: length(${metricbeat.modules}) > 0 and hasKey(${output}, 'elasticsearch', 'redis', 'kafka', 'logstash')