-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
[filebeat] Elasticsearch state storage for httpjson and cel inputs #41446
base: main
Are you sure you want to change the base?
Changes from 1 commit
55c72d3
1bf288d
e003053
dfce978
e2e25fa
953355b
c1fc2a8
c9b0256
21d451d
ffb9364
10d212f
24000d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,8 +18,10 @@ | |
package beater | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
|
@@ -39,6 +41,7 @@ import ( | |
"github.com/elastic/beats/v7/libbeat/beat" | ||
"github.com/elastic/beats/v7/libbeat/cfgfile" | ||
"github.com/elastic/beats/v7/libbeat/common/cfgwarn" | ||
"github.com/elastic/beats/v7/libbeat/common/reload" | ||
"github.com/elastic/beats/v7/libbeat/esleg/eslegclient" | ||
"github.com/elastic/beats/v7/libbeat/management" | ||
"github.com/elastic/beats/v7/libbeat/monitoring/inputmon" | ||
|
@@ -79,7 +82,7 @@ type Filebeat struct { | |
type PluginFactory func(beat.Info, *logp.Logger, StateStore) []v2.Plugin | ||
|
||
type StateStore interface { | ||
Access() (*statestore.Store, error) | ||
Access(typ string) (*statestore.Store, error) | ||
CleanupInterval() time.Duration | ||
} | ||
|
||
|
@@ -281,13 +284,44 @@ func (fb *Filebeat) Run(b *beat.Beat) error { | |
return err | ||
} | ||
|
||
stateStore, err := openStateStore(b.Info, logp.NewLogger("filebeat"), config.Registry) | ||
// Use context, like normal people do, hooking up to the beat.done channel | ||
ctx, cn := context.WithCancel(context.Background()) | ||
go func() { | ||
<-fb.done | ||
cn() | ||
}() | ||
|
||
stateStore, err := openStateStore(ctx, b.Info, logp.NewLogger("filebeat"), config.Registry) | ||
if err != nil { | ||
logp.Err("Failed to open state store: %+v", err) | ||
return err | ||
} | ||
defer stateStore.Close() | ||
|
||
// If notifier is set, configure the listener for output configuration | ||
// The notifier passes the elasticsearch output configuration down to the Elasticsearch backed state storage | ||
// in order to allow it fully configure | ||
if stateStore.notifier != nil { | ||
b.OutputConfigReloader = reload.ReloadableFunc(func(r *reload.ConfigWithMeta) error { | ||
outCfg := conf.Namespace{} | ||
if err := r.Config.Unpack(&outCfg); err != nil || outCfg.Name() != "elasticsearch" { | ||
return nil | ||
} | ||
|
||
// TODO: REMOVE THIS HACK BEFORE MERGE. LEAVING FOR TESTING FOR DRAFT | ||
// Injecting the ApiKey that has enough permissions to write to the index | ||
// TODO: need to figure out how add permissions for the state index | ||
// agentless-state-<input id>, for example httpjson-okta.system-028ecf4b-babe-44c6-939e-9e3096af6959 | ||
apiKey := os.Getenv("AGENTLESS_ELASTICSEARCH_APIKEY") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will collaborate with agentless team on addressing this part There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When running under Elastic agent, every change of the output configuration results in a restart of the Beat process, in case that simplifies anything here for you. |
||
if apiKey != "" { | ||
outCfg.Config().SetString("api_key", -1, apiKey) | ||
} | ||
|
||
stateStore.notifier.NotifyConfigUpdate(outCfg.Config()) | ||
return nil | ||
}) | ||
} | ||
|
||
err = processLogInputTakeOver(stateStore, config) | ||
if err != nil { | ||
logp.Err("Failed to attempt filestream state take over: %+v", err) | ||
|
@@ -341,6 +375,8 @@ func (fb *Filebeat) Run(b *beat.Beat) error { | |
defer func() { | ||
_ = inputTaskGroup.Stop() | ||
}() | ||
|
||
// Store needs to be fully configured at this point | ||
if err := v2InputLoader.Init(&inputTaskGroup); err != nil { | ||
logp.Err("Failed to initialize the input managers: %v", err) | ||
return err | ||
|
@@ -509,7 +545,7 @@ func processLogInputTakeOver(stateStore StateStore, config *cfg.Config) error { | |
return nil | ||
} | ||
|
||
store, err := stateStore.Access() | ||
store, err := stateStore.Access("") | ||
if err != nil { | ||
return fmt.Errorf("Failed to access state when attempting take over: %w", err) | ||
} | ||
|
@@ -567,3 +603,8 @@ func fetchInputConfiguration(config *cfg.Config) (inputs []*conf.C, err error) { | |
|
||
return inputs, nil | ||
} | ||
|
||
func useElasticsearchStorage() bool { | ||
s := os.Getenv("AGENTLESS_ELASTICSEARCH_STATE_STORE") | ||
return s != "" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. licenses this file to you under | ||
// the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package features | ||
|
||
import "os" | ||
|
||
type void struct{} | ||
|
||
// List of input types Elasticsearch state store is enabled for | ||
var esTypesEnabled = map[string]void{ | ||
"httpjson": {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be configuration instead of in the code, maybe another env var? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure can do. Something like this? |
||
} | ||
|
||
var isESEnabled bool | ||
|
||
func init() { | ||
isESEnabled = (os.Getenv("AGENTLESS_ELASTICSEARCH_STATE_STORE_ENABLED") != "") | ||
} | ||
|
||
// IsElasticsearchStateStoreEnabled returns true if feature is enabled for agentless | ||
func IsElasticsearchStateStoreEnabled() bool { | ||
return isESEnabled | ||
} | ||
|
||
// IsElasticsearchStateStoreEnabledForInput returns true if the provided input type uses Elasticsearch for state storage if the Elasticsearch state store feature is enabled | ||
func IsElasticsearchStateStoreEnabledForInput(inputType string) bool { | ||
if IsElasticsearchStateStoreEnabled() { | ||
if _, ok := esTypesEnabled[inputType]; ok { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fleet knows when something is an agentless package and that is probably what would hook into this to generate the key.
We could add a new state storage section to an agent policy (
agent.storage
?) that Fleet knows how to template when this happens.Agent could then send it down as another output unit with a new type (or we could define a new type of unit but that is even more work).
This would allow the key to update on the fly through Fleet and control protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also possibly be handled in the agentless api / controller and hidden from Fleet if we just inject it in as an env var. No opposition to that either really.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I brought this up during the meeting today as an option. IMHO it's just one thing to manage, might be cleaner if all in one place in the policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of details we need to think about with respect to these keys is what the process should be for rotating and/or revoking them.