-
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
[Ingest Manager] Refuse invalid stream values in configuration #19587
Changes from 8 commits
ab12d3e
5f2f369
794c6fb
4c8f931
4d386ba
4314afb
f703cbe
128529e
31bae6f
a6d685c
d431d9a
e0d9412
90594b7
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 |
---|---|---|
|
@@ -10,10 +10,14 @@ outputs: | |
|
||
inputs: | ||
- type: system/metrics | ||
|
||
# The only two requirement are that it has only characters allowed in an Elasticsearch index name | ||
# and does NOT contain a `-`. | ||
dataset.namespace: default | ||
use_output: default | ||
streams: | ||
- metricset: cpu | ||
# Same requirements as for the namespace apply. | ||
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. I think it would be better to be explicit here as well, what ever is picked above, should just repeat verbatim here. |
||
dataset.name: system.cpu | ||
- metricset: memory | ||
dataset.name: system.memory | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// 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 filters | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" | ||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/transpiler" | ||
"github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" | ||
) | ||
|
||
// ErrInvalidNamespace is error returned when namespace value provided is invalid. | ||
var ErrInvalidNamespace = errors.New("provided namespace is invalid", errors.TypeConfig) | ||
|
||
// ErrInvalidDataset is error returned when dataset name value provided is invalid. | ||
var ErrInvalidDataset = errors.New("provided dataset name is invalid", errors.TypeConfig) | ||
|
||
// StreamChecker checks for invalid values in stream namespace and dataset. | ||
func StreamChecker(log *logger.Logger, ast *transpiler.AST) error { | ||
inputsNode, found := transpiler.Lookup(ast, "inputs") | ||
if !found { | ||
return nil | ||
} | ||
|
||
inputsNodeList, ok := inputsNode.Value().(*transpiler.List) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
inputsNodeListCollection, ok := inputsNodeList.Value().([]transpiler.Node) | ||
if !ok { | ||
return errors.New("inputs is not a list", errors.TypeConfig) | ||
} | ||
|
||
for _, inputNode := range inputsNodeListCollection { | ||
// fail only if dataset.namespace or dataset[namespace] is found and invalid | ||
// not provided values are ok and will be fixed by rules | ||
if nsNode, found := inputNode.Find("dataset.namespace"); found { | ||
nsKey, ok := nsNode.(*transpiler.Key) | ||
if ok { | ||
if newNamespace := nsKey.Value().(transpiler.Node).String(); !isNamespaceValid(newNamespace) { | ||
return ErrInvalidNamespace | ||
} | ||
} | ||
} else { | ||
dsNode, found := inputNode.Find("dataset") | ||
if found { | ||
// got a dataset | ||
datasetMap, ok := dsNode.Value().(*transpiler.Dict) | ||
if ok { | ||
nsNode, found := datasetMap.Find("namespace") | ||
if found { | ||
nsKey, ok := nsNode.(*transpiler.Key) | ||
if ok { | ||
if newNamespace := nsKey.Value().(transpiler.Node).String(); !isNamespaceValid(newNamespace) { | ||
return ErrInvalidNamespace | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
streamsNode, ok := inputNode.Find("streams") | ||
if !ok { | ||
continue | ||
} | ||
|
||
streamsList, ok := streamsNode.Value().(*transpiler.List) | ||
if !ok { | ||
continue | ||
} | ||
|
||
streamNodes, ok := streamsList.Value().([]transpiler.Node) | ||
if !ok { | ||
return errors.New("streams is not a list", errors.TypeConfig) | ||
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. Have you tested with Endpoint Security enabled in Fleet? I think this will cause an issue, because it does come with https://github.com/elastic/beats/pull/19248/files#diff-68f0d6baed417771ae64bf9e5a7587a7R267 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. good point here |
||
} | ||
|
||
for _, streamNode := range streamNodes { | ||
streamMap, ok := streamNode.(*transpiler.Dict) | ||
if !ok { | ||
continue | ||
} | ||
|
||
// fix this only if in compact form | ||
if dsNameNode, found := streamMap.Find("dataset.name"); found { | ||
dsKey, ok := dsNameNode.(*transpiler.Key) | ||
if ok { | ||
if newDataset := dsKey.Value().(transpiler.Node).String(); !isDatasetValid(newDataset) { | ||
return ErrInvalidDataset | ||
} | ||
} | ||
} else { | ||
datasetNode, found := streamMap.Find("dataset") | ||
if found { | ||
datasetMap, ok := datasetNode.Value().(*transpiler.Dict) | ||
if !ok { | ||
continue | ||
} | ||
|
||
dsNameNode, found := datasetMap.Find("name") | ||
if found { | ||
dsKey, ok := dsNameNode.(*transpiler.Key) | ||
if ok { | ||
if newDataset := dsKey.Value().(transpiler.Node).String(); !isDatasetValid(newDataset) { | ||
return ErrInvalidDataset | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// The only two requirement are that it has only characters allowed in an Elasticsearch index name | ||
// and does NOT contain a `-`. | ||
// Index names must meet the following criteria: | ||
// Lowercase only | ||
// Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, # | ||
// Cannot start with -, _, + | ||
// Cannot be . or .. | ||
|
||
func isNamespaceValid(namespace string) bool { | ||
// Cannot be . or .. | ||
if namespace == "." || namespace == ".." { | ||
return false | ||
} | ||
|
||
if len(namespace) <= 0 || len(namespace) > 255 { | ||
return false | ||
} | ||
|
||
// Lowercase only | ||
if strings.ToLower(namespace) != namespace { | ||
return false | ||
} | ||
|
||
// Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, # | ||
if strings.ContainsAny(namespace, "\\/*?\"<>| ,#-") { | ||
return false | ||
} | ||
|
||
// Cannot start with -, _, + | ||
if strings.HasPrefix(namespace, "-") || strings.HasPrefix(namespace, "_") || strings.HasPrefix(namespace, "+") { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
// The same requirements as for the namespace apply. | ||
func isDatasetValid(dataset string) bool { | ||
// Cannot be . or .. | ||
if dataset == "." || dataset == ".." { | ||
return false | ||
} | ||
|
||
if len(dataset) <= 0 || len(dataset) > 255 { | ||
return false | ||
} | ||
|
||
// Lowercase only | ||
if strings.ToLower(dataset) != dataset { | ||
return false | ||
} | ||
|
||
// Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, # | ||
if strings.ContainsAny(dataset, "\\/*?\"<>| ,#-") { | ||
return false | ||
} | ||
|
||
// Cannot start with -, _, + | ||
if strings.HasPrefix(dataset, "-") || strings.HasPrefix(dataset, "_") || strings.HasPrefix(dataset, "+") { | ||
return false | ||
} | ||
|
||
return true | ||
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. Why duplicate the same function and just give it the same name? Could remove the second one and rename it to
That way in the future if the validation of dataset does differ from namespace, then only that function body needs to be changed. |
||
} |
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 think it would be better to be explicit on what is allowed even if it is conservative in nature. That way its less likely for them to make a mistake and its clear directly from the comment.
How about:
Must be all lowercase, no spaces, only characters a-z or numbers 0-9 (no special characters), and no more than 255 characters.
I think the character length might be an issue being its concat-ed to make the index name.